aboutsummaryrefslogtreecommitdiff
path: root/sound/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/Kconfig528
-rw-r--r--sound/pci/Makefile64
-rw-r--r--sound/pci/ac97/Makefile18
-rw-r--r--sound/pci/ac97/ac97_codec.c2579
-rw-r--r--sound/pci/ac97/ac97_id.h62
-rw-r--r--sound/pci/ac97/ac97_local.h83
-rw-r--r--sound/pci/ac97/ac97_patch.c2309
-rw-r--r--sound/pci/ac97/ac97_patch.h59
-rw-r--r--sound/pci/ac97/ac97_pcm.c700
-rw-r--r--sound/pci/ac97/ac97_proc.c449
-rw-r--r--sound/pci/ac97/ak4531_codec.c437
-rw-r--r--sound/pci/ali5451/Makefile9
-rw-r--r--sound/pci/ali5451/ali5451.c2282
-rw-r--r--sound/pci/als4000.c789
-rw-r--r--sound/pci/atiixp.c1657
-rw-r--r--sound/pci/atiixp_modem.c1344
-rw-r--r--sound/pci/au88x0/Makefile7
-rw-r--r--sound/pci/au88x0/au8810.c17
-rw-r--r--sound/pci/au88x0/au8810.h229
-rw-r--r--sound/pci/au88x0/au8820.c15
-rw-r--r--sound/pci/au88x0/au8820.h209
-rw-r--r--sound/pci/au88x0/au8830.c18
-rw-r--r--sound/pci/au88x0/au8830.h256
-rw-r--r--sound/pci/au88x0/au88x0.c388
-rw-r--r--sound/pci/au88x0/au88x0.h284
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c914
-rw-r--r--sound/pci/au88x0/au88x0_a3d.h123
-rw-r--r--sound/pci/au88x0/au88x0_a3ddata.c91
-rw-r--r--sound/pci/au88x0/au88x0_core.c2837
-rw-r--r--sound/pci/au88x0/au88x0_eq.c937
-rw-r--r--sound/pci/au88x0/au88x0_eq.h46
-rw-r--r--sound/pci/au88x0/au88x0_eqdata.c112
-rw-r--r--sound/pci/au88x0/au88x0_game.c135
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c33
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c112
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c548
-rw-r--r--sound/pci/au88x0/au88x0_sb.h40
-rw-r--r--sound/pci/au88x0/au88x0_synth.c395
-rw-r--r--sound/pci/au88x0/au88x0_wt.h65
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.c787
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.h61
-rw-r--r--sound/pci/azt3328.c1536
-rw-r--r--sound/pci/azt3328.h165
-rw-r--r--sound/pci/bt87x.c930
-rw-r--r--sound/pci/ca0106/Makefile3
-rw-r--r--sound/pci/ca0106/ca0106.h549
-rw-r--r--sound/pci/ca0106/ca0106_main.c1283
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c634
-rw-r--r--sound/pci/ca0106/ca0106_proc.c436
-rw-r--r--sound/pci/cmipci.c2956
-rw-r--r--sound/pci/cs4281.c2136
-rw-r--r--sound/pci/cs46xx/Makefile12
-rw-r--r--sound/pci/cs46xx/cs46xx.c183
-rw-r--r--sound/pci/cs46xx/cs46xx_image.h3468
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c3922
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.h182
-rw-r--r--sound/pci/cs46xx/dsp_spos.c1892
-rw-r--r--sound/pci/cs46xx/dsp_spos.h225
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c1750
-rw-r--r--sound/pci/cs46xx/imgs/cwc4630.h320
-rw-r--r--sound/pci/cs46xx/imgs/cwcasync.h176
-rw-r--r--sound/pci/cs46xx/imgs/cwcbinhack.h48
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.asp169
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.h68
-rw-r--r--sound/pci/cs46xx/imgs/cwcemb80.h1607
-rw-r--r--sound/pci/cs46xx/imgs/cwcsnoop.h46
-rw-r--r--sound/pci/emu10k1/Makefile23
-rw-r--r--sound/pci/emu10k1/emu10k1.c240
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c540
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c875
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c223
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c120
-rw-r--r--sound/pci/emu10k1/emu10k1_synth_local.h38
-rw-r--r--sound/pci/emu10k1/emu10k1x.c1643
-rw-r--r--sound/pci/emu10k1/emufx.c2320
-rw-r--r--sound/pci/emu10k1/emumixer.c955
-rw-r--r--sound/pci/emu10k1/emumpu401.c374
-rw-r--r--sound/pci/emu10k1/emupcm.c1724
-rw-r--r--sound/pci/emu10k1/emuproc.c568
-rw-r--r--sound/pci/emu10k1/io.c404
-rw-r--r--sound/pci/emu10k1/irq.c189
-rw-r--r--sound/pci/emu10k1/memory.c564
-rw-r--r--sound/pci/emu10k1/p16v.c736
-rw-r--r--sound/pci/emu10k1/p16v.h299
-rw-r--r--sound/pci/emu10k1/timer.c97
-rw-r--r--sound/pci/emu10k1/voice.c152
-rw-r--r--sound/pci/ens1370.c2413
-rw-r--r--sound/pci/ens1371.c2
-rw-r--r--sound/pci/es1938.c1773
-rw-r--r--sound/pci/es1968.c2807
-rw-r--r--sound/pci/fm801.c1480
-rw-r--r--sound/pci/hda/Makefile7
-rw-r--r--sound/pci/hda/hda_codec.c1856
-rw-r--r--sound/pci/hda/hda_codec.h604
-rw-r--r--sound/pci/hda/hda_generic.c906
-rw-r--r--sound/pci/hda/hda_intel.c1449
-rw-r--r--sound/pci/hda/hda_local.h161
-rw-r--r--sound/pci/hda/hda_patch.h17
-rw-r--r--sound/pci/hda/hda_proc.c298
-rw-r--r--sound/pci/hda/patch_analog.c445
-rw-r--r--sound/pci/hda/patch_cmedia.c621
-rw-r--r--sound/pci/hda/patch_realtek.c1503
-rw-r--r--sound/pci/ice1712/Makefile12
-rw-r--r--sound/pci/ice1712/ak4xxx.c194
-rw-r--r--sound/pci/ice1712/amp.c65
-rw-r--r--sound/pci/ice1712/amp.h34
-rw-r--r--sound/pci/ice1712/aureon.c1948
-rw-r--r--sound/pci/ice1712/aureon.h56
-rw-r--r--sound/pci/ice1712/delta.c771
-rw-r--r--sound/pci/ice1712/delta.h150
-rw-r--r--sound/pci/ice1712/envy24ht.h215
-rw-r--r--sound/pci/ice1712/ews.c1036
-rw-r--r--sound/pci/ice1712/ews.h84
-rw-r--r--sound/pci/ice1712/hoontech.c326
-rw-r--r--sound/pci/ice1712/hoontech.h77
-rw-r--r--sound/pci/ice1712/ice1712.c2760
-rw-r--r--sound/pci/ice1712/ice1712.h494
-rw-r--r--sound/pci/ice1712/ice1724.c2340
-rw-r--r--sound/pci/ice1712/juli.c230
-rw-r--r--sound/pci/ice1712/juli.h10
-rw-r--r--sound/pci/ice1712/phase.c138
-rw-r--r--sound/pci/ice1712/phase.h34
-rw-r--r--sound/pci/ice1712/pontis.c849
-rw-r--r--sound/pci/ice1712/pontis.h33
-rw-r--r--sound/pci/ice1712/prodigy192.c524
-rw-r--r--sound/pci/ice1712/prodigy192.h11
-rw-r--r--sound/pci/ice1712/revo.c205
-rw-r--r--sound/pci/ice1712/revo.h48
-rw-r--r--sound/pci/ice1712/stac946x.h25
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c115
-rw-r--r--sound/pci/ice1712/vt1720_mobo.h39
-rw-r--r--sound/pci/intel8x0.c2855
-rw-r--r--sound/pci/intel8x0m.c1462
-rw-r--r--sound/pci/korg1212/Makefile9
-rw-r--r--sound/pci/korg1212/korg1212-firmware.h987
-rw-r--r--sound/pci/korg1212/korg1212.c2553
-rw-r--r--sound/pci/maestro3.c2714
-rw-r--r--sound/pci/mixart/Makefile8
-rw-r--r--sound/pci/mixart/mixart.c1443
-rw-r--r--sound/pci/mixart/mixart.h242
-rw-r--r--sound/pci/mixart/mixart_core.c588
-rw-r--r--sound/pci/mixart/mixart_core.h607
-rw-r--r--sound/pci/mixart/mixart_hwdep.c647
-rw-r--r--sound/pci/mixart/mixart_hwdep.h145
-rw-r--r--sound/pci/mixart/mixart_mixer.c1136
-rw-r--r--sound/pci/mixart/mixart_mixer.h31
-rw-r--r--sound/pci/nm256/Makefile9
-rw-r--r--sound/pci/nm256/nm256.c1657
-rw-r--r--sound/pci/nm256/nm256_coef.c4607
-rw-r--r--sound/pci/rme32.c2043
-rw-r--r--sound/pci/rme96.c2449
-rw-r--r--sound/pci/rme9652/Makefile11
-rw-r--r--sound/pci/rme9652/hdsp.c5206
-rw-r--r--sound/pci/rme9652/rme9652.c2676
-rw-r--r--sound/pci/sonicvibes.c1534
-rw-r--r--sound/pci/trident/Makefile19
-rw-r--r--sound/pci/trident/trident.c196
-rw-r--r--sound/pci/trident/trident_main.c3991
-rw-r--r--sound/pci/trident/trident_memory.c476
-rw-r--r--sound/pci/trident/trident_synth.c1031
-rw-r--r--sound/pci/via82xx.c2346
-rw-r--r--sound/pci/via82xx_modem.c1245
-rw-r--r--sound/pci/vx222/Makefile8
-rw-r--r--sound/pci/vx222/vx222.c272
-rw-r--r--sound/pci/vx222/vx222.h114
-rw-r--r--sound/pci/vx222/vx222_ops.c1004
-rw-r--r--sound/pci/ymfpci/Makefile9
-rw-r--r--sound/pci/ymfpci/ymfpci.c372
-rw-r--r--sound/pci/ymfpci/ymfpci_image.h1565
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c2273
170 files changed, 138513 insertions, 0 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
new file mode 100644
index 00000000000..428efdbd70a
--- /dev/null
+++ b/sound/pci/Kconfig
@@ -0,0 +1,528 @@
+# ALSA PCI drivers
+
+menu "PCI devices"
+ depends on SND!=n && PCI
+
+config SND_AC97_CODEC
+ tristate
+ select SND_PCM
+
+config SND_ALI5451
+ tristate "ALi M5451 PCI Audio Controller"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the integrated AC97 sound
+ device on motherboards using the ALi M5451 Audio Controller
+ (M1535/M1535D/M1535+/M1535D+ south bridges). Newer chipsets
+ use the "Intel/SiS/nVidia/AMD/ALi AC97 Controller" driver.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ali5451.
+
+config SND_ATIIXP
+ tristate "ATI IXP AC97 Controller"
+ depends on SND
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the integrated AC97 sound
+ device on motherboards with ATI chipsets (ATI IXP 150/200/250/
+ 300/400).
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-atiixp.
+
+config SND_ATIIXP_MODEM
+ tristate "ATI IXP Modem"
+ depends on SND
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the integrated MC97 modem on
+ motherboards with ATI chipsets (ATI IXP 150/200/250).
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-atiixp-modem.
+
+config SND_AU8810
+ tristate "Aureal Advantage"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for Aureal Advantage soundcards.
+
+ Supported features: Hardware Mixer, SRC, EQ and SPDIF output.
+ 3D support code is in place, but not yet useable. For more info,
+ email the ALSA developer list, or <mjander@users.sourceforge.net>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-au8810.
+
+config SND_AU8820
+ tristate "Aureal Vortex"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for Aureal Vortex soundcards.
+
+ Supported features: Hardware Mixer and SRC. For more info, email
+ the ALSA developer list, or <mjander@users.sourceforge.net>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-au8820.
+
+config SND_AU8830
+ tristate "Aureal Vortex 2"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for Aureal Vortex 2 soundcards.
+
+ Supported features: Hardware Mixer, SRC, EQ and SPDIF output.
+ 3D support code is in place, but not yet useable. For more info,
+ email the ALSA developer list, or <mjander@users.sourceforge.net>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-au8830.
+
+config SND_AZT3328
+ tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
+ depends on SND && EXPERIMENTAL
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_PCM
+ help
+ Say Y here to include support for Aztech AZF3328 (PCI168)
+ soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-azt3328.
+
+config SND_BT87X
+ tristate "Bt87x Audio Capture"
+ depends on SND
+ select SND_PCM
+ help
+ If you want to record audio from TV cards based on
+ Brooktree Bt878/Bt879 chips, say Y here and read
+ <file:Documentation/sound/alsa/Bt87x.txt>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-bt87x.
+
+config SND_BT87X_OVERCLOCK
+ bool "Bt87x Audio overclocking"
+ depends on SND_BT87X
+ help
+ Say Y here if 448000 Hz isn't enough for you and you want to
+ record from the analog input with up to 1792000 Hz.
+
+ Higher sample rates won't hurt your hardware, but audio
+ quality may suffer.
+
+config SND_CS46XX
+ tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
+ depends on SND
+ select SND_RAWMIDI
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for Cirrus Logic CS4610/CS4612/
+ CS4614/CS4615/CS4622/CS4624/CS4630/CS4280 chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-cs46xx.
+
+config SND_CS46XX_NEW_DSP
+ bool "Cirrus Logic (Sound Fusion) New DSP support (EXPERIMENTAL)"
+ depends on SND_CS46XX && EXPERIMENTAL
+ help
+ Say Y here to use a new DSP image for SPDIF and dual codecs.
+
+ This works better than the old code, so say Y.
+
+config SND_CS4281
+ tristate "Cirrus Logic (Sound Fusion) CS4281"
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_RAWMIDI
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for Cirrus Logic CS4281 chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-cs4281.
+
+config SND_EMU10K1
+ tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
+ depends on SND
+ select SND_HWDEP
+ select SND_RAWMIDI
+ select SND_AC97_CODEC
+ help
+ Say Y to include support for Sound Blaster PCI 512, Live!,
+ Audigy and E-mu APS (partially supported) soundcards.
+
+ The confusing multitude of mixer controls is documented in
+ <file:Documentation/sound/alsa/SB-Live-mixer.txt> and
+ <file:Documentation/sound/alsa/Audigy-mixer.txt>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-emu10k1.
+
+config SND_EMU10K1X
+ tristate "Emu10k1X (Dell OEM Version)"
+ depends on SND
+ select SND_AC97_CODEC
+ select SND_RAWMIDI
+ help
+ Say Y here to include support for the Dell OEM version of the
+ Sound Blaster Live!.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-emu10k1x.
+
+config SND_CA0106
+ tristate "SB Audigy LS / Live 24bit"
+ depends on SND
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the Sound Blaster Audigy LS
+ and Live 24bit.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ca0106.
+
+config SND_KORG1212
+ tristate "Korg 1212 IO"
+ depends on SND
+ select SND_PCM
+ help
+ Say Y here to include support for Korg 1212IO soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-korg1212.
+
+config SND_MIXART
+ tristate "Digigram miXart"
+ depends on SND
+ select SND_HWDEP
+ select SND_PCM
+ help
+ If you want to use Digigram miXart soundcards, say Y here and
+ read <file:Documentation/sound/alsa/MIXART.txt>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-mixart.
+
+config SND_NM256
+ tristate "NeoMagic NM256AV/ZX"
+ depends on SND
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for NeoMagic NM256AV/ZX chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-nm256.
+
+config SND_RME32
+ tristate "RME Digi32, 32/8, 32 PRO"
+ depends on SND
+ select SND_PCM
+ help
+ Say Y to include support for RME Digi32, Digi32 PRO and
+ Digi32/8 (Sek'd Prodif32, Prodif96 and Prodif Gold) audio
+ devices.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-rme32.
+
+config SND_RME96
+ tristate "RME Digi96, 96/8, 96/8 PRO"
+ depends on SND
+ select SND_PCM
+ help
+ Say Y here to include support for RME Digi96, Digi96/8 and
+ Digi96/8 PRO/PAD/PST soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-rme96.
+
+config SND_RME9652
+ tristate "RME Digi9652 (Hammerfall)"
+ depends on SND
+ select SND_PCM
+ help
+ Say Y here to include support for RME Hammerfall (RME
+ Digi9652/Digi9636) soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-rme9652.
+
+config SND_HDSP
+ tristate "RME Hammerfall DSP Audio"
+ depends on SND
+ select SND_HWDEP
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say Y here to include support for RME Hammerfall DSP Audio
+ soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hdsp.
+
+config SND_TRIDENT
+ tristate "Trident 4D-Wave DX/NX; SiS 7018"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on Trident
+ 4D-Wave DX/NX or SiS 7018 chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-trident.
+
+config SND_YMFPCI
+ tristate "Yamaha YMF724/740/744/754"
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for Yamaha PCI audio chips -
+ YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ymfpci.
+
+config SND_ALS4000
+ tristate "Avance Logic ALS4000"
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_PCM
+ help
+ Say Y here to include support for soundcards based on Avance Logic
+ ALS4000 chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-als4000.
+
+config SND_CMIPCI
+ tristate "C-Media 8738, 8338"
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_PCM
+ help
+ If you want to use soundcards based on C-Media CMI8338 or CMI8738
+ chips, say Y here and read
+ <file:Documentation/sound/alsa/CMIPCI.txt>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-cmipci.
+
+config SND_ENS1370
+ tristate "(Creative) Ensoniq AudioPCI 1370"
+ depends on SND
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say Y here to include support for Ensoniq AudioPCI ES1370 chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ens1370.
+
+config SND_ENS1371
+ tristate "(Creative) Ensoniq AudioPCI 1371/1373"
+ depends on SND
+ select SND_RAWMIDI
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for Ensoniq AudioPCI ES1371 chips and
+ Sound Blaster PCI 64 or 128 soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ens1371.
+
+config SND_ES1938
+ tristate "ESS ES1938/1946/1969 (Solo-1)"
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on ESS Solo-1
+ (ES1938, ES1946, ES1969) chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-es1938.
+
+config SND_ES1968
+ tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on ESS Maestro
+ 1/2/2E chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-es1968.
+
+config SND_MAESTRO3
+ tristate "ESS Allegro/Maestro3"
+ depends on SND
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on ESS Maestro 3
+ (Allegro) chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-maestro3.
+
+config SND_FM801
+ tristate "ForteMedia FM801"
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on the ForteMedia
+ FM801 chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-fm801.
+
+config SND_FM801_TEA575X
+ tristate "ForteMedia FM801 + TEA5757 tuner"
+ depends on SND_FM801
+ select VIDEO_DEV
+ help
+ Say Y here to include support for soundcards based on the ForteMedia
+ FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
+ Forte SF256-PCS-02).
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-fm801-tea575x.
+
+config SND_ICE1712
+ tristate "ICEnsemble ICE1712 (Envy24)"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on the
+ ICE1712 (Envy24) chip.
+
+ Currently supported hardware is: M-Audio Delta 1010(LT),
+ DiO 2496, 66, 44, 410, Audiophile 24/96; Digigram VX442;
+ TerraTec EWX 24/96, EWS 88MT, 88D, DMX 6Fire, Phase 88;
+ Hoontech SoundTrack DSP 24/Value/Media7.1; Event EZ8.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ice1712.
+
+config SND_ICE1724
+ tristate "ICE/VT1724/1720 (Envy24HT/PT)"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on
+ ICE/VT1724/1720 (Envy24HT/PT) chips.
+
+ Currently supported hardware is: AMP AUDIO2000; M-Audio
+ Revolution 7.1; TerraTec Aureon 5.1 Sky, 7.1 Space/Universe;
+ AudioTrak Prodigy 7.1; Pontis MS300; Albatron K8X800 Pro II;
+ Chaintech ZNF3-150/250.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-ice1724.
+
+config SND_INTEL8X0
+ tristate "Intel/SiS/nVidia/AMD/ALi AC97 Controller"
+ depends on SND
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the integrated AC97 sound
+ device on motherboards with Intel/SiS/nVidia/AMD chipsets, or
+ ALi chipsets using the M5455 Audio Controller. (There is a
+ separate driver for ALi M5451 Audio Controllers.)
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-intel8x0.
+
+config SND_INTEL8X0M
+ tristate "Intel/SiS/nVidia/AMD MC97 Modem (EXPERIMENTAL)"
+ depends on SND && EXPERIMENTAL
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the integrated MC97 modem on
+ motherboards with Intel/SiS/nVidia/AMD chipsets.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-intel8x0m.
+
+config SND_SONICVIBES
+ tristate "S3 SonicVibes"
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for soundcards based on the S3
+ SonicVibes chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-sonicvibes.
+
+config SND_VIA82XX
+ tristate "VIA 82C686A/B, 8233/8235 AC97 Controller"
+ depends on SND
+ select SND_MPU401_UART
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the integrated AC97 sound
+ device on motherboards with VIA chipsets.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-via82xx.
+
+config SND_VIA82XX_MODEM
+ tristate "VIA 82C686A/B, 8233 based Modems"
+ depends on SND
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the integrated MC97 modem on
+ motherboards with VIA chipsets.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-via82xx-modem.
+
+config SND_VX222
+ tristate "Digigram VX222"
+ depends on SND
+ select SND_VX_LIB
+ help
+ Say Y here to include support for Digigram VX222 soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-vx222.
+
+config SND_HDA_INTEL
+ tristate "Intel HD Audio"
+ depends on SND
+ select SND_PCM
+ help
+ Say Y here to include support for Intel "High Definition
+ Audio" (Azalia) motherboard devices.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-intel.
+
+endmenu
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
new file mode 100644
index 00000000000..b40575c3349
--- /dev/null
+++ b/sound/pci/Makefile
@@ -0,0 +1,64 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-als4000-objs := als4000.o
+snd-atiixp-objs := atiixp.o
+snd-atiixp-modem-objs := atiixp_modem.o
+snd-azt3328-objs := azt3328.o
+snd-bt87x-objs := bt87x.o
+snd-cmipci-objs := cmipci.o
+snd-cs4281-objs := cs4281.o
+snd-ens1370-objs := ens1370.o
+snd-ens1371-objs := ens1371.o
+snd-es1938-objs := es1938.o
+snd-es1968-objs := es1968.o
+snd-fm801-objs := fm801.o
+snd-intel8x0-objs := intel8x0.o
+snd-intel8x0m-objs := intel8x0m.o
+snd-maestro3-objs := maestro3.o
+snd-rme32-objs := rme32.o
+snd-rme96-objs := rme96.o
+snd-sonicvibes-objs := sonicvibes.o
+snd-via82xx-objs := via82xx.o
+snd-via82xx-modem-objs := via82xx_modem.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALS4000) += snd-als4000.o
+obj-$(CONFIG_SND_ATIIXP) += snd-atiixp.o
+obj-$(CONFIG_SND_ATIIXP_MODEM) += snd-atiixp-modem.o
+obj-$(CONFIG_SND_AZT3328) += snd-azt3328.o
+obj-$(CONFIG_SND_BT87X) += snd-bt87x.o
+obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o
+obj-$(CONFIG_SND_CS4281) += snd-cs4281.o
+obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o
+obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o
+obj-$(CONFIG_SND_ES1938) += snd-es1938.o
+obj-$(CONFIG_SND_ES1968) += snd-es1968.o
+obj-$(CONFIG_SND_FM801) += snd-fm801.o
+obj-$(CONFIG_SND_INTEL8X0) += snd-intel8x0.o
+obj-$(CONFIG_SND_INTEL8X0M) += snd-intel8x0m.o
+obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
+obj-$(CONFIG_SND_RME32) += snd-rme32.o
+obj-$(CONFIG_SND_RME96) += snd-rme96.o
+obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
+obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
+obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
+
+obj-$(CONFIG_SND) += \
+ ac97/ \
+ ali5451/ \
+ au88x0/ \
+ ca0106/ \
+ cs46xx/ \
+ emu10k1/ \
+ hda/ \
+ ice1712/ \
+ korg1212/ \
+ mixart/ \
+ nm256/ \
+ rme9652/ \
+ trident/ \
+ ymfpci/ \
+ vx222/
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
new file mode 100644
index 00000000000..3c3222122d8
--- /dev/null
+++ b/sound/pci/ac97/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-ac97-codec-objs := ac97_codec.o ac97_pcm.o ac97_patch.o
+
+ifneq ($(CONFIG_PROC_FS),)
+snd-ac97-codec-objs += ac97_proc.o
+endif
+
+snd-ak4531-codec-objs := ak4531_codec.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o
+
+obj-m := $(sort $(obj-m))
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
new file mode 100644
index 00000000000..0b024ec1f70
--- /dev/null
+++ b/sound/pci/ac97/ac97_codec.c
@@ -0,0 +1,2579 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal interface for Audio Codec '97
+ *
+ * For more details look to AC '97 component specification revision 2.2
+ * by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include "ac97_local.h"
+#include "ac97_id.h"
+#include "ac97_patch.h"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Universal interface for Audio Codec '97");
+MODULE_LICENSE("GPL");
+
+static int enable_loopback;
+
+module_param(enable_loopback, bool, 0444);
+MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
+
+/*
+
+ */
+
+typedef struct {
+ unsigned int id;
+ unsigned int mask;
+ const char *name;
+ int (*patch)(ac97_t *ac97);
+ int (*mpatch)(ac97_t *ac97);
+ unsigned int flags;
+} ac97_codec_id_t;
+
+static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = {
+{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL },
+{ 0x41445300, 0xffffff00, "Analog Devices", NULL, NULL },
+{ 0x414c4300, 0xffffff00, "Realtek", NULL, NULL },
+{ 0x414c4700, 0xffffff00, "Realtek", NULL, NULL },
+{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL },
+{ 0x43525900, 0xffffff00, "Cirrus Logic", NULL, NULL },
+{ 0x43585400, 0xffffff00, "Conexant", NULL, NULL },
+{ 0x44543000, 0xffffff00, "Diamond Technology", NULL, NULL },
+{ 0x454d4300, 0xffffff00, "eMicro", NULL, NULL },
+{ 0x45838300, 0xffffff00, "ESS Technology", NULL, NULL },
+{ 0x48525300, 0xffffff00, "Intersil", NULL, NULL },
+{ 0x49434500, 0xffffff00, "ICEnsemble", NULL, NULL },
+{ 0x49544500, 0xffffff00, "ITE Tech.Inc", NULL, NULL },
+{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL, NULL },
+{ 0x50534300, 0xffffff00, "Philips", NULL, NULL },
+{ 0x53494c00, 0xffffff00, "Silicon Laboratory", NULL, NULL },
+{ 0x54524100, 0xffffff00, "TriTech", NULL, NULL },
+{ 0x54584e00, 0xffffff00, "Texas Instruments", NULL, NULL },
+{ 0x56494100, 0xffffff00, "VIA Technologies", NULL, NULL },
+{ 0x57454300, 0xffffff00, "Winbond", NULL, NULL },
+{ 0x574d4c00, 0xffffff00, "Wolfson", NULL, NULL },
+{ 0x594d4800, 0xffffff00, "Yamaha", NULL, NULL },
+{ 0x83847600, 0xffffff00, "SigmaTel", NULL, NULL },
+{ 0, 0, NULL, NULL, NULL }
+};
+
+static const ac97_codec_id_t snd_ac97_codec_ids[] = {
+{ 0x414b4d00, 0xffffffff, "AK4540", NULL, NULL },
+{ 0x414b4d01, 0xffffffff, "AK4542", NULL, NULL },
+{ 0x414b4d02, 0xffffffff, "AK4543", NULL, NULL },
+{ 0x414b4d06, 0xffffffff, "AK4544A", NULL, NULL },
+{ 0x414b4d07, 0xffffffff, "AK4545", NULL, NULL },
+{ 0x41445303, 0xffffffff, "AD1819", patch_ad1819, NULL },
+{ 0x41445340, 0xffffffff, "AD1881", patch_ad1881, NULL },
+{ 0x41445348, 0xffffffff, "AD1881A", patch_ad1881, NULL },
+{ 0x41445360, 0xffffffff, "AD1885", patch_ad1885, NULL },
+{ 0x41445361, 0xffffffff, "AD1886", patch_ad1886, NULL },
+{ 0x41445362, 0xffffffff, "AD1887", patch_ad1881, NULL },
+{ 0x41445363, 0xffffffff, "AD1886A", patch_ad1881, NULL },
+{ 0x41445368, 0xffffffff, "AD1888", patch_ad1888, NULL },
+{ 0x41445370, 0xffffffff, "AD1980", patch_ad1980, NULL },
+{ 0x41445372, 0xffffffff, "AD1981A", patch_ad1981a, NULL },
+{ 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL },
+{ 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL },
+{ 0x41445378, 0xffffffff, "AD1986", patch_ad1985, NULL },
+{ 0x414c4300, 0xffffff00, "ALC100,100P", NULL, NULL },
+{ 0x414c4710, 0xfffffff0, "ALC200,200P", NULL, NULL },
+{ 0x414c4721, 0xffffffff, "ALC650D", NULL, NULL }, /* already patched */
+{ 0x414c4722, 0xffffffff, "ALC650E", NULL, NULL }, /* already patched */
+{ 0x414c4723, 0xffffffff, "ALC650F", NULL, NULL }, /* already patched */
+{ 0x414c4720, 0xfffffff0, "ALC650", patch_alc650, NULL },
+{ 0x414c4760, 0xfffffff0, "ALC655", patch_alc655, NULL },
+{ 0x414c4780, 0xfffffff0, "ALC658", patch_alc655, NULL },
+{ 0x414c4790, 0xfffffff0, "ALC850", patch_alc850, NULL },
+{ 0x414c4730, 0xffffffff, "ALC101", NULL, NULL },
+{ 0x414c4740, 0xfffffff0, "ALC202", NULL, NULL },
+{ 0x414c4750, 0xfffffff0, "ALC250", NULL, NULL },
+{ 0x414c4770, 0xfffffff0, "ALC203", NULL, NULL },
+{ 0x434d4941, 0xffffffff, "CMI9738", patch_cm9738, NULL },
+{ 0x434d4961, 0xffffffff, "CMI9739", patch_cm9739, NULL },
+{ 0x434d4978, 0xffffffff, "CMI9761", patch_cm9761, NULL },
+{ 0x434d4982, 0xffffffff, "CMI9761", patch_cm9761, NULL },
+{ 0x434d4983, 0xffffffff, "CMI9761", patch_cm9761, NULL },
+{ 0x43525900, 0xfffffff8, "CS4297", NULL, NULL },
+{ 0x43525910, 0xfffffff8, "CS4297A", patch_cirrus_spdif, NULL },
+{ 0x43525920, 0xfffffff8, "CS4298", patch_cirrus_spdif, NULL },
+{ 0x43525928, 0xfffffff8, "CS4294", NULL, NULL },
+{ 0x43525930, 0xfffffff8, "CS4299", patch_cirrus_cs4299, NULL },
+{ 0x43525948, 0xfffffff8, "CS4201", NULL, NULL },
+{ 0x43525958, 0xfffffff8, "CS4205", patch_cirrus_spdif, NULL },
+{ 0x43525960, 0xfffffff8, "CS4291", NULL, NULL },
+{ 0x43525970, 0xfffffff8, "CS4202", NULL, NULL },
+{ 0x43585421, 0xffffffff, "HSD11246", NULL, NULL }, // SmartMC II
+{ 0x43585428, 0xfffffff8, "Cx20468", patch_conexant, NULL }, // SmartAMC fixme: the mask might be different
+{ 0x44543031, 0xfffffff0, "DT0398", NULL, NULL },
+{ 0x454d4328, 0xffffffff, "28028", NULL, NULL }, // same as TR28028?
+{ 0x45838308, 0xffffffff, "ESS1988", NULL, NULL },
+{ 0x48525300, 0xffffff00, "HMP9701", NULL, NULL },
+{ 0x49434501, 0xffffffff, "ICE1230", NULL, NULL },
+{ 0x49434511, 0xffffffff, "ICE1232", NULL, NULL }, // alias VIA VT1611A?
+{ 0x49434514, 0xffffffff, "ICE1232A", NULL, NULL },
+{ 0x49434551, 0xffffffff, "VT1616", patch_vt1616, NULL },
+{ 0x49434552, 0xffffffff, "VT1616i", patch_vt1616, NULL }, // VT1616 compatible (chipset integrated)
+{ 0x49544520, 0xffffffff, "IT2226E", NULL, NULL },
+{ 0x49544561, 0xffffffff, "IT2646E", patch_it2646, NULL },
+{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk
+{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL },
+{ 0x4e534350, 0xffffffff, "LM4550", NULL, NULL },
+{ 0x50534304, 0xffffffff, "UCB1400", NULL, NULL },
+{ 0x53494c20, 0xffffffe0, "Si3036,8", NULL, mpatch_si3036 },
+{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL },
+{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL },
+{ 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028, NULL }, // added by xin jin [07/09/99]
+{ 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
+{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL },
+{ 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF
+{ 0x57454301, 0xffffffff, "W83971D", NULL, NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701A", NULL, NULL },
+{ 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL},
+{ 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q", patch_wolfson04, NULL},
+{ 0x574d4C05, 0xffffffff, "WM9705,WM9710", patch_wolfson05, NULL},
+{ 0x574d4C09, 0xffffffff, "WM9709", NULL, NULL},
+{ 0x574d4C12, 0xffffffff, "WM9711,WM9712", patch_wolfson11, NULL},
+{ 0x574d4c13, 0xffffffff, "WM9713,WM9714", patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
+{ 0x594d4800, 0xffffffff, "YMF743", NULL, NULL },
+{ 0x594d4802, 0xffffffff, "YMF752", NULL, NULL },
+{ 0x594d4803, 0xffffffff, "YMF753", patch_yamaha_ymf753, NULL },
+{ 0x83847600, 0xffffffff, "STAC9700,83,84", patch_sigmatel_stac9700, NULL },
+{ 0x83847604, 0xffffffff, "STAC9701,3,4,5", NULL, NULL },
+{ 0x83847605, 0xffffffff, "STAC9704", NULL, NULL },
+{ 0x83847608, 0xffffffff, "STAC9708,11", patch_sigmatel_stac9708, NULL },
+{ 0x83847609, 0xffffffff, "STAC9721,23", patch_sigmatel_stac9721, NULL },
+{ 0x83847644, 0xffffffff, "STAC9744", patch_sigmatel_stac9744, NULL },
+{ 0x83847650, 0xffffffff, "STAC9750,51", NULL, NULL }, // patch?
+{ 0x83847652, 0xffffffff, "STAC9752,53", NULL, NULL }, // patch?
+{ 0x83847656, 0xffffffff, "STAC9756,57", patch_sigmatel_stac9756, NULL },
+{ 0x83847658, 0xffffffff, "STAC9758,59", patch_sigmatel_stac9758, NULL },
+{ 0x83847666, 0xffffffff, "STAC9766,67", NULL, NULL }, // patch?
+{ 0, 0, NULL, NULL, NULL }
+};
+
+const char *snd_ac97_stereo_enhancements[] =
+{
+ /* 0 */ "No 3D Stereo Enhancement",
+ /* 1 */ "Analog Devices Phat Stereo",
+ /* 2 */ "Creative Stereo Enhancement",
+ /* 3 */ "National Semi 3D Stereo Enhancement",
+ /* 4 */ "YAMAHA Ymersion",
+ /* 5 */ "BBE 3D Stereo Enhancement",
+ /* 6 */ "Crystal Semi 3D Stereo Enhancement",
+ /* 7 */ "Qsound QXpander",
+ /* 8 */ "Spatializer 3D Stereo Enhancement",
+ /* 9 */ "SRS 3D Stereo Enhancement",
+ /* 10 */ "Platform Tech 3D Stereo Enhancement",
+ /* 11 */ "AKM 3D Audio",
+ /* 12 */ "Aureal Stereo Enhancement",
+ /* 13 */ "Aztech 3D Enhancement",
+ /* 14 */ "Binaura 3D Audio Enhancement",
+ /* 15 */ "ESS Technology Stereo Enhancement",
+ /* 16 */ "Harman International VMAx",
+ /* 17 */ "Nvidea/IC Ensemble/KS Waves 3D Stereo Enhancement",
+ /* 18 */ "Philips Incredible Sound",
+ /* 19 */ "Texas Instruments 3D Stereo Enhancement",
+ /* 20 */ "VLSI Technology 3D Stereo Enhancement",
+ /* 21 */ "TriTech 3D Stereo Enhancement",
+ /* 22 */ "Realtek 3D Stereo Enhancement",
+ /* 23 */ "Samsung 3D Stereo Enhancement",
+ /* 24 */ "Wolfson Microelectronics 3D Enhancement",
+ /* 25 */ "Delta Integration 3D Enhancement",
+ /* 26 */ "SigmaTel 3D Enhancement",
+ /* 27 */ "IC Ensemble/KS Waves",
+ /* 28 */ "Rockwell 3D Stereo Enhancement",
+ /* 29 */ "Reserved 29",
+ /* 30 */ "Reserved 30",
+ /* 31 */ "Reserved 31"
+};
+
+/*
+ * Shared AC97 controllers (ICH, ATIIXP...)
+ */
+static DECLARE_MUTEX(shared_codec_mutex);
+static ac97_t *shared_codec[AC97_SHARED_TYPES][4];
+
+
+/*
+ * I/O routines
+ */
+
+static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg)
+{
+ if (ac97->limited_regs && ! test_bit(reg, ac97->reg_accessed))
+ return 0;
+
+ /* filter some registers for buggy codecs */
+ switch (ac97->id) {
+ case AC97_ID_AK4540:
+ case AC97_ID_AK4542:
+ if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c)
+ return 1;
+ return 0;
+ case AC97_ID_AD1819: /* AD1819 */
+ case AC97_ID_AD1881: /* AD1881 */
+ case AC97_ID_AD1881A: /* AD1881A */
+ if (reg >= 0x3a && reg <= 0x6e) /* 0x59 */
+ return 0;
+ return 1;
+ case AC97_ID_AD1885: /* AD1885 */
+ case AC97_ID_AD1886: /* AD1886 */
+ case AC97_ID_AD1886A: /* AD1886A - !!verify!! --jk */
+ case AC97_ID_AD1887: /* AD1887 - !!verify!! --jk */
+ if (reg == 0x5a)
+ return 1;
+ if (reg >= 0x3c && reg <= 0x6e) /* 0x59 */
+ return 0;
+ return 1;
+ case AC97_ID_STAC9700:
+ case AC97_ID_STAC9704:
+ case AC97_ID_STAC9705:
+ case AC97_ID_STAC9708:
+ case AC97_ID_STAC9721:
+ case AC97_ID_STAC9744:
+ case AC97_ID_STAC9756:
+ if (reg <= 0x3a || reg >= 0x5a)
+ return 1;
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * snd_ac97_write - write a value on the given register
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @value: the value to set
+ *
+ * Writes a value on the given register. This will invoke the write
+ * callback directly after the register check.
+ * This function doesn't change the register cache unlike
+ * #snd_ca97_write_cache(), so use this only when you don't want to
+ * reflect the change to the suspend/resume state.
+ */
+void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value)
+{
+ if (!snd_ac97_valid_reg(ac97, reg))
+ return;
+ if ((ac97->id & 0xffffff00) == AC97_ID_ALC100) {
+ /* Fix H/W bug of ALC100/100P */
+ if (reg == AC97_MASTER || reg == AC97_HEADPHONE)
+ ac97->bus->ops->write(ac97, AC97_RESET, 0); /* reset audio codec */
+ }
+ ac97->bus->ops->write(ac97, reg, value);
+}
+
+/**
+ * snd_ac97_read - read a value from the given register
+ *
+ * @ac97: the ac97 instance
+ * @reg: the register to read
+ *
+ * Reads a value from the given register. This will invoke the read
+ * callback directly after the register check.
+ *
+ * Returns the read value.
+ */
+unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ if (!snd_ac97_valid_reg(ac97, reg))
+ return 0;
+ return ac97->bus->ops->read(ac97, reg);
+}
+
+/* read a register - return the cached value if already read */
+static inline unsigned short snd_ac97_read_cache(ac97_t *ac97, unsigned short reg)
+{
+ if (! test_bit(reg, ac97->reg_accessed)) {
+ ac97->regs[reg] = ac97->bus->ops->read(ac97, reg);
+ // set_bit(reg, ac97->reg_accessed);
+ }
+ return ac97->regs[reg];
+}
+
+/**
+ * snd_ac97_write_cache - write a value on the given register and update the cache
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @value: the value to set
+ *
+ * Writes a value on the given register and updates the register
+ * cache. The cached values are used for the cached-read and the
+ * suspend/resume.
+ */
+void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value)
+{
+ if (!snd_ac97_valid_reg(ac97, reg))
+ return;
+ down(&ac97->reg_mutex);
+ ac97->regs[reg] = value;
+ ac97->bus->ops->write(ac97, reg, value);
+ set_bit(reg, ac97->reg_accessed);
+ up(&ac97->reg_mutex);
+}
+
+/**
+ * snd_ac97_update - update the value on the given register
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @value: the value to set
+ *
+ * Compares the value with the register cache and updates the value
+ * only when the value is changed.
+ *
+ * Returns 1 if the value is changed, 0 if no change, or a negative
+ * code on failure.
+ */
+int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value)
+{
+ int change;
+
+ if (!snd_ac97_valid_reg(ac97, reg))
+ return -EINVAL;
+ down(&ac97->reg_mutex);
+ change = ac97->regs[reg] != value;
+ if (change) {
+ ac97->regs[reg] = value;
+ ac97->bus->ops->write(ac97, reg, value);
+ }
+ up(&ac97->reg_mutex);
+ return change;
+}
+
+/**
+ * snd_ac97_update_bits - update the bits on the given register
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @mask: the bit-mask to change
+ * @value: the value to set
+ *
+ * Updates the masked-bits on the given register only when the value
+ * is changed.
+ *
+ * Returns 1 if the bits are changed, 0 if no change, or a negative
+ * code on failure.
+ */
+int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value)
+{
+ int change;
+
+ if (!snd_ac97_valid_reg(ac97, reg))
+ return -EINVAL;
+ down(&ac97->reg_mutex);
+ change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
+ up(&ac97->reg_mutex);
+ return change;
+}
+
+/* no lock version - see snd_ac97_updat_bits() */
+int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg,
+ unsigned short mask, unsigned short value)
+{
+ int change;
+ unsigned short old, new;
+
+ old = snd_ac97_read_cache(ac97, reg);
+ new = (old & ~mask) | value;
+ change = old != new;
+ if (change) {
+ ac97->regs[reg] = new;
+ ac97->bus->ops->write(ac97, reg, new);
+ }
+ return change;
+}
+
+static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value)
+{
+ int change;
+ unsigned short old, new, cfg;
+
+ down(&ac97->page_mutex);
+ old = ac97->spec.ad18xx.pcmreg[codec];
+ new = (old & ~mask) | value;
+ change = old != new;
+ if (change) {
+ down(&ac97->reg_mutex);
+ cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG);
+ ac97->spec.ad18xx.pcmreg[codec] = new;
+ /* select single codec */
+ ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
+ (cfg & ~0x7000) |
+ ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
+ /* update PCM bits */
+ ac97->bus->ops->write(ac97, AC97_PCM, new);
+ /* select all codecs */
+ ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
+ cfg | 0x7000);
+ up(&ac97->reg_mutex);
+ }
+ up(&ac97->page_mutex);
+ return change;
+}
+
+/*
+ * Controls
+ */
+
+int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
+ uinfo->value.enumerated.items = e->mask;
+
+ if (uinfo->value.enumerated.item > e->mask - 1)
+ uinfo->value.enumerated.item = e->mask - 1;
+ strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+ unsigned short val;
+
+ val = snd_ac97_read_cache(ac97, e->reg);
+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1);
+ if (e->shift_l != e->shift_r)
+ ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (e->mask - 1);
+
+ return 0;
+}
+
+int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+ unsigned short val;
+ unsigned short mask;
+
+ if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+ return -EINVAL;
+ val = ucontrol->value.enumerated.item[0] << e->shift_l;
+ mask = (e->mask - 1) << e->shift_l;
+ if (e->shift_l != e->shift_r) {
+ if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+ return -EINVAL;
+ val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+ mask |= (e->mask - 1) << e->shift_r;
+ }
+ return snd_ac97_update_bits(ac97, e->reg, mask, val);
+}
+
+/* save/restore ac97 v2.3 paging */
+static int snd_ac97_page_save(ac97_t *ac97, int reg, snd_kcontrol_t *kcontrol)
+{
+ int page_save = -1;
+ if ((kcontrol->private_value & (1<<25)) &&
+ (ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 &&
+ (reg >= 0x60 && reg < 0x70)) {
+ unsigned short page = (kcontrol->private_value >> 26) & 0x0f;
+ down(&ac97->page_mutex); /* lock paging */
+ page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
+ }
+ return page_save;
+}
+
+static void snd_ac97_page_restore(ac97_t *ac97, int page_save)
+{
+ if (page_save >= 0) {
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
+ up(&ac97->page_mutex); /* unlock paging */
+ }
+}
+
+/* volume and switch controls */
+int snd_ac97_info_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = shift == rshift ? 1 : 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+int snd_ac97_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0x01;
+ int page_save;
+
+ page_save = snd_ac97_page_save(ac97, reg, kcontrol);
+ ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask;
+ if (shift != rshift)
+ ucontrol->value.integer.value[1] = (snd_ac97_read_cache(ac97, reg) >> rshift) & mask;
+ if (invert) {
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ if (shift != rshift)
+ ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ }
+ snd_ac97_page_restore(ac97, page_save);
+ return 0;
+}
+
+int snd_ac97_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0x01;
+ int err, page_save;
+ unsigned short val, val2, val_mask;
+
+ page_save = snd_ac97_page_save(ac97, reg, kcontrol);
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = mask - val;
+ val_mask = mask << shift;
+ val = val << shift;
+ if (shift != rshift) {
+ val2 = (ucontrol->value.integer.value[1] & mask);
+ if (invert)
+ val2 = mask - val2;
+ val_mask |= mask << rshift;
+ val |= val2 << rshift;
+ }
+ err = snd_ac97_update_bits(ac97, reg, val_mask, val);
+ snd_ac97_page_restore(ac97, page_save);
+ return err;
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = {
+AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_tone[2] = {
+AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
+AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_pc_beep[2] = {
+AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_mic_boost =
+ AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);
+
+
+static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};
+static const char* std_3d_path[] = {"pre 3D", "post 3D"};
+static const char* std_mix[] = {"Mix", "Mic"};
+static const char* std_mic[] = {"Mic1", "Mic2"};
+
+static const struct ac97_enum std_enum[] = {
+AC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, std_rec_sel),
+AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, std_3d_path),
+AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, std_mix),
+AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, std_mic),
+};
+
+static const snd_kcontrol_new_t snd_ac97_control_capture_src =
+AC97_ENUM("Capture Source", std_enum[0]);
+
+static const snd_kcontrol_new_t snd_ac97_control_capture_vol =
+AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0);
+
+static const snd_kcontrol_new_t snd_ac97_controls_mic_capture[2] = {
+AC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1),
+AC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0)
+};
+
+typedef enum {
+ AC97_GENERAL_PCM_OUT = 0,
+ AC97_GENERAL_STEREO_ENHANCEMENT,
+ AC97_GENERAL_3D,
+ AC97_GENERAL_LOUDNESS,
+ AC97_GENERAL_MONO,
+ AC97_GENERAL_MIC,
+ AC97_GENERAL_LOOPBACK
+} ac97_general_index_t;
+
+static const snd_kcontrol_new_t snd_ac97_controls_general[7] = {
+AC97_ENUM("PCM Out Path & Mute", std_enum[1]),
+AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0),
+AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
+AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),
+AC97_ENUM("Mono Output Select", std_enum[2]),
+AC97_ENUM("Mic Select", std_enum[3]),
+AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
+};
+
+const snd_kcontrol_new_t snd_ac97_controls_3d[2] = {
+AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0),
+AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_center[2] = {
+AC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1),
+AC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_lfe[2] = {
+AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1),
+AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = {
+AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
+AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
+};
+
+static const snd_kcontrol_new_t snd_ac97_control_eapd =
+AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1);
+
+/* change the existing EAPD control as inverted */
+static void set_inv_eapd(ac97_t *ac97, snd_kcontrol_t *kctl)
+{
+ kctl->private_value = AC97_SINGLE_VALUE(AC97_POWERDOWN, 15, 1, 0);
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN, (1<<15), (1<<15)); /* EAPD up */
+ ac97->scaps |= AC97_SCAP_INV_EAPD;
+}
+
+static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ac97_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_NONAUDIO |
+ IEC958_AES0_CON_EMPHASIS_5015 |
+ IEC958_AES0_CON_NOT_COPYRIGHT;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+ IEC958_AES1_CON_ORIGINAL;
+ ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+ return 0;
+}
+
+static int snd_ac97_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ /* FIXME: AC'97 spec doesn't say which bits are used for what */
+ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PRO_FS |
+ IEC958_AES0_PRO_EMPHASIS_5015;
+ return 0;
+}
+
+static int snd_ac97_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ down(&ac97->reg_mutex);
+ ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff;
+ ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff;
+ up(&ac97->reg_mutex);
+ return 0;
+}
+
+static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned int new = 0;
+ unsigned short val = 0;
+ int change;
+
+ new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO);
+ if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) {
+ new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015);
+ switch (new & IEC958_AES0_PRO_FS) {
+ case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break;
+ case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break;
+ case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break;
+ default: val |= 1<<12; break;
+ }
+ if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
+ val |= 1<<3;
+ } else {
+ new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT);
+ new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8);
+ new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24);
+ if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
+ val |= 1<<3;
+ if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT))
+ val |= 1<<2;
+ val |= ((new >> 8) & 0xff) << 4; // category + original
+ switch ((new >> 24) & 0xff) {
+ case IEC958_AES3_CON_FS_44100: val |= 0<<12; break;
+ case IEC958_AES3_CON_FS_48000: val |= 2<<12; break;
+ case IEC958_AES3_CON_FS_32000: val |= 3<<12; break;
+ default: val |= 1<<12; break;
+ }
+ }
+
+ down(&ac97->reg_mutex);
+ change = ac97->spdif_status != new;
+ ac97->spdif_status = new;
+
+ if (ac97->flags & AC97_CS_SPDIF) {
+ int x = (val >> 12) & 0x03;
+ switch (x) {
+ case 0: x = 1; break; // 44.1
+ case 2: x = 0; break; // 48.0
+ default: x = 0; break; // illegal.
+ }
+ change |= snd_ac97_update_bits_nolock(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12)));
+ } else if (ac97->flags & AC97_CX_SPDIF) {
+ int v;
+ v = new & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT;
+ v |= new & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM;
+ change |= snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC,
+ AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT,
+ v);
+ } else {
+ unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS);
+ snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */
+
+ change |= snd_ac97_update_bits_nolock(ac97, AC97_SPDIF, 0x3fff, val);
+ if (extst & AC97_EA_SPDIF) {
+ snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
+ }
+ }
+ up(&ac97->reg_mutex);
+
+ return change;
+}
+
+static int snd_ac97_put_spsa(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ // int invert = (kcontrol->private_value >> 24) & 0xff;
+ unsigned short value, old, new;
+ int change;
+
+ value = (ucontrol->value.integer.value[0] & mask);
+
+ down(&ac97->reg_mutex);
+ mask <<= shift;
+ value <<= shift;
+ old = snd_ac97_read_cache(ac97, reg);
+ new = (old & ~mask) | value;
+ change = old != new;
+
+ if (change) {
+ unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS);
+ snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */
+ change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
+ if (extst & AC97_EA_SPDIF)
+ snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
+ }
+ up(&ac97->reg_mutex);
+ return change;
+}
+
+const snd_kcontrol_new_t snd_ac97_controls_spdif[5] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_ac97_spdif_mask_info,
+ .get = snd_ac97_spdif_cmask_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .info = snd_ac97_spdif_mask_info,
+ .get = snd_ac97_spdif_pmask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_ac97_spdif_mask_info,
+ .get = snd_ac97_spdif_default_get,
+ .put = snd_ac97_spdif_default_put,
+ },
+
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_get_volsw,
+ .put = snd_ac97_put_spsa,
+ .private_value = AC97_SINGLE_VALUE(AC97_EXTENDED_STATUS, 4, 3, 0)
+ },
+};
+
+#define AD18XX_PCM_BITS(xname, codec, lshift, rshift, mask) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_bits, \
+ .get = snd_ac97_ad18xx_pcm_get_bits, .put = snd_ac97_ad18xx_pcm_put_bits, \
+ .private_value = (codec) | ((lshift) << 8) | ((rshift) << 12) | ((mask) << 16) }
+
+static int snd_ac97_ad18xx_pcm_info_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int mask = (kcontrol->private_value >> 16) & 0x0f;
+ int lshift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES))
+ uinfo->count = 2;
+ else
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_get_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int codec = kcontrol->private_value & 3;
+ int lshift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+
+ ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> lshift) & mask);
+ if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES))
+ ucontrol->value.integer.value[1] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> rshift) & mask);
+ return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_put_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int codec = kcontrol->private_value & 3;
+ int lshift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ unsigned short val, valmask;
+
+ val = (mask - (ucontrol->value.integer.value[0] & mask)) << lshift;
+ valmask = mask << lshift;
+ if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES)) {
+ val |= (mask - (ucontrol->value.integer.value[1] & mask)) << rshift;
+ valmask |= mask << rshift;
+ }
+ return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, valmask, val);
+}
+
+#define AD18XX_PCM_VOLUME(xname, codec) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_volume, \
+ .get = snd_ac97_ad18xx_pcm_get_volume, .put = snd_ac97_ad18xx_pcm_put_volume, \
+ .private_value = codec }
+
+static int snd_ac97_ad18xx_pcm_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 31;
+ return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int codec = kcontrol->private_value & 3;
+
+ down(&ac97->page_mutex);
+ ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
+ ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
+ up(&ac97->page_mutex);
+ return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int codec = kcontrol->private_value & 3;
+ unsigned short val1, val2;
+
+ val1 = 31 - (ucontrol->value.integer.value[0] & 31);
+ val2 = 31 - (ucontrol->value.integer.value[1] & 31);
+ return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2);
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_pcm[2] = {
+AD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 7, 1),
+AD18XX_PCM_VOLUME("PCM Playback Volume", 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_surround[2] = {
+AD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 7, 1),
+AD18XX_PCM_VOLUME("Surround Playback Volume", 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_center[2] = {
+AD18XX_PCM_BITS("Center Playback Switch", 2, 15, 15, 1),
+AD18XX_PCM_BITS("Center Playback Volume", 2, 8, 8, 31)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_lfe[2] = {
+AD18XX_PCM_BITS("LFE Playback Switch", 2, 7, 7, 1),
+AD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 0, 31)
+};
+
+/*
+ *
+ */
+
+static void snd_ac97_powerdown(ac97_t *ac97);
+
+static int snd_ac97_bus_free(ac97_bus_t *bus)
+{
+ if (bus) {
+ snd_ac97_bus_proc_done(bus);
+ kfree(bus->pcms);
+ if (bus->private_free)
+ bus->private_free(bus);
+ kfree(bus);
+ }
+ return 0;
+}
+
+static int snd_ac97_bus_dev_free(snd_device_t *device)
+{
+ ac97_bus_t *bus = device->device_data;
+ return snd_ac97_bus_free(bus);
+}
+
+static int snd_ac97_free(ac97_t *ac97)
+{
+ if (ac97) {
+ snd_ac97_proc_done(ac97);
+ if (ac97->bus) {
+ ac97->bus->codec[ac97->num] = NULL;
+ if (ac97->bus->shared_type) {
+ down(&shared_codec_mutex);
+ shared_codec[ac97->bus->shared_type-1][ac97->num] = NULL;
+ up(&shared_codec_mutex);
+ }
+ }
+ if (ac97->private_free)
+ ac97->private_free(ac97);
+ kfree(ac97);
+ }
+ return 0;
+}
+
+static int snd_ac97_dev_free(snd_device_t *device)
+{
+ ac97_t *ac97 = device->device_data;
+ snd_ac97_powerdown(ac97); /* for avoiding click noises during shut down */
+ return snd_ac97_free(ac97);
+}
+
+static int snd_ac97_try_volume_mix(ac97_t * ac97, int reg)
+{
+ unsigned short val, mask = 0x8000;
+
+ if (! snd_ac97_valid_reg(ac97, reg))
+ return 0;
+
+ switch (reg) {
+ case AC97_MASTER_TONE:
+ return ac97->caps & 0x04 ? 1 : 0;
+ case AC97_HEADPHONE:
+ return ac97->caps & 0x10 ? 1 : 0;
+ case AC97_REC_GAIN_MIC:
+ return ac97->caps & 0x01 ? 1 : 0;
+ case AC97_3D_CONTROL:
+ if (ac97->caps & 0x7c00) {
+ val = snd_ac97_read(ac97, reg);
+ /* if nonzero - fixed and we can't set it */
+ return val == 0;
+ }
+ return 0;
+ case AC97_CENTER_LFE_MASTER: /* center */
+ if ((ac97->ext_id & AC97_EI_CDAC) == 0)
+ return 0;
+ break;
+ case AC97_CENTER_LFE_MASTER+1: /* lfe */
+ if ((ac97->ext_id & AC97_EI_LDAC) == 0)
+ return 0;
+ reg = AC97_CENTER_LFE_MASTER;
+ mask = 0x0080;
+ break;
+ case AC97_SURROUND_MASTER:
+ if ((ac97->ext_id & AC97_EI_SDAC) == 0)
+ return 0;
+ break;
+ }
+
+ if (ac97->limited_regs && test_bit(reg, ac97->reg_accessed))
+ return 1; /* allow without check */
+
+ val = snd_ac97_read(ac97, reg);
+ if (!(val & mask)) {
+ /* nothing seems to be here - mute flag is not set */
+ /* try another test */
+ snd_ac97_write_cache(ac97, reg, val | mask);
+ val = snd_ac97_read(ac97, reg);
+ if (!(val & mask))
+ return 0; /* nothing here */
+ }
+ return 1; /* success, useable */
+}
+
+static void check_volume_resolution(ac97_t *ac97, int reg, unsigned char *lo_max, unsigned char *hi_max)
+{
+ unsigned short cbit[3] = { 0x20, 0x10, 0x01 };
+ unsigned char max[3] = { 63, 31, 15 };
+ int i;
+
+ *lo_max = *hi_max = 0;
+ for (i = 0 ; i < ARRAY_SIZE(cbit); i++) {
+ unsigned short val;
+ snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
+ val = snd_ac97_read(ac97, reg);
+ if (! *lo_max && (val & cbit[i]))
+ *lo_max = max[i];
+ if (! *hi_max && (val & (cbit[i] << 8)))
+ *hi_max = max[i];
+ if (*lo_max && *hi_max)
+ break;
+ }
+}
+
+int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit)
+{
+ unsigned short mask, val, orig, res;
+
+ mask = 1 << bit;
+ orig = snd_ac97_read(ac97, reg);
+ val = orig ^ mask;
+ snd_ac97_write(ac97, reg, val);
+ res = snd_ac97_read(ac97, reg);
+ snd_ac97_write_cache(ac97, reg, orig);
+ return res == val;
+}
+
+/* check the volume resolution of center/lfe */
+static void snd_ac97_change_volume_params2(ac97_t * ac97, int reg, int shift, unsigned char *max)
+{
+ unsigned short val, val1;
+
+ *max = 63;
+ val = 0x8080 | (0x20 << shift);
+ snd_ac97_write(ac97, reg, val);
+ val1 = snd_ac97_read(ac97, reg);
+ if (val != val1) {
+ *max = 31;
+ }
+ /* reset volume to zero */
+ snd_ac97_write_cache(ac97, reg, 0x8080);
+}
+
+static inline int printable(unsigned int x)
+{
+ x &= 0xff;
+ if (x < ' ' || x >= 0x71) {
+ if (x <= 0x89)
+ return x - 0x71 + 'A';
+ return '?';
+ }
+ return x;
+}
+
+snd_kcontrol_t *snd_ac97_cnew(const snd_kcontrol_new_t *_template, ac97_t * ac97)
+{
+ snd_kcontrol_new_t template;
+ memcpy(&template, _template, sizeof(template));
+ snd_runtime_check(!template.index, return NULL);
+ template.index = ac97->num;
+ return snd_ctl_new1(&template, ac97);
+}
+
+/*
+ * create mute switch(es) for normal stereo controls
+ */
+static int snd_ac97_cmute_new_stereo(snd_card_t *card, char *name, int reg, int check_stereo, ac97_t *ac97)
+{
+ snd_kcontrol_t *kctl;
+ int err;
+ unsigned short val, val1, mute_mask;
+
+ if (! snd_ac97_valid_reg(ac97, reg))
+ return 0;
+
+ mute_mask = 0x8000;
+ val = snd_ac97_read(ac97, reg);
+ if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) {
+ /* check whether both mute bits work */
+ val1 = val | 0x8080;
+ snd_ac97_write(ac97, reg, val1);
+ if (val1 == snd_ac97_read(ac97, reg))
+ mute_mask = 0x8080;
+ }
+ if (mute_mask == 0x8080) {
+ snd_kcontrol_new_t tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
+ tmp.index = ac97->num;
+ kctl = snd_ctl_new1(&tmp, ac97);
+ } else {
+ snd_kcontrol_new_t tmp = AC97_SINGLE(name, reg, 15, 1, 1);
+ tmp.index = ac97->num;
+ kctl = snd_ctl_new1(&tmp, ac97);
+ }
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ /* mute as default */
+ snd_ac97_write_cache(ac97, reg, val | mute_mask);
+ return 0;
+}
+
+/*
+ * create a volume for normal stereo/mono controls
+ */
+static int snd_ac97_cvol_new(snd_card_t *card, char *name, int reg, unsigned int lo_max,
+ unsigned int hi_max, ac97_t *ac97)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+
+ if (! snd_ac97_valid_reg(ac97, reg))
+ return 0;
+ if (hi_max) {
+ /* invert */
+ snd_kcontrol_new_t tmp = AC97_DOUBLE(name, reg, 8, 0, lo_max, 1);
+ tmp.index = ac97->num;
+ kctl = snd_ctl_new1(&tmp, ac97);
+ } else {
+ /* invert */
+ snd_kcontrol_new_t tmp = AC97_SINGLE(name, reg, 0, lo_max, 1);
+ tmp.index = ac97->num;
+ kctl = snd_ctl_new1(&tmp, ac97);
+ }
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ snd_ac97_write_cache(ac97, reg,
+ (snd_ac97_read(ac97, reg) & 0x8080) |
+ lo_max | (hi_max << 8));
+ return 0;
+}
+
+/*
+ * create a mute-switch and a volume for normal stereo/mono controls
+ */
+static int snd_ac97_cmix_new_stereo(snd_card_t *card, const char *pfx, int reg, int check_stereo, ac97_t *ac97)
+{
+ int err;
+ char name[44];
+ unsigned char lo_max, hi_max;
+
+ if (! snd_ac97_valid_reg(ac97, reg))
+ return 0;
+
+ if (snd_ac97_try_bit(ac97, reg, 15)) {
+ sprintf(name, "%s Switch", pfx);
+ if ((err = snd_ac97_cmute_new_stereo(card, name, reg, check_stereo, ac97)) < 0)
+ return err;
+ }
+ check_volume_resolution(ac97, reg, &lo_max, &hi_max);
+ if (lo_max) {
+ sprintf(name, "%s Volume", pfx);
+ if ((err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97)) < 0)
+ return err;
+ }
+ return 0;
+}
+
+#define snd_ac97_cmix_new(card, pfx, reg, ac97) snd_ac97_cmix_new_stereo(card, pfx, reg, 0, ac97)
+#define snd_ac97_cmute_new(card, name, reg, ac97) snd_ac97_cmute_new_stereo(card, name, reg, 0, ac97)
+
+static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97);
+
+static int snd_ac97_mixer_build(ac97_t * ac97)
+{
+ snd_card_t *card = ac97->bus->card;
+ snd_kcontrol_t *kctl;
+ int err;
+ unsigned int idx;
+ unsigned char max;
+
+ /* build master controls */
+ /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */
+ if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) {
+ if (ac97->flags & AC97_HAS_NO_MASTER_VOL)
+ err = snd_ac97_cmute_new(card, "Master Playback Switch", AC97_MASTER, ac97);
+ else
+ err = snd_ac97_cmix_new(card, "Master Playback", AC97_MASTER, ac97);
+ if (err < 0)
+ return err;
+ }
+
+ ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080;
+
+ /* build center controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0)
+ return err;
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0)
+ return err;
+ snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
+ kctl->private_value &= ~(0xff << 16);
+ kctl->private_value |= (int)max << 16;
+ snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max);
+ }
+
+ /* build LFE controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0)
+ return err;
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0)
+ return err;
+ snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
+ kctl->private_value &= ~(0xff << 16);
+ kctl->private_value |= (int)max << 16;
+ snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8);
+ }
+
+ /* build surround controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) {
+ /* Surround Master (0x38) is with stereo mutes */
+ if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0)
+ return err;
+ }
+
+ /* build headphone controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
+ if ((err = snd_ac97_cmix_new(card, "Headphone Playback", AC97_HEADPHONE, ac97)) < 0)
+ return err;
+ }
+
+ /* build master mono controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
+ if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", AC97_MASTER_MONO, ac97)) < 0)
+ return err;
+ }
+
+ /* build master tone controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) {
+ for (idx = 0; idx < 2; idx++) {
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0)
+ return err;
+ if (ac97->id == AC97_ID_YMF753) {
+ kctl->private_value &= ~(0xff << 16);
+ kctl->private_value |= 7 << 16;
+ }
+ }
+ snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f);
+ }
+
+ /* build PC Speaker controls */
+ if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
+ ((ac97->flags & AC97_HAS_PC_BEEP) ||
+ snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
+ for (idx = 0; idx < 2; idx++)
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
+ return err;
+ snd_ac97_write_cache(ac97, AC97_PC_BEEP,
+ snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
+ }
+
+ /* build Phone controls */
+ if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
+ if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
+ if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0)
+ return err;
+ }
+ }
+
+ /* build MIC controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
+ if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0)
+ return err;
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0)
+ return err;
+ }
+
+ /* build Line controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
+ if ((err = snd_ac97_cmix_new(card, "Line Playback", AC97_LINE, ac97)) < 0)
+ return err;
+ }
+
+ /* build CD controls */
+ if (!(ac97->flags & AC97_HAS_NO_CD)) {
+ if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
+ if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0)
+ return err;
+ }
+ }
+
+ /* build Video controls */
+ if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
+ if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
+ if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0)
+ return err;
+ }
+ }
+
+ /* build Aux controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
+ if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0)
+ return err;
+ }
+
+ /* build PCM controls */
+ if (ac97->flags & AC97_AD_MULTI) {
+ unsigned short init_val;
+ if (ac97->flags & AC97_STEREO_MUTES)
+ init_val = 0x9f9f;
+ else
+ init_val = 0x9f1f;
+ for (idx = 0; idx < 2; idx++)
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
+ return err;
+ ac97->spec.ad18xx.pcmreg[0] = init_val;
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
+ for (idx = 0; idx < 2; idx++)
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
+ return err;
+ ac97->spec.ad18xx.pcmreg[1] = init_val;
+ }
+ if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
+ for (idx = 0; idx < 2; idx++)
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
+ return err;
+ for (idx = 0; idx < 2; idx++)
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
+ return err;
+ ac97->spec.ad18xx.pcmreg[2] = init_val;
+ }
+ snd_ac97_write_cache(ac97, AC97_PCM, init_val);
+ } else {
+ if (ac97->flags & AC97_HAS_NO_PCM_VOL)
+ err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97);
+ else
+ err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97);
+ if (err < 0)
+ return err;
+ }
+
+ /* build Capture controls */
+ if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
+ return err;
+ if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
+ if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0)
+ return err;
+ }
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
+ return err;
+ snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
+ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
+ }
+ /* build MIC Capture controls */
+ if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
+ for (idx = 0; idx < 2; idx++)
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
+ return err;
+ snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
+ }
+
+ /* build PCM out path & mute control */
+ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0)
+ return err;
+ }
+
+ /* build Simulated Stereo Enhancement control */
+ if (ac97->caps & 0x0008) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0)
+ return err;
+ }
+
+ /* build 3D Stereo Enhancement control */
+ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0)
+ return err;
+ }
+
+ /* build Loudness control */
+ if (ac97->caps & 0x0020) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0)
+ return err;
+ }
+
+ /* build Mono output select control */
+ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0)
+ return err;
+ }
+
+ /* build Mic select control */
+ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0)
+ return err;
+ }
+
+ /* build ADC/DAC loopback control */
+ if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0)
+ return err;
+ }
+
+ snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, ~AC97_GP_DRSS_MASK, 0x0000);
+
+ /* build 3D controls */
+ if (ac97->build_ops->build_3d) {
+ ac97->build_ops->build_3d(ac97);
+ } else {
+ if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) {
+ unsigned short val;
+ val = 0x0707;
+ snd_ac97_write(ac97, AC97_3D_CONTROL, val);
+ val = snd_ac97_read(ac97, AC97_3D_CONTROL);
+ val = val == 0x0606;
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ return err;
+ if (val)
+ kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16);
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0)
+ return err;
+ if (val)
+ kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16);
+ snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+ }
+ }
+
+ /* build S/PDIF controls */
+ if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
+ if (ac97->build_ops->build_spdif) {
+ if ((err = ac97->build_ops->build_spdif(ac97)) < 0)
+ return err;
+ } else {
+ for (idx = 0; idx < 5; idx++)
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+ return err;
+ if (ac97->build_ops->build_post_spdif) {
+ if ((err = ac97->build_ops->build_post_spdif(ac97)) < 0)
+ return err;
+ }
+ /* set default PCM S/PDIF params */
+ /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
+ snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
+ ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97);
+ }
+ ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+ }
+
+ /* build chip specific controls */
+ if (ac97->build_ops->build_specific)
+ if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+ return err;
+
+ if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) {
+ kctl = snd_ac97_cnew(&snd_ac97_control_eapd, ac97);
+ if (! kctl)
+ return -ENOMEM;
+ if (ac97->scaps & AC97_SCAP_INV_EAPD)
+ set_inv_eapd(ac97, kctl);
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_ac97_modem_build(snd_card_t * card, ac97_t * ac97)
+{
+ /* TODO */
+ //printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG));
+ snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
+ snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH));
+ snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff);
+ snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0);
+ snd_ac97_write(ac97, AC97_MISC_AFE, 0x0);
+ return 0;
+}
+
+static int snd_ac97_test_rate(ac97_t *ac97, int reg, int shadow_reg, int rate)
+{
+ unsigned short val;
+ unsigned int tmp;
+
+ tmp = ((unsigned int)rate * ac97->bus->clock) / 48000;
+ snd_ac97_write_cache(ac97, reg, tmp & 0xffff);
+ if (shadow_reg)
+ snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff);
+ val = snd_ac97_read(ac97, reg);
+ return val == (tmp & 0xffff);
+}
+
+static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result)
+{
+ unsigned int result = 0;
+ unsigned short saved;
+
+ if (ac97->bus->no_vra) {
+ *r_result = SNDRV_PCM_RATE_48000;
+ if ((ac97->flags & AC97_DOUBLE_RATE) &&
+ reg == AC97_PCM_FRONT_DAC_RATE)
+ *r_result |= SNDRV_PCM_RATE_96000;
+ return;
+ }
+
+ saved = snd_ac97_read(ac97, reg);
+ if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_DRA, 0);
+ /* test a non-standard rate */
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000))
+ result |= SNDRV_PCM_RATE_CONTINUOUS;
+ /* let's try to obtain standard rates */
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000))
+ result |= SNDRV_PCM_RATE_8000;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025))
+ result |= SNDRV_PCM_RATE_11025;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000))
+ result |= SNDRV_PCM_RATE_16000;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050))
+ result |= SNDRV_PCM_RATE_22050;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000))
+ result |= SNDRV_PCM_RATE_32000;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100))
+ result |= SNDRV_PCM_RATE_44100;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000))
+ result |= SNDRV_PCM_RATE_48000;
+ if ((ac97->flags & AC97_DOUBLE_RATE) &&
+ reg == AC97_PCM_FRONT_DAC_RATE) {
+ /* test standard double rates */
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_DRA, AC97_EA_DRA);
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 64000 / 2))
+ result |= SNDRV_PCM_RATE_64000;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 88200 / 2))
+ result |= SNDRV_PCM_RATE_88200;
+ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 96000 / 2))
+ result |= SNDRV_PCM_RATE_96000;
+ /* some codecs don't support variable double rates */
+ if (!snd_ac97_test_rate(ac97, reg, shadow_reg, 76100 / 2))
+ result &= ~SNDRV_PCM_RATE_CONTINUOUS;
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_DRA, 0);
+ }
+ /* restore the default value */
+ snd_ac97_write_cache(ac97, reg, saved);
+ if (shadow_reg)
+ snd_ac97_write_cache(ac97, shadow_reg, saved);
+ *r_result = result;
+}
+
+/* check AC97_SPDIF register to accept which sample rates */
+static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97)
+{
+ unsigned int result = 0;
+ int i;
+ static unsigned short ctl_bits[] = {
+ AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K
+ };
+ static unsigned int rate_bits[] = {
+ SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000
+ };
+
+ for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) {
+ snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]);
+ if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i])
+ result |= rate_bits[i];
+ }
+ return result;
+}
+
+/* look for the codec id table matching with the given id */
+static const ac97_codec_id_t *look_for_codec_id(const ac97_codec_id_t *table,
+ unsigned int id)
+{
+ const ac97_codec_id_t *pid;
+
+ for (pid = table; pid->id; pid++)
+ if (pid->id == (id & pid->mask))
+ return pid;
+ return NULL;
+}
+
+void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem)
+{
+ const ac97_codec_id_t *pid;
+
+ sprintf(name, "0x%x %c%c%c", id,
+ printable(id >> 24),
+ printable(id >> 16),
+ printable(id >> 8));
+ pid = look_for_codec_id(snd_ac97_codec_id_vendors, id);
+ if (! pid)
+ return;
+
+ strcpy(name, pid->name);
+ if (ac97 && pid->patch) {
+ if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
+ (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
+ pid->patch(ac97);
+ }
+
+ pid = look_for_codec_id(snd_ac97_codec_ids, id);
+ if (pid) {
+ strcat(name, " ");
+ strcat(name, pid->name);
+ if (pid->mask != 0xffffffff)
+ sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
+ if (ac97 && pid->patch) {
+ if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
+ (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
+ pid->patch(ac97);
+ }
+ } else
+ sprintf(name + strlen(name), " id %x", id & 0xff);
+}
+
+/**
+ * snd_ac97_get_short_name - retrieve codec name
+ * @ac97: the codec instance
+ *
+ * Returns the short identifying name of the codec.
+ */
+const char *snd_ac97_get_short_name(ac97_t *ac97)
+{
+ const ac97_codec_id_t *pid;
+
+ for (pid = snd_ac97_codec_ids; pid->id; pid++)
+ if (pid->id == (ac97->id & pid->mask))
+ return pid->name;
+ return "unknown codec";
+}
+
+
+/* wait for a while until registers are accessible after RESET
+ * return 0 if ok, negative not ready
+ */
+static int ac97_reset_wait(ac97_t *ac97, int timeout, int with_modem)
+{
+ unsigned long end_time;
+ unsigned short val;
+
+ end_time = jiffies + timeout;
+ do {
+
+ /* use preliminary reads to settle the communication */
+ snd_ac97_read(ac97, AC97_RESET);
+ snd_ac97_read(ac97, AC97_VENDOR_ID1);
+ snd_ac97_read(ac97, AC97_VENDOR_ID2);
+ /* modem? */
+ if (with_modem) {
+ val = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+ if (val != 0xffff && (val & 1) != 0)
+ return 0;
+ }
+ if (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) {
+ /* probably only Xbox issue - all registers are read as zero */
+ val = snd_ac97_read(ac97, AC97_VENDOR_ID1);
+ if (val != 0 && val != 0xffff)
+ return 0;
+ } else {
+ /* because the PCM or MASTER volume registers can be modified,
+ * the REC_GAIN register is used for tests
+ */
+ /* test if we can write to the record gain volume register */
+ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
+ if ((snd_ac97_read(ac97, AC97_REC_GAIN) & 0x7fff) == 0x0a05)
+ return 0;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after_eq(end_time, jiffies));
+ return -ENODEV;
+}
+
+/**
+ * snd_ac97_bus - create an AC97 bus component
+ * @card: the card instance
+ * @num: the bus number
+ * @ops: the bus callbacks table
+ * @private_data: private data pointer for the new instance
+ * @rbus: the pointer to store the new AC97 bus instance.
+ *
+ * Creates an AC97 bus component. An ac97_bus_t instance is newly
+ * allocated and initialized.
+ *
+ * The ops table must include valid callbacks (at least read and
+ * write). The other callbacks, wait and reset, are not mandatory.
+ *
+ * The clock is set to 48000. If another clock is needed, set
+ * (*rbus)->clock manually.
+ *
+ * The AC97 bus instance is registered as a low-level device, so you don't
+ * have to release it manually.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ac97_bus(snd_card_t *card, int num, ac97_bus_ops_t *ops,
+ void *private_data, ac97_bus_t **rbus)
+{
+ int err;
+ ac97_bus_t *bus;
+ static snd_device_ops_t dev_ops = {
+ .dev_free = snd_ac97_bus_dev_free,
+ };
+
+ snd_assert(card != NULL, return -EINVAL);
+ snd_assert(rbus != NULL, return -EINVAL);
+ bus = kcalloc(1, sizeof(*bus), GFP_KERNEL);
+ if (bus == NULL)
+ return -ENOMEM;
+ bus->card = card;
+ bus->num = num;
+ bus->ops = ops;
+ bus->private_data = private_data;
+ bus->clock = 48000;
+ spin_lock_init(&bus->bus_lock);
+ snd_ac97_bus_proc_init(bus);
+ if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
+ snd_ac97_bus_free(bus);
+ return err;
+ }
+ *rbus = bus;
+ return 0;
+}
+
+/* build_ops to do nothing */
+static struct snd_ac97_build_ops null_build_ops;
+
+/**
+ * snd_ac97_mixer - create an Codec97 component
+ * @bus: the AC97 bus which codec is attached to
+ * @template: the template of ac97, including index, callbacks and
+ * the private data.
+ * @rac97: the pointer to store the new ac97 instance.
+ *
+ * Creates an Codec97 component. An ac97_t instance is newly
+ * allocated and initialized from the template. The codec
+ * is then initialized by the standard procedure.
+ *
+ * The template must include the codec number (num) and address (addr),
+ * and the private data (private_data).
+ *
+ * The ac97 instance is registered as a low-level device, so you don't
+ * have to release it manually.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
+{
+ int err;
+ ac97_t *ac97;
+ snd_card_t *card;
+ char name[64];
+ unsigned long end_time;
+ unsigned int reg;
+ const ac97_codec_id_t *pid;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_ac97_dev_free,
+ };
+
+ snd_assert(rac97 != NULL, return -EINVAL);
+ *rac97 = NULL;
+ snd_assert(bus != NULL && template != NULL, return -EINVAL);
+ snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL);
+
+ snd_assert(bus->shared_type <= AC97_SHARED_TYPES, return -EINVAL);
+ if (bus->shared_type) {
+ /* already shared? */
+ down(&shared_codec_mutex);
+ ac97 = shared_codec[bus->shared_type-1][template->num];
+ if (ac97) {
+ if ((ac97_is_audio(ac97) && (template->scaps & AC97_SCAP_SKIP_AUDIO)) ||
+ (ac97_is_modem(ac97) && (template->scaps & AC97_SCAP_SKIP_MODEM))) {
+ up(&shared_codec_mutex);
+ return -EACCES; /* skip this */
+ }
+ }
+ up(&shared_codec_mutex);
+ }
+
+ card = bus->card;
+ ac97 = kcalloc(1, sizeof(*ac97), GFP_KERNEL);
+ if (ac97 == NULL)
+ return -ENOMEM;
+ ac97->private_data = template->private_data;
+ ac97->private_free = template->private_free;
+ ac97->bus = bus;
+ ac97->pci = template->pci;
+ ac97->num = template->num;
+ ac97->addr = template->addr;
+ ac97->scaps = template->scaps;
+ ac97->limited_regs = template->limited_regs;
+ memcpy(ac97->reg_accessed, template->reg_accessed, sizeof(ac97->reg_accessed));
+ bus->codec[ac97->num] = ac97;
+ init_MUTEX(&ac97->reg_mutex);
+ init_MUTEX(&ac97->page_mutex);
+
+ if (ac97->pci) {
+ pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor);
+ pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device);
+ }
+ if (bus->ops->reset) {
+ bus->ops->reset(ac97);
+ goto __access_ok;
+ }
+
+ ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
+ ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
+ if (ac97->id && ac97->id != (unsigned int)-1) {
+ pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
+ if (pid && (pid->flags & AC97_DEFAULT_POWER_OFF))
+ goto __access_ok;
+ }
+
+ snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */
+ if (bus->ops->wait)
+ bus->ops->wait(ac97);
+ else {
+ udelay(50);
+ if (ac97->scaps & AC97_SCAP_SKIP_AUDIO)
+ err = ac97_reset_wait(ac97, HZ/2, 1);
+ else {
+ err = ac97_reset_wait(ac97, HZ/2, 0);
+ if (err < 0)
+ err = ac97_reset_wait(ac97, HZ/2, 1);
+ }
+ if (err < 0) {
+ snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num);
+ /* proceed anyway - it's often non-critical */
+ }
+ }
+ __access_ok:
+ ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
+ ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
+ if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) &&
+ (ac97->id == 0x00000000 || ac97->id == 0xffffffff)) {
+ snd_printk(KERN_ERR "AC'97 %d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->id);
+ snd_ac97_free(ac97);
+ return -EIO;
+ }
+ pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
+ if (pid)
+ ac97->flags |= pid->flags;
+
+ /* test for AC'97 */
+ if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {
+ /* test if we can write to the record gain volume register */
+ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
+ if (((err = snd_ac97_read(ac97, AC97_REC_GAIN)) & 0x7fff) == 0x0a06)
+ ac97->scaps |= AC97_SCAP_AUDIO;
+ }
+ if (ac97->scaps & AC97_SCAP_AUDIO) {
+ ac97->caps = snd_ac97_read(ac97, AC97_RESET);
+ ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+ if (ac97->ext_id == 0xffff) /* invalid combination */
+ ac97->ext_id = 0;
+ }
+
+ /* test for MC'97 */
+ if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM) && !(ac97->scaps & AC97_SCAP_MODEM)) {
+ ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+ if (ac97->ext_mid == 0xffff) /* invalid combination */
+ ac97->ext_mid = 0;
+ if (ac97->ext_mid & 1)
+ ac97->scaps |= AC97_SCAP_MODEM;
+ }
+
+ if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) {
+ if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM)))
+ snd_printk(KERN_ERR "AC'97 %d access error (not audio or modem codec)\n", ac97->num);
+ snd_ac97_free(ac97);
+ return -EACCES;
+ }
+
+ if (bus->ops->reset) // FIXME: always skipping?
+ goto __ready_ok;
+
+ /* FIXME: add powerdown control */
+ if (ac97_is_audio(ac97)) {
+ /* nothing should be in powerdown mode */
+ snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
+ if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+ snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */
+ udelay(100);
+ snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
+ }
+ /* nothing should be in powerdown mode */
+ snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
+ end_time = jiffies + (HZ / 10);
+ do {
+ if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
+ goto __ready_ok;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk(KERN_WARNING "AC'97 %d analog subsections not ready\n", ac97->num);
+ }
+
+ /* FIXME: add powerdown control */
+ if (ac97_is_modem(ac97)) {
+ unsigned char tmp;
+
+ /* nothing should be in powerdown mode */
+ /* note: it's important to set the rate at first */
+ tmp = AC97_MEA_GPIO;
+ if (ac97->ext_mid & AC97_MEI_LINE1) {
+ snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 12000);
+ tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1;
+ }
+ if (ac97->ext_mid & AC97_MEI_LINE2) {
+ snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 12000);
+ tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2;
+ }
+ if (ac97->ext_mid & AC97_MEI_HANDSET) {
+ snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 12000);
+ tmp |= AC97_MEA_HADC | AC97_MEA_HDAC;
+ }
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));
+ udelay(100);
+ /* nothing should be in powerdown mode */
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));
+ end_time = jiffies + (HZ / 10);
+ do {
+ if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)
+ goto __ready_ok;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk(KERN_WARNING "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
+ }
+
+ __ready_ok:
+ if (ac97_is_audio(ac97))
+ ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT;
+ else
+ ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT;
+ if (ac97->ext_id & 0x01c9) { /* L/R, MIC, SDAC, LDAC VRA support */
+ reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+ reg |= ac97->ext_id & 0x01c0; /* LDAC/SDAC/CDAC */
+ if (! bus->no_vra)
+ reg |= ac97->ext_id & 0x0009; /* VRA/VRM */
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
+ }
+ if ((ac97->ext_id & AC97_EI_DRA) && bus->dra) {
+ /* Intel controllers require double rate data to be put in
+ * slots 7+8, so let's hope the codec supports it. */
+ snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, AC97_GP_DRSS_78);
+ if ((snd_ac97_read(ac97, AC97_GENERAL_PURPOSE) & AC97_GP_DRSS_MASK) == AC97_GP_DRSS_78)
+ ac97->flags |= AC97_DOUBLE_RATE;
+ }
+ if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */
+ snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]);
+ snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]);
+ } else {
+ ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
+ if (ac97->flags & AC97_DOUBLE_RATE)
+ ac97->rates[AC97_RATES_FRONT_DAC] |= SNDRV_PCM_RATE_96000;
+ ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
+ }
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ /* codec specific code (patch) should override these values */
+ ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000;
+ }
+ if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */
+ snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]);
+ } else {
+ ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
+ }
+ if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */
+ snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
+ ac97->scaps |= AC97_SCAP_SURROUND_DAC;
+ }
+ if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */
+ snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
+ ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
+ }
+ /* additional initializations */
+ if (bus->ops->init)
+ bus->ops->init(ac97);
+ snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97));
+ snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code
+ if (! ac97->build_ops)
+ ac97->build_ops = &null_build_ops;
+
+ if (ac97_is_audio(ac97)) {
+ char comp[16];
+ if (card->mixername[0] == '\0') {
+ strcpy(card->mixername, name);
+ } else {
+ if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
+ strcat(card->mixername, ",");
+ strcat(card->mixername, name);
+ }
+ }
+ sprintf(comp, "AC97a:%08x", ac97->id);
+ if ((err = snd_component_add(card, comp)) < 0) {
+ snd_ac97_free(ac97);
+ return err;
+ }
+ if (snd_ac97_mixer_build(ac97) < 0) {
+ snd_ac97_free(ac97);
+ return -ENOMEM;
+ }
+ }
+ if (ac97_is_modem(ac97)) {
+ char comp[16];
+ if (card->mixername[0] == '\0') {
+ strcpy(card->mixername, name);
+ } else {
+ if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
+ strcat(card->mixername, ",");
+ strcat(card->mixername, name);
+ }
+ }
+ sprintf(comp, "AC97m:%08x", ac97->id);
+ if ((err = snd_component_add(card, comp)) < 0) {
+ snd_ac97_free(ac97);
+ return err;
+ }
+ if (snd_ac97_modem_build(card, ac97) < 0) {
+ snd_ac97_free(ac97);
+ return -ENOMEM;
+ }
+ }
+ /* make sure the proper powerdown bits are cleared */
+ if (ac97->scaps) {
+ reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+ reg &= ~AC97_EA_PRJ;
+ if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+ reg &= ~(AC97_EA_PRI | AC97_EA_PRK);
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
+ }
+ snd_ac97_proc_init(ac97);
+ if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) {
+ snd_ac97_free(ac97);
+ return err;
+ }
+ *rac97 = ac97;
+
+ if (bus->shared_type) {
+ down(&shared_codec_mutex);
+ shared_codec[bus->shared_type-1][ac97->num] = ac97;
+ up(&shared_codec_mutex);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Power down the chip.
+ *
+ * MASTER and HEADPHONE registers are muted but the register cache values
+ * are not changed, so that the values can be restored in snd_ac97_resume().
+ */
+static void snd_ac97_powerdown(ac97_t *ac97)
+{
+ unsigned short power;
+
+ if (ac97_is_audio(ac97)) {
+ /* some codecs have stereo mute bits */
+ snd_ac97_write(ac97, AC97_MASTER, 0x9f9f);
+ snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f);
+ }
+
+ power = ac97->regs[AC97_POWERDOWN] | 0x8000; /* EAPD */
+ power |= 0x4000; /* Headphone amplifier powerdown */
+ power |= 0x0300; /* ADC & DAC powerdown */
+ snd_ac97_write(ac97, AC97_POWERDOWN, power);
+ udelay(100);
+ power |= 0x0400; /* Analog Mixer powerdown (Vref on) */
+ snd_ac97_write(ac97, AC97_POWERDOWN, power);
+ udelay(100);
+#if 0
+ /* FIXME: this causes click noises on some boards at resume */
+ power |= 0x3800; /* AC-link powerdown, internal Clk disable */
+ snd_ac97_write(ac97, AC97_POWERDOWN, power);
+#endif
+}
+
+
+#ifdef CONFIG_PM
+/**
+ * snd_ac97_suspend - General suspend function for AC97 codec
+ * @ac97: the ac97 instance
+ *
+ * Suspends the codec, power down the chip.
+ */
+void snd_ac97_suspend(ac97_t *ac97)
+{
+ if (ac97->build_ops->suspend)
+ ac97->build_ops->suspend(ac97);
+ snd_ac97_powerdown(ac97);
+}
+
+/*
+ * restore ac97 status
+ */
+void snd_ac97_restore_status(ac97_t *ac97)
+{
+ int i;
+
+ for (i = 2; i < 0x7c ; i += 2) {
+ if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID)
+ continue;
+ /* restore only accessible registers
+ * some chip (e.g. nm256) may hang up when unsupported registers
+ * are accessed..!
+ */
+ if (test_bit(i, ac97->reg_accessed)) {
+ snd_ac97_write(ac97, i, ac97->regs[i]);
+ snd_ac97_read(ac97, i);
+ }
+ }
+}
+
+/*
+ * restore IEC958 status
+ */
+void snd_ac97_restore_iec958(ac97_t *ac97)
+{
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) {
+ /* reset spdif status */
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+ snd_ac97_write(ac97, AC97_EXTENDED_STATUS, ac97->regs[AC97_EXTENDED_STATUS]);
+ if (ac97->flags & AC97_CS_SPDIF)
+ snd_ac97_write(ac97, AC97_CSR_SPDIF, ac97->regs[AC97_CSR_SPDIF]);
+ else
+ snd_ac97_write(ac97, AC97_SPDIF, ac97->regs[AC97_SPDIF]);
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
+ }
+ }
+}
+
+/**
+ * snd_ac97_resume - General resume function for AC97 codec
+ * @ac97: the ac97 instance
+ *
+ * Do the standard resume procedure, power up and restoring the
+ * old register values.
+ */
+void snd_ac97_resume(ac97_t *ac97)
+{
+ int i;
+
+ if (ac97->bus->ops->reset) {
+ ac97->bus->ops->reset(ac97);
+ goto __reset_ready;
+ }
+
+ snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+ if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+ snd_ac97_write(ac97, AC97_RESET, 0);
+ udelay(100);
+ snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+ }
+ snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0);
+
+ snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
+ if (ac97_is_audio(ac97)) {
+ ac97->bus->ops->write(ac97, AC97_MASTER, 0x8101);
+ for (i = HZ/10; i >= 0; i--) {
+ if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101)
+ break;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ /* FIXME: extra delay */
+ ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000);
+ if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ }
+ } else {
+ for (i = HZ/10; i >= 0; i--) {
+ unsigned short val = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+ if (val != 0xffff && (val & 1) != 0)
+ break;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ }
+__reset_ready:
+
+ if (ac97->bus->ops->init)
+ ac97->bus->ops->init(ac97);
+
+ if (ac97->build_ops->resume)
+ ac97->build_ops->resume(ac97);
+ else {
+ snd_ac97_restore_status(ac97);
+ snd_ac97_restore_iec958(ac97);
+ }
+}
+#endif
+
+
+/*
+ * Hardware tuning
+ */
+static void set_ctl_name(char *dst, const char *src, const char *suffix)
+{
+ if (suffix)
+ sprintf(dst, "%s %s", src, suffix);
+ else
+ strcpy(dst, src);
+}
+
+/* remove the control with the given name and optional suffix */
+int snd_ac97_remove_ctl(ac97_t *ac97, const char *name, const char *suffix)
+{
+ snd_ctl_elem_id_t id;
+ memset(&id, 0, sizeof(id));
+ set_ctl_name(id.name, name, suffix);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_remove_id(ac97->bus->card, &id);
+}
+
+static snd_kcontrol_t *ctl_find(ac97_t *ac97, const char *name, const char *suffix)
+{
+ snd_ctl_elem_id_t sid;
+ memset(&sid, 0, sizeof(sid));
+ set_ctl_name(sid.name, name, suffix);
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_find_id(ac97->bus->card, &sid);
+}
+
+/* rename the control with the given name and optional suffix */
+int snd_ac97_rename_ctl(ac97_t *ac97, const char *src, const char *dst, const char *suffix)
+{
+ snd_kcontrol_t *kctl = ctl_find(ac97, src, suffix);
+ if (kctl) {
+ set_ctl_name(kctl->id.name, dst, suffix);
+ return 0;
+ }
+ return -ENOENT;
+}
+
+/* rename both Volume and Switch controls - don't check the return value */
+void snd_ac97_rename_vol_ctl(ac97_t *ac97, const char *src, const char *dst)
+{
+ snd_ac97_rename_ctl(ac97, src, dst, "Switch");
+ snd_ac97_rename_ctl(ac97, src, dst, "Volume");
+}
+
+/* swap controls */
+int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2, const char *suffix)
+{
+ snd_kcontrol_t *kctl1, *kctl2;
+ kctl1 = ctl_find(ac97, s1, suffix);
+ kctl2 = ctl_find(ac97, s2, suffix);
+ if (kctl1 && kctl2) {
+ set_ctl_name(kctl1->id.name, s2, suffix);
+ set_ctl_name(kctl2->id.name, s1, suffix);
+ return 0;
+ }
+ return -ENOENT;
+}
+
+#if 1
+/* bind hp and master controls instead of using only hp control */
+static int bind_hp_volsw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ int err = snd_ac97_put_volsw(kcontrol, ucontrol);
+ if (err > 0) {
+ unsigned long priv_saved = kcontrol->private_value;
+ kcontrol->private_value = (kcontrol->private_value & ~0xff) | AC97_HEADPHONE;
+ snd_ac97_put_volsw(kcontrol, ucontrol);
+ kcontrol->private_value = priv_saved;
+ }
+ return err;
+}
+
+/* ac97 tune: bind Master and Headphone controls */
+static int tune_hp_only(ac97_t *ac97)
+{
+ snd_kcontrol_t *msw = ctl_find(ac97, "Master Playback Switch", NULL);
+ snd_kcontrol_t *mvol = ctl_find(ac97, "Master Playback Volume", NULL);
+ if (! msw || ! mvol)
+ return -ENOENT;
+ msw->put = bind_hp_volsw_put;
+ mvol->put = bind_hp_volsw_put;
+ snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
+ snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
+ return 0;
+}
+
+#else
+/* ac97 tune: use Headphone control as master */
+static int tune_hp_only(ac97_t *ac97)
+{
+ if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL)
+ return -ENOENT;
+ snd_ac97_remove_ctl(ac97, "Master Playback", "Switch");
+ snd_ac97_remove_ctl(ac97, "Master Playback", "Volume");
+ snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+ return 0;
+}
+#endif
+
+/* ac97 tune: swap Headphone and Master controls */
+static int tune_swap_hp(ac97_t *ac97)
+{
+ if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL)
+ return -ENOENT;
+ snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Line-Out Playback");
+ snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+ return 0;
+}
+
+/* ac97 tune: swap Surround and Master controls */
+static int tune_swap_surround(ac97_t *ac97)
+{
+ if (snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Switch") ||
+ snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Volume"))
+ return -ENOENT;
+ return 0;
+}
+
+/* ac97 tune: set up mic sharing for AD codecs */
+static int tune_ad_sharing(ac97_t *ac97)
+{
+ unsigned short scfg;
+ if ((ac97->id & 0xffffff00) != 0x41445300) {
+ snd_printk(KERN_ERR "ac97_quirk AD_SHARING is only for AD codecs\n");
+ return -EINVAL;
+ }
+ /* Turn on OMS bit to route microphone to back panel */
+ scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
+ snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x0200);
+ return 0;
+}
+
+static const snd_kcontrol_new_t snd_ac97_alc_jack_detect =
+AC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0);
+
+/* ac97 tune: set up ALC jack-select */
+static int tune_alc_jack(ac97_t *ac97)
+{
+ if ((ac97->id & 0xffffff00) != 0x414c4700) {
+ snd_printk(KERN_ERR "ac97_quirk ALC_JACK is only for Realtek codecs\n");
+ return -EINVAL;
+ }
+ snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */
+ snd_ac97_update_bits(ac97, 0x7a, 0x01, 0x01); /* Line-out auto mute */
+ return snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_alc_jack_detect, ac97));
+}
+
+/* ac97 tune: inversed EAPD bit */
+static int tune_inv_eapd(ac97_t *ac97)
+{
+ snd_kcontrol_t *kctl = ctl_find(ac97, "External Amplifier", NULL);
+ if (! kctl)
+ return -ENOENT;
+ set_inv_eapd(ac97, kctl);
+ return 0;
+}
+
+static int master_mute_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ int err = snd_ac97_put_volsw(kcontrol, ucontrol);
+ if (err > 0) {
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ unsigned short mask;
+ if (shift != rshift)
+ mask = 0x8080;
+ else
+ mask = 0x8000;
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+ (ac97->regs[AC97_MASTER] & mask) == mask ?
+ 0x8000 : 0);
+ }
+ return err;
+}
+
+/* ac97 tune: EAPD controls mute LED bound with the master mute */
+static int tune_mute_led(ac97_t *ac97)
+{
+ snd_kcontrol_t *msw = ctl_find(ac97, "Master Playback Switch", NULL);
+ if (! msw)
+ return -ENOENT;
+ msw->put = master_mute_sw_put;
+ snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+ return 0;
+}
+
+struct quirk_table {
+ const char *name;
+ int (*func)(ac97_t *);
+};
+
+static struct quirk_table applicable_quirks[] = {
+ { "none", NULL },
+ { "hp_only", tune_hp_only },
+ { "swap_hp", tune_swap_hp },
+ { "swap_surround", tune_swap_surround },
+ { "ad_sharing", tune_ad_sharing },
+ { "alc_jack", tune_alc_jack },
+ { "inv_eapd", tune_inv_eapd },
+ { "mute_led", tune_mute_led },
+};
+
+/* apply the quirk with the given type */
+static int apply_quirk(ac97_t *ac97, int type)
+{
+ if (type <= 0)
+ return 0;
+ else if (type >= ARRAY_SIZE(applicable_quirks))
+ return -EINVAL;
+ if (applicable_quirks[type].func)
+ return applicable_quirks[type].func(ac97);
+ return 0;
+}
+
+/* apply the quirk with the given name */
+static int apply_quirk_str(ac97_t *ac97, const char *typestr)
+{
+ int i;
+ struct quirk_table *q;
+
+ for (i = 0; i < ARRAY_SIZE(applicable_quirks); i++) {
+ q = &applicable_quirks[i];
+ if (q->name && ! strcmp(typestr, q->name))
+ return apply_quirk(ac97, i);
+ }
+ /* for compatibility, accept the numbers, too */
+ if (*typestr >= '0' && *typestr <= '9')
+ return apply_quirk(ac97, (int)simple_strtoul(typestr, NULL, 10));
+ return -EINVAL;
+}
+
+/**
+ * snd_ac97_tune_hardware - tune up the hardware
+ * @ac97: the ac97 instance
+ * @quirk: quirk list
+ * @override: explicit quirk value (overrides the list if non-NULL)
+ *
+ * Do some workaround for each pci device, such as renaming of the
+ * headphone (true line-out) control as "Master".
+ * The quirk-list must be terminated with a zero-filled entry.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+
+int snd_ac97_tune_hardware(ac97_t *ac97, struct ac97_quirk *quirk, const char *override)
+{
+ int result;
+
+ snd_assert(quirk, return -EINVAL);
+
+ /* quirk overriden? */
+ if (override && strcmp(override, "-1") && strcmp(override, "default")) {
+ result = apply_quirk_str(ac97, override);
+ if (result < 0)
+ snd_printk(KERN_ERR "applying quirk type %s failed (%d)\n", override, result);
+ return result;
+ }
+
+ for (; quirk->vendor; quirk++) {
+ if (quirk->vendor != ac97->subsystem_vendor)
+ continue;
+ if ((! quirk->mask && quirk->device == ac97->subsystem_device) ||
+ quirk->device == (quirk->mask & ac97->subsystem_device)) {
+ if (quirk->codec_id && quirk->codec_id != ac97->id)
+ continue;
+ snd_printdd("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, ac97->subsystem_device);
+ result = apply_quirk(ac97, quirk->type);
+ if (result < 0)
+ snd_printk(KERN_ERR "applying quirk type %d for %s failed (%d)\n", quirk->type, quirk->name, result);
+ return result;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Exported symbols
+ */
+
+EXPORT_SYMBOL(snd_ac97_write);
+EXPORT_SYMBOL(snd_ac97_read);
+EXPORT_SYMBOL(snd_ac97_write_cache);
+EXPORT_SYMBOL(snd_ac97_update);
+EXPORT_SYMBOL(snd_ac97_update_bits);
+EXPORT_SYMBOL(snd_ac97_get_short_name);
+EXPORT_SYMBOL(snd_ac97_bus);
+EXPORT_SYMBOL(snd_ac97_mixer);
+EXPORT_SYMBOL(snd_ac97_pcm_assign);
+EXPORT_SYMBOL(snd_ac97_pcm_open);
+EXPORT_SYMBOL(snd_ac97_pcm_close);
+EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
+EXPORT_SYMBOL(snd_ac97_tune_hardware);
+EXPORT_SYMBOL(snd_ac97_set_rate);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_ac97_resume);
+EXPORT_SYMBOL(snd_ac97_suspend);
+#endif
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_ac97_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_ac97_exit(void)
+{
+}
+
+module_init(alsa_ac97_init)
+module_exit(alsa_ac97_exit)
diff --git a/sound/pci/ac97/ac97_id.h b/sound/pci/ac97/ac97_id.h
new file mode 100644
index 00000000000..dadf387ad0b
--- /dev/null
+++ b/sound/pci/ac97/ac97_id.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal interface for Audio Codec '97
+ *
+ * For more details look to AC '97 component specification revision 2.2
+ * by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define AC97_ID_AK4540 0x414b4d00
+#define AC97_ID_AK4542 0x414b4d01
+#define AC97_ID_AD1819 0x41445303
+#define AC97_ID_AD1881 0x41445340
+#define AC97_ID_AD1881A 0x41445348
+#define AC97_ID_AD1885 0x41445360
+#define AC97_ID_AD1886 0x41445361
+#define AC97_ID_AD1887 0x41445362
+#define AC97_ID_AD1886A 0x41445363
+#define AC97_ID_AD1980 0x41445370
+#define AC97_ID_TR28028 0x54524108
+#define AC97_ID_STAC9700 0x83847600
+#define AC97_ID_STAC9704 0x83847604
+#define AC97_ID_STAC9705 0x83847605
+#define AC97_ID_STAC9708 0x83847608
+#define AC97_ID_STAC9721 0x83847609
+#define AC97_ID_STAC9744 0x83847644
+#define AC97_ID_STAC9756 0x83847656
+#define AC97_ID_CS4297A 0x43525910
+#define AC97_ID_CS4299 0x43525930
+#define AC97_ID_CS4201 0x43525948
+#define AC97_ID_CS4205 0x43525958
+#define AC97_ID_CS_MASK 0xfffffff8 /* bit 0-2: rev */
+#define AC97_ID_ALC100 0x414c4300
+#define AC97_ID_ALC650 0x414c4720
+#define AC97_ID_ALC650D 0x414c4721
+#define AC97_ID_ALC650E 0x414c4722
+#define AC97_ID_ALC650F 0x414c4723
+#define AC97_ID_ALC655 0x414c4760
+#define AC97_ID_ALC658 0x414c4780
+#define AC97_ID_ALC850 0x414c4790
+#define AC97_ID_YMF753 0x594d4803
+#define AC97_ID_VT1616 0x49434551
+#define AC97_ID_CM9738 0x434d4941
+#define AC97_ID_CM9739 0x434d4961
+#define AC97_ID_CM9761_78 0x434d4978
+#define AC97_ID_CM9761_82 0x434d4982
+#define AC97_ID_CM9761_83 0x434d4983
diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h
new file mode 100644
index 00000000000..536a4d4793a
--- /dev/null
+++ b/sound/pci/ac97/ac97_local.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal interface for Audio Codec '97
+ *
+ * For more details look to AC '97 component specification revision 2.2
+ * by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define AC97_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) | ((shift) << 12) | ((mask) << 16) | ((invert) << 24))
+#define AC97_PAGE_SINGLE_VALUE(reg,shift,mask,invert,page) (AC97_SINGLE_VALUE(reg,shift,mask,invert) | (1<<25) | ((page) << 26))
+#define AC97_SINGLE(xname, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \
+ .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
+ .private_value = AC97_SINGLE_VALUE(reg, shift, mask, invert) }
+#define AC97_PAGE_SINGLE(xname, reg, shift, mask, invert, page) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \
+ .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
+ .private_value = AC97_PAGE_SINGLE_VALUE(reg, shift, mask, invert, page) }
+#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_volsw, \
+ .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
+ .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
+#define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
+{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+ .mask = xmask, .texts = xtexts }
+#define AC97_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \
+ AC97_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts)
+#define AC97_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \
+ .get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \
+ .private_value = (unsigned long)&xenum }
+
+/* ac97_codec.c */
+extern const char *snd_ac97_stereo_enhancements[];
+extern const snd_kcontrol_new_t snd_ac97_controls_3d[];
+extern const snd_kcontrol_new_t snd_ac97_controls_spdif[];
+snd_kcontrol_t *snd_ac97_cnew(const snd_kcontrol_new_t *_template, ac97_t * ac97);
+void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem);
+int snd_ac97_info_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ac97_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ac97_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit);
+int snd_ac97_remove_ctl(ac97_t *ac97, const char *name, const char *suffix);
+int snd_ac97_rename_ctl(ac97_t *ac97, const char *src, const char *dst, const char *suffix);
+int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2, const char *suffix);
+void snd_ac97_rename_vol_ctl(ac97_t *ac97, const char *src, const char *dst);
+void snd_ac97_restore_status(ac97_t *ac97);
+void snd_ac97_restore_iec958(ac97_t *ac97);
+int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg,
+ unsigned short mask, unsigned short value);
+
+/* ac97_proc.c */
+#ifdef CONFIG_PROC_FS
+void snd_ac97_bus_proc_init(ac97_bus_t * ac97);
+void snd_ac97_bus_proc_done(ac97_bus_t * ac97);
+void snd_ac97_proc_init(ac97_t * ac97);
+void snd_ac97_proc_done(ac97_t * ac97);
+#else
+#define snd_ac97_bus_proc_init(ac97_bus_t) do { } while (0)
+#define snd_ac97_bus_proc_done(ac97_bus_t) do { } while (0)
+#define snd_ac97_proc_init(ac97_t) do { } while (0)
+#define snd_ac97_proc_done(ac97_t) do { } while (0)
+#endif
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
new file mode 100644
index 00000000000..13c34a5d820
--- /dev/null
+++ b/sound/pci/ac97/ac97_patch.c
@@ -0,0 +1,2309 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal interface for Audio Codec '97
+ *
+ * For more details look to AC '97 component specification revision 2.2
+ * by Intel Corporation (http://developer.intel.com) and to datasheets
+ * for specific codecs.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+#include "ac97_patch.h"
+#include "ac97_id.h"
+#include "ac97_local.h"
+
+/*
+ * Chip specific initialization
+ */
+
+static int patch_build_controls(ac97_t * ac97, const snd_kcontrol_new_t *controls, int count)
+{
+ int idx, err;
+
+ for (idx = 0; idx < count; idx++)
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&controls[idx], ac97))) < 0)
+ return err;
+ return 0;
+}
+
+/* set to the page, update bits and restore the page */
+static int ac97_update_bits_page(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page)
+{
+ unsigned short page_save;
+ int ret;
+
+ down(&ac97->page_mutex);
+ page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
+ ret = snd_ac97_update_bits(ac97, reg, mask, value);
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
+ up(&ac97->page_mutex); /* unlock paging */
+ return ret;
+}
+
+/* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */
+
+/* It is possible to indicate to the Yamaha YMF753 the type of speakers being used. */
+static int snd_ac97_ymf753_info_speaker(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[3] = {
+ "Standard", "Small", "Smaller"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_ymf753_get_speaker(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_YMF753_3D_MODE_SEL];
+ val = (val >> 10) & 3;
+ if (val > 0) /* 0 = invalid */
+ val--;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int snd_ac97_ymf753_put_speaker(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ val = (ucontrol->value.enumerated.item[0] + 1) << 10;
+ return snd_ac97_update(ac97, AC97_YMF753_3D_MODE_SEL, val);
+}
+
+static const snd_kcontrol_new_t snd_ac97_ymf753_controls_speaker =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "3D Control - Speaker",
+ .info = snd_ac97_ymf753_info_speaker,
+ .get = snd_ac97_ymf753_get_speaker,
+ .put = snd_ac97_ymf753_put_speaker,
+};
+
+/* It is possible to indicate to the Yamaha YMF753 the source to direct to the S/PDIF output. */
+static int snd_ac97_ymf753_spdif_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[2] = { "AC-Link", "A/D Converter" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_ymf753_spdif_source_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_YMF753_DIT_CTRL2];
+ ucontrol->value.enumerated.item[0] = (val >> 1) & 1;
+ return 0;
+}
+
+static int snd_ac97_ymf753_spdif_source_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+ val = ucontrol->value.enumerated.item[0] << 1;
+ return snd_ac97_update_bits(ac97, AC97_YMF753_DIT_CTRL2, 0x0002, val);
+}
+
+/* The AC'97 spec states that the S/PDIF signal is to be output at pin 48.
+ The YMF753 will output the S/PDIF signal to pin 43, 47 (EAPD), or 48.
+ By default, no output pin is selected, and the S/PDIF signal is not output.
+ There is also a bit to mute S/PDIF output in a vendor-specific register. */
+static int snd_ac97_ymf753_spdif_output_pin_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_ymf753_spdif_output_pin_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_YMF753_DIT_CTRL2];
+ ucontrol->value.enumerated.item[0] = (val & 0x0008) ? 2 : (val & 0x0020) ? 1 : 0;
+ return 0;
+}
+
+static int snd_ac97_ymf753_spdif_output_pin_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ val = (ucontrol->value.enumerated.item[0] == 2) ? 0x0008 :
+ (ucontrol->value.enumerated.item[0] == 1) ? 0x0020 : 0;
+ return snd_ac97_update_bits(ac97, AC97_YMF753_DIT_CTRL2, 0x0028, val);
+ /* The following can be used to direct S/PDIF output to pin 47 (EAPD).
+ snd_ac97_write_cache(ac97, 0x62, snd_ac97_read(ac97, 0x62) | 0x0008); */
+}
+
+static const snd_kcontrol_new_t snd_ac97_ymf753_controls_spdif[3] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+ .info = snd_ac97_ymf753_spdif_source_info,
+ .get = snd_ac97_ymf753_spdif_source_get,
+ .put = snd_ac97_ymf753_spdif_source_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Output Pin",
+ .info = snd_ac97_ymf753_spdif_output_pin_info,
+ .get = snd_ac97_ymf753_spdif_output_pin_get,
+ .put = snd_ac97_ymf753_spdif_output_pin_put,
+ },
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",NONE,NONE) "Mute", AC97_YMF753_DIT_CTRL2, 2, 1, 1)
+};
+
+static int patch_yamaha_ymf753_3d(ac97_t * ac97)
+{
+ snd_kcontrol_t *kctl;
+ int err;
+
+ if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ return err;
+ strcpy(kctl->id.name, "3D Control - Wide");
+ kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0);
+ snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_ymf753_controls_speaker, ac97))) < 0)
+ return err;
+ snd_ac97_write_cache(ac97, AC97_YMF753_3D_MODE_SEL, 0x0c00);
+ return 0;
+}
+
+static int patch_yamaha_ymf753_post_spdif(ac97_t * ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, snd_ac97_ymf753_controls_spdif, ARRAY_SIZE(snd_ac97_ymf753_controls_spdif))) < 0)
+ return err;
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_yamaha_ymf753_ops = {
+ .build_3d = patch_yamaha_ymf753_3d,
+ .build_post_spdif = patch_yamaha_ymf753_post_spdif
+};
+
+int patch_yamaha_ymf753(ac97_t * ac97)
+{
+ /* Patch for Yamaha YMF753, Copyright (c) by David Shust, dshust@shustring.com.
+ This chip has nonstandard and extended behaviour with regard to its S/PDIF output.
+ The AC'97 spec states that the S/PDIF signal is to be output at pin 48.
+ The YMF753 will ouput the S/PDIF signal to pin 43, 47 (EAPD), or 48.
+ By default, no output pin is selected, and the S/PDIF signal is not output.
+ There is also a bit to mute S/PDIF output in a vendor-specific register.
+ */
+ ac97->build_ops = &patch_yamaha_ymf753_ops;
+ ac97->caps |= AC97_BC_BASS_TREBLE;
+ ac97->caps |= 0x04 << 10; /* Yamaha 3D enhancement */
+ return 0;
+}
+
+/*
+ * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * removed broken wolfson00 patch.
+ * added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717.
+ */
+
+int patch_wolfson03(ac97_t * ac97)
+{
+ /* This is known to work for the ViewSonic ViewPad 1000
+ Randolph Bentson <bentson@holmsjoen.com> */
+
+ // WM9703/9707/9708/9717
+ snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
+ snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x8000);
+ return 0;
+}
+
+int patch_wolfson04(ac97_t * ac97)
+{
+ // WM9704M/9704Q
+ // set front and rear mixer volume
+ snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
+ snd_ac97_write_cache(ac97, AC97_WM9704_RMIXER_VOL, 0x0808);
+
+ // patch for DVD noise
+ snd_ac97_write_cache(ac97, AC97_WM9704_TEST, 0x0200);
+
+ // init vol
+ snd_ac97_write_cache(ac97, AC97_WM9704_RPCM_VOL, 0x0808);
+
+ // set rear surround volume
+ snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
+ return 0;
+}
+
+int patch_wolfson05(ac97_t * ac97)
+{
+ // WM9705, WM9710
+ // set front mixer volume
+ snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
+ return 0;
+}
+
+int patch_wolfson11(ac97_t * ac97)
+{
+ // WM9711, WM9712
+ // set out3 volume
+ snd_ac97_write_cache(ac97, AC97_WM9711_OUT3VOL, 0x0808);
+ return 0;
+}
+
+static const char* wm9713_mic_mixer[] = {"Stereo", "Mic1", "Mic2", "Mute"};
+static const char* wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
+static const char* wm9713_rec_src_l[] = {"Mic1", "Mic2", "Line L", "Mono In", "HP Mix L", "Spk Mix", "Mono Mix", "Zh"};
+static const char* wm9713_rec_src_r[] = {"Mic1", "Mic2", "Line R", "Mono In", "HP Mix R", "Spk Mix", "Mono Mix", "Zh"};
+
+static const struct ac97_enum wm9713_enum[] = {
+AC97_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer),
+AC97_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux),
+AC97_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux),
+AC97_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src_l),
+AC97_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src_r),
+};
+
+static const snd_kcontrol_new_t wm13_snd_ac97_controls_line_in[] = {
+AC97_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
+AC97_SINGLE("Line In to Headphone Mute", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("Line In to Speaker Mute", AC97_PC_BEEP, 14, 1, 1),
+AC97_SINGLE("Line In to Mono Mute", AC97_PC_BEEP, 13, 1, 1),
+};
+
+static const snd_kcontrol_new_t wm13_snd_ac97_controls_dac[] = {
+AC97_DOUBLE("DAC Volume", AC97_PHONE, 8, 0, 31, 1),
+AC97_SINGLE("DAC to Headphone Mute", AC97_PHONE, 15, 1, 1),
+AC97_SINGLE("DAC to Speaker Mute", AC97_PHONE, 14, 1, 1),
+AC97_SINGLE("DAC to Mono Mute", AC97_PHONE, 13, 1, 1),
+};
+
+static const snd_kcontrol_new_t wm13_snd_ac97_controls_mic[] = {
+AC97_SINGLE("MICA Volume", AC97_MIC, 8, 31, 1),
+AC97_SINGLE("MICB Volume", AC97_MIC, 0, 31, 1),
+AC97_SINGLE("MICA to Mono Mute", AC97_LINE, 7, 1, 1),
+AC97_SINGLE("MICB to Mono Mute", AC97_LINE, 6, 1, 1),
+AC97_SINGLE("MIC Boost (+20dB)", AC97_LINE, 5, 1, 1),
+AC97_ENUM("MIC Headphone Routing", wm9713_enum[0]),
+AC97_SINGLE("MIC Headphone Mixer Volume", AC97_LINE, 0, 7, 1)
+};
+
+static const snd_kcontrol_new_t wm13_snd_ac97_controls_adc[] = {
+AC97_SINGLE("ADC Mute", AC97_CD, 15, 1, 1),
+AC97_DOUBLE("Gain Step Size (1.5dB/0.75dB)", AC97_CD, 14, 6, 1, 1),
+AC97_DOUBLE("ADC Volume",AC97_CD, 8, 0, 15, 0),
+AC97_SINGLE("ADC Zero Cross", AC97_CD, 7, 1, 1),
+};
+
+static const snd_kcontrol_new_t wm13_snd_ac97_controls_recsel[] = {
+AC97_ENUM("Record to Headphone Path", wm9713_enum[1]),
+AC97_SINGLE("Record to Headphone Volume", AC97_VIDEO, 11, 7, 0),
+AC97_ENUM("Record to Mono Path", wm9713_enum[2]),
+AC97_SINGLE("Record to Mono Boost (+20dB)", AC97_VIDEO, 8, 1, 0),
+AC97_SINGLE("Record ADC Boost (+20dB)", AC97_VIDEO, 6, 1, 0),
+AC97_ENUM("Record Select Left", wm9713_enum[3]),
+AC97_ENUM("Record Select Right", wm9713_enum[4]),
+};
+
+static int patch_wolfson_wm9713_specific(ac97_t * ac97)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_line_in); i++) {
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_line_in[i], ac97))) < 0)
+ return err;
+ }
+ snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808);
+
+ for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_dac); i++) {
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_dac[i], ac97))) < 0)
+ return err;
+ }
+ snd_ac97_write_cache(ac97, AC97_PHONE, 0x0808);
+
+ for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_mic); i++) {
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_mic[i], ac97))) < 0)
+ return err;
+ }
+ snd_ac97_write_cache(ac97, AC97_MIC, 0x0808);
+ snd_ac97_write_cache(ac97, AC97_LINE, 0x00da);
+
+ for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_adc); i++) {
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_adc[i], ac97))) < 0)
+ return err;
+ }
+ snd_ac97_write_cache(ac97, AC97_CD, 0x0808);
+
+ for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_recsel); i++) {
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_recsel[i], ac97))) < 0)
+ return err;
+ }
+ snd_ac97_write_cache(ac97, AC97_VIDEO, 0xd612);
+ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x1ba0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static void patch_wolfson_wm9713_suspend (ac97_t * ac97)
+{
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xfeff);
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xffff);
+}
+
+static void patch_wolfson_wm9713_resume (ac97_t * ac97)
+{
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
+ snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
+}
+#endif
+
+static struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
+ .build_specific = patch_wolfson_wm9713_specific,
+#ifdef CONFIG_PM
+ .suspend = patch_wolfson_wm9713_suspend,
+ .resume = patch_wolfson_wm9713_resume
+#endif
+};
+
+int patch_wolfson13(ac97_t * ac97)
+{
+ ac97->build_ops = &patch_wolfson_wm9713_ops;
+
+ ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_PHONE |
+ AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD;
+
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
+ snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
+
+ return 0;
+}
+
+/*
+ * Tritech codec
+ */
+int patch_tritech_tr28028(ac97_t * ac97)
+{
+ snd_ac97_write_cache(ac97, 0x26, 0x0300);
+ snd_ac97_write_cache(ac97, 0x26, 0x0000);
+ snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
+ snd_ac97_write_cache(ac97, AC97_SPDIF, 0x0000);
+ return 0;
+}
+
+/*
+ * Sigmatel STAC97xx codecs
+ */
+static int patch_sigmatel_stac9700_3d(ac97_t * ac97)
+{
+ snd_kcontrol_t *kctl;
+ int err;
+
+ if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ return err;
+ strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+ kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
+ snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+ return 0;
+}
+
+static int patch_sigmatel_stac9708_3d(ac97_t * ac97)
+{
+ snd_kcontrol_t *kctl;
+ int err;
+
+ if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ return err;
+ strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+ kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0);
+ if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ return err;
+ strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
+ kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
+ snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+ return 0;
+}
+
+static const snd_kcontrol_new_t snd_ac97_sigmatel_4speaker =
+AC97_SINGLE("Sigmatel 4-Speaker Stereo Playback Switch", AC97_SIGMATEL_DAC2INVERT, 2, 1, 0);
+
+static const snd_kcontrol_new_t snd_ac97_sigmatel_phaseinvert =
+AC97_SINGLE("Sigmatel Surround Phase Inversion Playback Switch", AC97_SIGMATEL_DAC2INVERT, 3, 1, 0);
+
+static const snd_kcontrol_new_t snd_ac97_sigmatel_controls[] = {
+AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0),
+AC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0)
+};
+
+static int patch_sigmatel_stac97xx_specific(ac97_t * ac97)
+{
+ int err;
+
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003);
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1))
+ if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[0], 1)) < 0)
+ return err;
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0))
+ if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[1], 1)) < 0)
+ return err;
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 2))
+ if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_4speaker, 1)) < 0)
+ return err;
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 3))
+ if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_phaseinvert, 1)) < 0)
+ return err;
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_sigmatel_stac9700_ops = {
+ .build_3d = patch_sigmatel_stac9700_3d,
+ .build_specific = patch_sigmatel_stac97xx_specific
+};
+
+int patch_sigmatel_stac9700(ac97_t * ac97)
+{
+ ac97->build_ops = &patch_sigmatel_stac9700_ops;
+ return 0;
+}
+
+static int snd_ac97_stac9708_put_bias(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int err;
+
+ down(&ac97->page_mutex);
+ snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+ err = snd_ac97_update_bits(ac97, AC97_SIGMATEL_BIAS2, 0x0010,
+ (ucontrol->value.integer.value[0] & 1) << 4);
+ snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0);
+ up(&ac97->page_mutex);
+ return err;
+}
+
+static const snd_kcontrol_new_t snd_ac97_stac9708_bias_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sigmatel Output Bias Switch",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_get_volsw,
+ .put = snd_ac97_stac9708_put_bias,
+ .private_value = AC97_SINGLE_VALUE(AC97_SIGMATEL_BIAS2, 4, 1, 0),
+};
+
+static int patch_sigmatel_stac9708_specific(ac97_t *ac97)
+{
+ int err;
+
+ snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback");
+ if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0)
+ return err;
+ return patch_sigmatel_stac97xx_specific(ac97);
+}
+
+static struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = {
+ .build_3d = patch_sigmatel_stac9708_3d,
+ .build_specific = patch_sigmatel_stac9708_specific
+};
+
+int patch_sigmatel_stac9708(ac97_t * ac97)
+{
+ unsigned int codec72, codec6c;
+
+ ac97->build_ops = &patch_sigmatel_stac9708_ops;
+ ac97->caps |= 0x10; /* HP (sigmatel surround) support */
+
+ codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000;
+ codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG);
+
+ if ((codec72==0) && (codec6c==0)) {
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1000);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0007);
+ } else if ((codec72==0x8000) && (codec6c==0)) {
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1001);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_DAC2INVERT, 0x0008);
+ } else if ((codec72==0x8000) && (codec6c==0x0080)) {
+ /* nothing */
+ }
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+ return 0;
+}
+
+int patch_sigmatel_stac9721(ac97_t * ac97)
+{
+ ac97->build_ops = &patch_sigmatel_stac9700_ops;
+ if (snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) == 0) {
+ // patch for SigmaTel
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x4000);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+ }
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+ return 0;
+}
+
+int patch_sigmatel_stac9744(ac97_t * ac97)
+{
+ // patch for SigmaTel
+ ac97->build_ops = &patch_sigmatel_stac9700_ops;
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+ return 0;
+}
+
+int patch_sigmatel_stac9756(ac97_t * ac97)
+{
+ // patch for SigmaTel
+ ac97->build_ops = &patch_sigmatel_stac9700_ops;
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+ snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+ return 0;
+}
+
+static int snd_ac97_stac9758_output_jack_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[5] = { "Input/Disabled", "Front Output",
+ "Rear Output", "Center/LFE Output", "Mixer Output" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 5;
+ if (uinfo->value.enumerated.item > 4)
+ uinfo->value.enumerated.item = 4;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_stac9758_output_jack_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value;
+ unsigned short val;
+
+ val = ac97->regs[AC97_SIGMATEL_OUTSEL] >> shift;
+ if (!(val & 4))
+ ucontrol->value.enumerated.item[0] = 0;
+ else
+ ucontrol->value.enumerated.item[0] = 1 + (val & 3);
+ return 0;
+}
+
+static int snd_ac97_stac9758_output_jack_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value;
+ unsigned short val;
+
+ if (ucontrol->value.enumerated.item[0] > 4)
+ return -EINVAL;
+ if (ucontrol->value.enumerated.item[0] == 0)
+ val = 0;
+ else
+ val = 4 | (ucontrol->value.enumerated.item[0] - 1);
+ return ac97_update_bits_page(ac97, AC97_SIGMATEL_OUTSEL,
+ 7 << shift, val << shift, 0);
+}
+
+static int snd_ac97_stac9758_input_jack_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack",
+ "Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 7;
+ if (uinfo->value.enumerated.item > 6)
+ uinfo->value.enumerated.item = 6;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_stac9758_input_jack_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value;
+ unsigned short val;
+
+ val = ac97->regs[AC97_SIGMATEL_INSEL];
+ ucontrol->value.enumerated.item[0] = (val >> shift) & 7;
+ return 0;
+}
+
+static int snd_ac97_stac9758_input_jack_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value;
+
+ return ac97_update_bits_page(ac97, AC97_SIGMATEL_INSEL, 7 << shift,
+ ucontrol->value.enumerated.item[0] << shift, 0);
+}
+
+static int snd_ac97_stac9758_phonesel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[3] = { "None", "Front Jack", "Rear Jack" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_stac9758_phonesel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = ac97->regs[AC97_SIGMATEL_IOMISC] & 3;
+ return 0;
+}
+
+static int snd_ac97_stac9758_phonesel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ return ac97_update_bits_page(ac97, AC97_SIGMATEL_IOMISC, 3,
+ ucontrol->value.enumerated.item[0], 0);
+}
+
+#define STAC9758_OUTPUT_JACK(xname, shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_ac97_stac9758_output_jack_info, \
+ .get = snd_ac97_stac9758_output_jack_get, \
+ .put = snd_ac97_stac9758_output_jack_put, \
+ .private_value = shift }
+#define STAC9758_INPUT_JACK(xname, shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_ac97_stac9758_input_jack_info, \
+ .get = snd_ac97_stac9758_input_jack_get, \
+ .put = snd_ac97_stac9758_input_jack_put, \
+ .private_value = shift }
+static const snd_kcontrol_new_t snd_ac97_sigmatel_stac9758_controls[] = {
+ STAC9758_OUTPUT_JACK("Mic1 Jack", 1),
+ STAC9758_OUTPUT_JACK("LineIn Jack", 4),
+ STAC9758_OUTPUT_JACK("Front Jack", 7),
+ STAC9758_OUTPUT_JACK("Rear Jack", 10),
+ STAC9758_OUTPUT_JACK("Center/LFE Jack", 13),
+ STAC9758_INPUT_JACK("Mic Input Source", 0),
+ STAC9758_INPUT_JACK("Line Input Source", 8),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Amp",
+ .info = snd_ac97_stac9758_phonesel_info,
+ .get = snd_ac97_stac9758_phonesel_get,
+ .put = snd_ac97_stac9758_phonesel_put
+ },
+ AC97_SINGLE("Exchange Center/LFE", AC97_SIGMATEL_IOMISC, 4, 1, 0),
+ AC97_SINGLE("Headphone +3dB Boost", AC97_SIGMATEL_IOMISC, 8, 1, 0)
+};
+
+static int patch_sigmatel_stac9758_specific(ac97_t *ac97)
+{
+ int err;
+
+ err = patch_sigmatel_stac97xx_specific(ac97);
+ if (err < 0)
+ return err;
+ err = patch_build_controls(ac97, snd_ac97_sigmatel_stac9758_controls,
+ ARRAY_SIZE(snd_ac97_sigmatel_stac9758_controls));
+ if (err < 0)
+ return err;
+ /* DAC-A direct */
+ snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Front Playback");
+ /* DAC-A to Mix = PCM */
+ /* DAC-B direct = Surround */
+ /* DAC-B to Mix */
+ snd_ac97_rename_vol_ctl(ac97, "Video Playback", "Surround Mix Playback");
+ /* DAC-C direct = Center/LFE */
+
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
+ .build_3d = patch_sigmatel_stac9700_3d,
+ .build_specific = patch_sigmatel_stac9758_specific
+};
+
+int patch_sigmatel_stac9758(ac97_t * ac97)
+{
+ static unsigned short regs[4] = {
+ AC97_SIGMATEL_OUTSEL,
+ AC97_SIGMATEL_IOMISC,
+ AC97_SIGMATEL_INSEL,
+ AC97_SIGMATEL_VARIOUS
+ };
+ static unsigned short def_regs[4] = {
+ /* OUTSEL */ 0xd794, /* CL:CL, SR:SR, LO:MX, LI:DS, MI:DS */
+ /* IOMISC */ 0x2001,
+ /* INSEL */ 0x0201, /* LI:LI, MI:M1 */
+ /* VARIOUS */ 0x0040
+ };
+ static unsigned short m675_regs[4] = {
+ /* OUTSEL */ 0xfc70, /* CL:MX, SR:MX, LO:DS, LI:MX, MI:DS */
+ /* IOMISC */ 0x2102, /* HP amp on */
+ /* INSEL */ 0x0203, /* LI:LI, MI:FR */
+ /* VARIOUS */ 0x0041 /* stereo mic */
+ };
+ unsigned short *pregs = def_regs;
+ int i;
+
+ /* Gateway M675 notebook */
+ if (ac97->pci &&
+ ac97->subsystem_vendor == 0x107b &&
+ ac97->subsystem_device == 0x0601)
+ pregs = m675_regs;
+
+ // patch for SigmaTel
+ ac97->build_ops = &patch_sigmatel_stac9758_ops;
+ /* FIXME: assume only page 0 for writing cache */
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
+ for (i = 0; i < 4; i++)
+ snd_ac97_write_cache(ac97, regs[i], pregs[i]);
+
+ ac97->flags |= AC97_STEREO_MUTES;
+ return 0;
+}
+
+/*
+ * Cirrus Logic CS42xx codecs
+ */
+static const snd_kcontrol_new_t snd_ac97_cirrus_controls_spdif[2] = {
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CSR_SPDIF, 15, 1, 0),
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", AC97_CSR_ACMODE, 0, 3, 0)
+};
+
+static int patch_cirrus_build_spdif(ac97_t * ac97)
+{
+ int err;
+
+ /* con mask, pro mask, default */
+ if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+ return err;
+ /* switch, spsa */
+ if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1)) < 0)
+ return err;
+ switch (ac97->id & AC97_ID_CS_MASK) {
+ case AC97_ID_CS4205:
+ if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1)) < 0)
+ return err;
+ break;
+ }
+ /* set default PCM S/PDIF params */
+ /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
+ snd_ac97_write_cache(ac97, AC97_CSR_SPDIF, 0x0a20);
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_cirrus_ops = {
+ .build_spdif = patch_cirrus_build_spdif
+};
+
+int patch_cirrus_spdif(ac97_t * ac97)
+{
+ /* Basically, the cs4201/cs4205/cs4297a has non-standard sp/dif registers.
+ WHY CAN'T ANYONE FOLLOW THE BLOODY SPEC? *sigh*
+ - sp/dif EA ID is not set, but sp/dif is always present.
+ - enable/disable is spdif register bit 15.
+ - sp/dif control register is 0x68. differs from AC97:
+ - valid is bit 14 (vs 15)
+ - no DRS
+ - only 44.1/48k [00 = 48, 01=44,1] (AC97 is 00=44.1, 10=48)
+ - sp/dif ssource select is in 0x5e bits 0,1.
+ */
+
+ ac97->build_ops = &patch_cirrus_ops;
+ ac97->flags |= AC97_CS_SPDIF;
+ ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000;
+ ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */
+ snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080);
+ return 0;
+}
+
+int patch_cirrus_cs4299(ac97_t * ac97)
+{
+ /* force the detection of PC Beep */
+ ac97->flags |= AC97_HAS_PC_BEEP;
+
+ return patch_cirrus_spdif(ac97);
+}
+
+/*
+ * Conexant codecs
+ */
+static const snd_kcontrol_new_t snd_ac97_conexant_controls_spdif[1] = {
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CXR_AUDIO_MISC, 3, 1, 0),
+};
+
+static int patch_conexant_build_spdif(ac97_t * ac97)
+{
+ int err;
+
+ /* con mask, pro mask, default */
+ if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+ return err;
+ /* switch */
+ if ((err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1)) < 0)
+ return err;
+ /* set default PCM S/PDIF params */
+ /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
+ snd_ac97_write_cache(ac97, AC97_CXR_AUDIO_MISC,
+ snd_ac97_read(ac97, AC97_CXR_AUDIO_MISC) & ~(AC97_CXR_SPDIFEN|AC97_CXR_COPYRGT|AC97_CXR_SPDIF_MASK));
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_conexant_ops = {
+ .build_spdif = patch_conexant_build_spdif
+};
+
+int patch_conexant(ac97_t * ac97)
+{
+ ac97->build_ops = &patch_conexant_ops;
+ ac97->flags |= AC97_CX_SPDIF;
+ ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */
+ ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+ return 0;
+}
+
+/*
+ * Analog Device AD18xx, AD19xx codecs
+ */
+#ifdef CONFIG_PM
+static void ad18xx_resume(ac97_t *ac97)
+{
+ static unsigned short setup_regs[] = {
+ AC97_AD_MISC, AC97_AD_SERIAL_CFG, AC97_AD_JACK_SPDIF,
+ };
+ int i, codec;
+
+ for (i = 0; i < (int)ARRAY_SIZE(setup_regs); i++) {
+ unsigned short reg = setup_regs[i];
+ if (test_bit(reg, ac97->reg_accessed)) {
+ snd_ac97_write(ac97, reg, ac97->regs[reg]);
+ snd_ac97_read(ac97, reg);
+ }
+ }
+
+ if (! (ac97->flags & AC97_AD_MULTI))
+ /* normal restore */
+ snd_ac97_restore_status(ac97);
+ else {
+ /* restore the AD18xx codec configurations */
+ for (codec = 0; codec < 3; codec++) {
+ if (! ac97->spec.ad18xx.id[codec])
+ continue;
+ /* select single codec */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+ ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
+ ac97->bus->ops->write(ac97, AC97_AD_CODEC_CFG, ac97->spec.ad18xx.codec_cfg[codec]);
+ }
+ /* select all codecs */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+
+ /* restore status */
+ for (i = 2; i < 0x7c ; i += 2) {
+ if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID)
+ continue;
+ if (test_bit(i, ac97->reg_accessed)) {
+ /* handle multi codecs for AD18xx */
+ if (i == AC97_PCM) {
+ for (codec = 0; codec < 3; codec++) {
+ if (! ac97->spec.ad18xx.id[codec])
+ continue;
+ /* select single codec */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+ ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
+ /* update PCM bits */
+ ac97->bus->ops->write(ac97, AC97_PCM, ac97->spec.ad18xx.pcmreg[codec]);
+ }
+ /* select all codecs */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+ continue;
+ } else if (i == AC97_AD_TEST ||
+ i == AC97_AD_CODEC_CFG ||
+ i == AC97_AD_SERIAL_CFG)
+ continue; /* ignore */
+ }
+ snd_ac97_write(ac97, i, ac97->regs[i]);
+ snd_ac97_read(ac97, i);
+ }
+ }
+
+ snd_ac97_restore_iec958(ac97);
+}
+#endif
+
+int patch_ad1819(ac97_t * ac97)
+{
+ unsigned short scfg;
+
+ // patch for Analog Devices
+ scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
+ snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */
+ return 0;
+}
+
+static unsigned short patch_ad1881_unchained(ac97_t * ac97, int idx, unsigned short mask)
+{
+ unsigned short val;
+
+ // test for unchained codec
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, mask);
+ snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); /* ID0C, ID1C, SDIE = off */
+ val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
+ if ((val & 0xff40) != 0x5340)
+ return 0;
+ ac97->spec.ad18xx.unchained[idx] = mask;
+ ac97->spec.ad18xx.id[idx] = val;
+ ac97->spec.ad18xx.codec_cfg[idx] = 0x0000;
+ return mask;
+}
+
+static int patch_ad1881_chained1(ac97_t * ac97, int idx, unsigned short codec_bits)
+{
+ static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
+ unsigned short val;
+
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, cfg_bits[idx]);
+ snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004); // SDIE
+ val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
+ if ((val & 0xff40) != 0x5340)
+ return 0;
+ if (codec_bits)
+ snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits);
+ ac97->spec.ad18xx.chained[idx] = cfg_bits[idx];
+ ac97->spec.ad18xx.id[idx] = val;
+ ac97->spec.ad18xx.codec_cfg[idx] = codec_bits ? codec_bits : 0x0004;
+ return 1;
+}
+
+static void patch_ad1881_chained(ac97_t * ac97, int unchained_idx, int cidx1, int cidx2)
+{
+ // already detected?
+ if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1])
+ cidx1 = -1;
+ if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2])
+ cidx2 = -1;
+ if (cidx1 < 0 && cidx2 < 0)
+ return;
+ // test for chained codecs
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+ ac97->spec.ad18xx.unchained[unchained_idx]);
+ snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002); // ID1C
+ ac97->spec.ad18xx.codec_cfg[unchained_idx] = 0x0002;
+ if (cidx1 >= 0) {
+ if (patch_ad1881_chained1(ac97, cidx1, 0x0006)) // SDIE | ID1C
+ patch_ad1881_chained1(ac97, cidx2, 0);
+ else if (patch_ad1881_chained1(ac97, cidx2, 0x0006)) // SDIE | ID1C
+ patch_ad1881_chained1(ac97, cidx1, 0);
+ } else if (cidx2 >= 0) {
+ patch_ad1881_chained1(ac97, cidx2, 0);
+ }
+}
+
+static struct snd_ac97_build_ops patch_ad1881_build_ops = {
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1881(ac97_t * ac97)
+{
+ static const char cfg_idxs[3][2] = {
+ {2, 1},
+ {0, 2},
+ {0, 1}
+ };
+
+ // patch for Analog Devices
+ unsigned short codecs[3];
+ unsigned short val;
+ int idx, num;
+
+ val = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
+ snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, val);
+ codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12));
+ codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14));
+ codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13));
+
+ snd_runtime_check(codecs[0] | codecs[1] | codecs[2], goto __end);
+
+ for (idx = 0; idx < 3; idx++)
+ if (ac97->spec.ad18xx.unchained[idx])
+ patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]);
+
+ if (ac97->spec.ad18xx.id[1]) {
+ ac97->flags |= AC97_AD_MULTI;
+ ac97->scaps |= AC97_SCAP_SURROUND_DAC;
+ }
+ if (ac97->spec.ad18xx.id[2]) {
+ ac97->flags |= AC97_AD_MULTI;
+ ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
+ }
+
+ __end:
+ /* select all codecs */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+ /* check if only one codec is present */
+ for (idx = num = 0; idx < 3; idx++)
+ if (ac97->spec.ad18xx.id[idx])
+ num++;
+ if (num == 1) {
+ /* ok, deselect all ID bits */
+ snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000);
+ ac97->spec.ad18xx.codec_cfg[0] =
+ ac97->spec.ad18xx.codec_cfg[1] =
+ ac97->spec.ad18xx.codec_cfg[2] = 0x0000;
+ }
+ /* required for AD1886/AD1885 combination */
+ ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+ if (ac97->spec.ad18xx.id[0]) {
+ ac97->id &= 0xffff0000;
+ ac97->id |= ac97->spec.ad18xx.id[0];
+ }
+ ac97->build_ops = &patch_ad1881_build_ops;
+ return 0;
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad1885[] = {
+ AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0),
+ /* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */
+ AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0),
+ AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0),
+ AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 9, 1, 1), /* inverted */
+ AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
+};
+
+static int patch_ad1885_specific(ac97_t * ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
+ return err;
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_ad1885_build_ops = {
+ .build_specific = &patch_ad1885_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1885(ac97_t * ac97)
+{
+ patch_ad1881(ac97);
+ /* This is required to deal with the Intel D815EEAL2 */
+ /* i.e. Line out is actually headphone out from codec */
+
+ /* set default */
+ snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404);
+
+ ac97->build_ops = &patch_ad1885_build_ops;
+ return 0;
+}
+
+int patch_ad1886(ac97_t * ac97)
+{
+ patch_ad1881(ac97);
+ /* Presario700 workaround */
+ /* for Jack Sense/SPDIF Register misetting causing */
+ snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
+ return 0;
+}
+
+/* MISC bits */
+#define AC97_AD198X_MBC 0x0003 /* mic boost */
+#define AC97_AD198X_MBC_20 0x0000 /* +20dB */
+#define AC97_AD198X_MBC_10 0x0001 /* +10dB */
+#define AC97_AD198X_MBC_30 0x0002 /* +30dB */
+#define AC97_AD198X_VREFD 0x0004 /* VREF high-Z */
+#define AC97_AD198X_VREFH 0x0008 /* 2.25V, 3.7V */
+#define AC97_AD198X_VREF_0 0x000c /* 0V */
+#define AC97_AD198X_SRU 0x0010 /* sample rate unlock */
+#define AC97_AD198X_LOSEL 0x0020 /* LINE_OUT amplifiers input select */
+#define AC97_AD198X_2MIC 0x0040 /* 2-channel mic select */
+#define AC97_AD198X_SPRD 0x0080 /* SPREAD enable */
+#define AC97_AD198X_DMIX0 0x0100 /* downmix mode: 0 = 6-to-4, 1 = 6-to-2 downmix */
+#define AC97_AD198X_DMIX1 0x0200 /* downmix mode: 1 = enabled */
+#define AC97_AD198X_HPSEL 0x0400 /* headphone amplifier input select */
+#define AC97_AD198X_CLDIS 0x0800 /* center/lfe disable */
+#define AC97_AD198X_LODIS 0x1000 /* LINE_OUT disable */
+#define AC97_AD198X_MSPLT 0x2000 /* mute split */
+#define AC97_AD198X_AC97NC 0x4000 /* AC97 no compatible mode */
+#define AC97_AD198X_DACZ 0x8000 /* DAC zero-fill mode */
+
+
+static int snd_ac97_ad198x_spdif_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[2] = { "AC-Link", "A/D Converter" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_ad198x_spdif_source_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_AD_SERIAL_CFG];
+ ucontrol->value.enumerated.item[0] = (val >> 2) & 1;
+ return 0;
+}
+
+static int snd_ac97_ad198x_spdif_source_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+ val = ucontrol->value.enumerated.item[0] << 2;
+ return snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x0004, val);
+}
+
+static const snd_kcontrol_new_t snd_ac97_ad198x_spdif_source = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+ .info = snd_ac97_ad198x_spdif_source_info,
+ .get = snd_ac97_ad198x_spdif_source_get,
+ .put = snd_ac97_ad198x_spdif_source_put,
+};
+
+static int patch_ad198x_post_spdif(ac97_t * ac97)
+{
+ return patch_build_controls(ac97, &snd_ac97_ad198x_spdif_source, 1);
+}
+
+static const snd_kcontrol_new_t snd_ac97_ad1981x_jack_sense[] = {
+ AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 11, 1, 0),
+ AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
+};
+
+static int patch_ad1981a_specific(ac97_t * ac97)
+{
+ return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
+ ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
+}
+
+static struct snd_ac97_build_ops patch_ad1981a_build_ops = {
+ .build_post_spdif = patch_ad198x_post_spdif,
+ .build_specific = patch_ad1981a_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
+static void check_ad1981_hp_jack_sense(ac97_t *ac97)
+{
+ u32 subid = ((u32)ac97->subsystem_vendor << 16) | ac97->subsystem_device;
+ switch (subid) {
+ case 0x103c0890: /* HP nc6000 */
+ case 0x103c006d: /* HP nx9105 */
+ case 0x17340088: /* FSC Scenic-W */
+ /* enable headphone jack sense */
+ snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11);
+ break;
+ }
+}
+
+int patch_ad1981a(ac97_t *ac97)
+{
+ patch_ad1881(ac97);
+ ac97->build_ops = &patch_ad1981a_build_ops;
+ snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT);
+ ac97->flags |= AC97_STEREO_MUTES;
+ check_ad1981_hp_jack_sense(ac97);
+ return 0;
+}
+
+static const snd_kcontrol_new_t snd_ac97_ad198x_2cmic =
+AC97_SINGLE("Stereo Mic", AC97_AD_MISC, 6, 1, 0);
+
+static int patch_ad1981b_specific(ac97_t *ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ return err;
+ return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
+ ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
+}
+
+static struct snd_ac97_build_ops patch_ad1981b_build_ops = {
+ .build_post_spdif = patch_ad198x_post_spdif,
+ .build_specific = patch_ad1981b_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1981b(ac97_t *ac97)
+{
+ patch_ad1881(ac97);
+ ac97->build_ops = &patch_ad1981b_build_ops;
+ snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT);
+ ac97->flags |= AC97_STEREO_MUTES;
+ check_ad1981_hp_jack_sense(ac97);
+ return 0;
+}
+
+static int snd_ac97_ad1888_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ac97_ad1888_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_AD_MISC];
+ ucontrol->value.integer.value[0] = !(val & AC97_AD198X_LOSEL);
+ return 0;
+}
+
+static int snd_ac97_ad1888_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = !ucontrol->value.integer.value[0]
+ ? (AC97_AD198X_LOSEL | AC97_AD198X_HPSEL) : 0;
+ return snd_ac97_update_bits(ac97, AC97_AD_MISC,
+ AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val);
+}
+
+static int snd_ac97_ad1888_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_ad1888_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_AD_MISC];
+ if (!(val & AC97_AD198X_DMIX1))
+ ucontrol->value.enumerated.item[0] = 0;
+ else
+ ucontrol->value.enumerated.item[0] = 1 + ((val >> 8) & 1);
+ return 0;
+}
+
+static int snd_ac97_ad1888_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ if (ucontrol->value.enumerated.item[0] == 0)
+ val = 0;
+ else
+ val = AC97_AD198X_DMIX1 |
+ ((ucontrol->value.enumerated.item[0] - 1) << 8);
+ return snd_ac97_update_bits(ac97, AC97_AD_MISC,
+ AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val);
+}
+
+static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Exchange Front/Surround",
+ .info = snd_ac97_ad1888_lohpsel_info,
+ .get = snd_ac97_ad1888_lohpsel_get,
+ .put = snd_ac97_ad1888_lohpsel_put
+ },
+ AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Downmix",
+ .info = snd_ac97_ad1888_downmix_info,
+ .get = snd_ac97_ad1888_downmix_get,
+ .put = snd_ac97_ad1888_downmix_put
+ },
+ AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0),
+ AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0),
+};
+
+static int patch_ad1888_specific(ac97_t *ac97)
+{
+ /* rename 0x04 as "Master" and 0x02 as "Master Surround" */
+ snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Master Surround Playback");
+ snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+ return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls));
+}
+
+static struct snd_ac97_build_ops patch_ad1888_build_ops = {
+ .build_post_spdif = patch_ad198x_post_spdif,
+ .build_specific = patch_ad1888_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1888(ac97_t * ac97)
+{
+ unsigned short misc;
+
+ patch_ad1881(ac97);
+ ac97->build_ops = &patch_ad1888_build_ops;
+ /* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */
+ /* it seems that most vendors connect line-out connector to headphone out of AC'97 */
+ /* AD-compatible mode */
+ /* Stereo mutes enabled */
+ misc = snd_ac97_read(ac97, AC97_AD_MISC);
+ snd_ac97_write_cache(ac97, AC97_AD_MISC, misc |
+ AC97_AD198X_LOSEL |
+ AC97_AD198X_HPSEL |
+ AC97_AD198X_MSPLT |
+ AC97_AD198X_AC97NC);
+ ac97->flags |= AC97_STEREO_MUTES;
+ return 0;
+}
+
+static int patch_ad1980_specific(ac97_t *ac97)
+{
+ int err;
+
+ if ((err = patch_ad1888_specific(ac97)) < 0)
+ return err;
+ return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+}
+
+static struct snd_ac97_build_ops patch_ad1980_build_ops = {
+ .build_post_spdif = patch_ad198x_post_spdif,
+ .build_specific = patch_ad1980_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1980(ac97_t * ac97)
+{
+ patch_ad1888(ac97);
+ ac97->build_ops = &patch_ad1980_build_ops;
+ return 0;
+}
+
+static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = {
+ AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0),
+ AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0)
+};
+
+static int patch_ad1985_specific(ac97_t *ac97)
+{
+ int err;
+
+ if ((err = patch_ad1980_specific(ac97)) < 0)
+ return err;
+ return patch_build_controls(ac97, snd_ac97_ad1985_controls, ARRAY_SIZE(snd_ac97_ad1985_controls));
+}
+
+static struct snd_ac97_build_ops patch_ad1985_build_ops = {
+ .build_post_spdif = patch_ad198x_post_spdif,
+ .build_specific = patch_ad1985_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1985(ac97_t * ac97)
+{
+ unsigned short misc;
+
+ patch_ad1881(ac97);
+ ac97->build_ops = &patch_ad1985_build_ops;
+ misc = snd_ac97_read(ac97, AC97_AD_MISC);
+ /* switch front/surround line-out/hp-out */
+ /* center/LFE, mic in 3.75V mode */
+ /* AD-compatible mode */
+ /* Stereo mutes enabled */
+ /* in accordance with ADI driver: misc | 0x5c28 */
+ snd_ac97_write_cache(ac97, AC97_AD_MISC, misc |
+ AC97_AD198X_VREFH |
+ AC97_AD198X_LOSEL |
+ AC97_AD198X_HPSEL |
+ AC97_AD198X_CLDIS |
+ AC97_AD198X_LODIS |
+ AC97_AD198X_MSPLT |
+ AC97_AD198X_AC97NC);
+ ac97->flags |= AC97_STEREO_MUTES;
+ /* on AD1985 rev. 3, AC'97 revision bits are zero */
+ ac97->ext_id = (ac97->ext_id & ~AC97_EI_REV_MASK) | AC97_EI_REV_23;
+ return 0;
+}
+
+/*
+ * realtek ALC65x/850 codecs
+ */
+static int snd_ac97_alc650_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
+ return 0;
+}
+
+static int snd_ac97_alc650_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ int change, val;
+ val = !!(snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10));
+ change = (ucontrol->value.integer.value[0] != val);
+ if (change) {
+ /* disable/enable vref */
+ snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
+ ucontrol->value.integer.value[0] ? (1 << 12) : 0);
+ /* turn on/off center-on-mic */
+ snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
+ ucontrol->value.integer.value[0] ? (1 << 10) : 0);
+ /* GPIO0 high for mic */
+ snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100,
+ ucontrol->value.integer.value[0] ? 0 : 0x100);
+ }
+ return change;
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = {
+ AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
+ AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0),
+ AC97_SINGLE("Center/LFE Down Mix", AC97_ALC650_MULTICH, 2, 1, 0),
+ AC97_SINGLE("Exchange Center/LFE", AC97_ALC650_MULTICH, 3, 1, 0),
+ /* 4: Analog Input To Surround */
+ /* 5: Analog Input To Center/LFE */
+ /* 6: Independent Master Volume Right */
+ /* 7: Independent Master Volume Left */
+ /* 8: reserved */
+ AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0),
+ /* 10: mic, see below */
+ /* 11-13: in IEC958 controls */
+ AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
+#if 0 /* always set in patch_alc650 */
+ AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
+ AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
+ AC97_SINGLE("Surround DAC Switch", AC97_ALC650_SURR_DAC_VOL, 15, 1, 1),
+ AC97_DOUBLE("Surround DAC Volume", AC97_ALC650_SURR_DAC_VOL, 8, 0, 31, 1),
+ AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1),
+ AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1),
+#endif
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic As Center/LFE",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_alc650_mic_get,
+ .put = snd_ac97_alc650_mic_put,
+ .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+ },
+};
+
+static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = {
+ AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0),
+ AC97_SINGLE("Analog to IEC958 Output", AC97_ALC650_MULTICH, 12, 1, 0),
+ /* disable this controls since it doesn't work as expected */
+ /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
+};
+
+static int patch_alc650_specific(ac97_t * ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650))) < 0)
+ return err;
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
+ return err;
+ }
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_alc650_ops = {
+ .build_specific = patch_alc650_specific
+};
+
+int patch_alc650(ac97_t * ac97)
+{
+ unsigned short val;
+
+ ac97->build_ops = &patch_alc650_ops;
+
+ /* determine the revision */
+ val = snd_ac97_read(ac97, AC97_ALC650_REVISION) & 0x3f;
+ if (val < 3)
+ ac97->id = 0x414c4720; /* Old version */
+ else if (val < 0x10)
+ ac97->id = 0x414c4721; /* D version */
+ else if (val < 0x20)
+ ac97->id = 0x414c4722; /* E version */
+ else if (val < 0x30)
+ ac97->id = 0x414c4723; /* F version */
+
+ /* revision E or F */
+ /* FIXME: what about revision D ? */
+ ac97->spec.dev_flags = (ac97->id == 0x414c4722 ||
+ ac97->id == 0x414c4723);
+
+ /* enable AC97_ALC650_GPIO_SETUP, AC97_ALC650_CLOCK for R/W */
+ snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS,
+ snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x8000);
+
+ /* Enable SPDIF-IN only on Rev.E and above */
+ val = snd_ac97_read(ac97, AC97_ALC650_CLOCK);
+ /* SPDIF IN with pin 47 */
+ if (ac97->spec.dev_flags)
+ val |= 0x03; /* enable */
+ else
+ val &= ~0x03; /* disable */
+ snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, val);
+
+ /* set default: slot 3,4,7,8,6,9
+ spdif-in monitor off, analog-spdif off, spdif-in off
+ center on mic off, surround on line-in off
+ downmix off, duplicate front off
+ */
+ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 0);
+
+ /* set GPIO0 for mic bias */
+ /* GPIO0 pin output, no interrupt, high */
+ snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP,
+ snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP) | 0x01);
+ snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS,
+ (snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x100) & ~0x10);
+
+ /* full DAC volume */
+ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
+ snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
+ return 0;
+}
+
+static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
+ return 0;
+}
+
+static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ /* misc control; vrefout disable */
+ snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
+ ucontrol->value.integer.value[0] ? (1 << 12) : 0);
+ return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10,
+ ucontrol->value.integer.value[0] ? (1 << 10) : 0,
+ 0);
+}
+
+
+static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = {
+ AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
+ AC97_PAGE_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic As Center/LFE",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_alc655_mic_get,
+ .put = snd_ac97_alc655_mic_put,
+ .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+ },
+};
+
+static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts_655[3] = { "PCM", "Analog In", "IEC958 In" };
+ static char *texts_658[4] = { "PCM", "Analog1 In", "Analog2 In", "IEC958 In" };
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = ac97->spec.dev_flags ? 4 : 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ ac97->spec.dev_flags ?
+ texts_658[uinfo->value.enumerated.item] :
+ texts_655[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int alc655_iec958_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_ALC650_MULTICH];
+ val = (val >> 12) & 3;
+ if (ac97->spec.dev_flags && val == 3)
+ val = 0;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int alc655_iec958_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 3 << 12,
+ (unsigned short)ucontrol->value.enumerated.item[0] << 12,
+ 0);
+}
+
+static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc655[] = {
+ AC97_PAGE_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0, 0),
+ /* disable this controls since it doesn't work as expected */
+ /* AC97_PAGE_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0, 0), */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Route",
+ .info = alc655_iec958_route_info,
+ .get = alc655_iec958_route_get,
+ .put = alc655_iec958_route_put,
+ },
+};
+
+static int patch_alc655_specific(ac97_t * ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, snd_ac97_controls_alc655, ARRAY_SIZE(snd_ac97_controls_alc655))) < 0)
+ return err;
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+ return err;
+ }
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_alc655_ops = {
+ .build_specific = patch_alc655_specific
+};
+
+int patch_alc655(ac97_t * ac97)
+{
+ unsigned int val;
+
+ ac97->spec.dev_flags = (ac97->id == 0x414c4780); /* ALC658 */
+
+ ac97->build_ops = &patch_alc655_ops;
+
+ /* assume only page 0 for writing cache */
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
+
+ /* adjust default values */
+ val = snd_ac97_read(ac97, 0x7a); /* misc control */
+ if (ac97->id == 0x414c4780) /* ALC658 */
+ val &= ~(1 << 1); /* Pin 47 is spdif input pin */
+ else /* ALC655 */
+ val |= (1 << 1); /* Pin 47 is spdif input pin */
+ val &= ~(1 << 12); /* vref enable */
+ snd_ac97_write_cache(ac97, 0x7a, val);
+ /* set default: spdif-in enabled,
+ spdif-in monitor off, spdif-in PCM off
+ center on mic off, surround on line-in off
+ duplicate front off
+ */
+ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
+
+ /* full DAC volume */
+ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
+ snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
+ return 0;
+}
+
+
+#define AC97_ALC850_JACK_SELECT 0x76
+#define AC97_ALC850_MISC1 0x7a
+
+static int ac97_alc850_surround_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 12) & 7) == 2;
+ return 0;
+}
+
+static int ac97_alc850_surround_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ /* SURR 1kOhm (bit4), Amp (bit5) */
+ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5),
+ ucontrol->value.integer.value[0] ? (1<<5) : (1<<4));
+ /* LINE-IN = 0, SURROUND = 2 */
+ return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
+ ucontrol->value.integer.value[0] ? (2<<12) : (0<<12));
+}
+
+static int ac97_alc850_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 4) & 7) == 2;
+ return 0;
+}
+
+static int ac97_alc850_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ /* Vref disable (bit12), 1kOhm (bit13) */
+ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13),
+ ucontrol->value.integer.value[0] ? (1<<12) : (1<<13));
+ /* MIC-IN = 1, CENTER-LFE = 2 */
+ return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
+ ucontrol->value.integer.value[0] ? (2<<4) : (1<<4));
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = {
+ AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-In As Surround",
+ .info = snd_ac97_info_volsw,
+ .get = ac97_alc850_surround_get,
+ .put = ac97_alc850_surround_put,
+ .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic As Center/LFE",
+ .info = snd_ac97_info_volsw,
+ .get = ac97_alc850_mic_get,
+ .put = ac97_alc850_mic_put,
+ .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+ },
+
+};
+
+static int patch_alc850_specific(ac97_t *ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850))) < 0)
+ return err;
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+ return err;
+ }
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_alc850_ops = {
+ .build_specific = patch_alc850_specific
+};
+
+int patch_alc850(ac97_t *ac97)
+{
+ ac97->build_ops = &patch_alc850_ops;
+
+ ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */
+
+ /* assume only page 0 for writing cache */
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
+
+ /* adjust default values */
+ /* set default: spdif-in enabled,
+ spdif-in monitor off, spdif-in PCM off
+ center on mic off, surround on line-in off
+ duplicate front off
+ */
+ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
+ /* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off
+ * Front Amp: on, Vref: enable, Center 1kOhm: on, Mix: on
+ */
+ snd_ac97_write_cache(ac97, 0x7a, (1<<1)|(1<<4)|(0<<5)|(1<<6)|
+ (1<<7)|(0<<12)|(1<<13)|(0<<14));
+ /* detection UIO2,3: all path floating, UIO3: MIC, Vref2: disable,
+ * UIO1: FRONT, Vref3: disable, UIO3: LINE, Front-Mic: mute
+ */
+ snd_ac97_write_cache(ac97, 0x76, (0<<0)|(0<<2)|(1<<4)|(1<<7)|(2<<8)|
+ (1<<11)|(0<<12)|(1<<15));
+
+ /* full DAC volume */
+ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
+ snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
+ return 0;
+}
+
+
+/*
+ * C-Media CM97xx codecs
+ */
+static const snd_kcontrol_new_t snd_ac97_cm9738_controls[] = {
+ AC97_SINGLE("Line-In As Surround", AC97_CM9738_VENDOR_CTRL, 10, 1, 0),
+ AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0),
+};
+
+static int patch_cm9738_specific(ac97_t * ac97)
+{
+ return patch_build_controls(ac97, snd_ac97_cm9738_controls, ARRAY_SIZE(snd_ac97_cm9738_controls));
+}
+
+static struct snd_ac97_build_ops patch_cm9738_ops = {
+ .build_specific = patch_cm9738_specific
+};
+
+int patch_cm9738(ac97_t * ac97)
+{
+ ac97->build_ops = &patch_cm9738_ops;
+ /* FIXME: can anyone confirm below? */
+ /* CM9738 has no PCM volume although the register reacts */
+ ac97->flags |= AC97_HAS_NO_PCM_VOL;
+ snd_ac97_write_cache(ac97, AC97_PCM, 0x8000);
+
+ return 0;
+}
+
+static int snd_ac97_cmedia_spdif_playback_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = { "Analog", "Digital" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ac97_cmedia_spdif_playback_source_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = ac97->regs[AC97_CM9739_SPDIF_CTRL];
+ ucontrol->value.enumerated.item[0] = (val >> 1) & 0x01;
+ return 0;
+}
+
+static int snd_ac97_cmedia_spdif_playback_source_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+ return snd_ac97_update_bits(ac97, AC97_CM9739_SPDIF_CTRL,
+ 0x01 << 1,
+ (ucontrol->value.enumerated.item[0] & 0x01) << 1);
+}
+
+static const snd_kcontrol_new_t snd_ac97_cm9739_controls_spdif[] = {
+ /* BIT 0: SPDI_EN - always true */
+ { /* BIT 1: SPDIFS */
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+ .info = snd_ac97_cmedia_spdif_playback_source_info,
+ .get = snd_ac97_cmedia_spdif_playback_source_get,
+ .put = snd_ac97_cmedia_spdif_playback_source_put,
+ },
+ /* BIT 2: IG_SPIV */
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9739_SPDIF_CTRL, 2, 1, 0),
+ /* BIT 3: SPI2F */
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9739_SPDIF_CTRL, 3, 1, 0),
+ /* BIT 4: SPI2SDI */
+ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9739_SPDIF_CTRL, 4, 1, 0),
+ /* BIT 8: SPD32 - 32bit SPDIF - not supported yet */
+};
+
+static int snd_ac97_cm9739_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int snd_ac97_cm9739_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
+ ucontrol->value.integer.value[0] ?
+ 0x1000 : 0x2000);
+}
+
+static const snd_kcontrol_new_t snd_ac97_cm9739_controls[] = {
+ AC97_SINGLE("Line-In As Surround", AC97_CM9739_MULTI_CHAN, 10, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic As Center/LFE",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_cm9739_center_mic_get,
+ .put = snd_ac97_cm9739_center_mic_put,
+ .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+ },
+};
+
+static int patch_cm9739_specific(ac97_t * ac97)
+{
+ return patch_build_controls(ac97, snd_ac97_cm9739_controls, ARRAY_SIZE(snd_ac97_cm9739_controls));
+}
+
+static int patch_cm9739_post_spdif(ac97_t * ac97)
+{
+ return patch_build_controls(ac97, snd_ac97_cm9739_controls_spdif, ARRAY_SIZE(snd_ac97_cm9739_controls_spdif));
+}
+
+static struct snd_ac97_build_ops patch_cm9739_ops = {
+ .build_specific = patch_cm9739_specific,
+ .build_post_spdif = patch_cm9739_post_spdif
+};
+
+int patch_cm9739(ac97_t * ac97)
+{
+ unsigned short val;
+
+ ac97->build_ops = &patch_cm9739_ops;
+
+ /* CM9739/A has no Master and PCM volume although the register reacts */
+ ac97->flags |= AC97_HAS_NO_MASTER_VOL | AC97_HAS_NO_PCM_VOL;
+ snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000);
+ snd_ac97_write_cache(ac97, AC97_PCM, 0x8000);
+
+ /* check spdif */
+ val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+ if (val & AC97_EA_SPCV) {
+ /* enable spdif in */
+ snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL,
+ snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) | 0x01);
+ ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+ } else {
+ ac97->ext_id &= ~AC97_EI_SPDIF; /* disable extended-id */
+ ac97->rates[AC97_RATES_SPDIF] = 0;
+ }
+
+ /* set-up multi channel */
+ /* bit 14: 0 = SPDIF, 1 = EAPD */
+ /* bit 13: enable internal vref output for mic */
+ /* bit 12: disable center/lfe (swithable) */
+ /* bit 10: disable surround/line (switchable) */
+ /* bit 9: mix 2 surround off */
+ /* bit 4: undocumented; 0 mutes the CM9739A, which defaults to 1 */
+ /* bit 3: undocumented; surround? */
+ /* bit 0: dB */
+ val = snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) & (1 << 4);
+ val |= (1 << 3);
+ val |= (1 << 13);
+ if (! (ac97->ext_id & AC97_EI_SPDIF))
+ val |= (1 << 14);
+ snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, val);
+
+ /* FIXME: set up GPIO */
+ snd_ac97_write_cache(ac97, 0x70, 0x0100);
+ snd_ac97_write_cache(ac97, 0x72, 0x0020);
+ /* Special exception for ASUS W1000/CMI9739. It does not have an SPDIF in. */
+ if (ac97->pci &&
+ ac97->subsystem_vendor == 0x1043 &&
+ ac97->subsystem_device == 0x1843) {
+ snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL,
+ snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) & ~0x01);
+ snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN,
+ snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) | (1 << 14));
+ }
+
+ return 0;
+}
+
+#define AC97_CM9761_MULTI_CHAN 0x64
+#define AC97_CM9761_SPDIF_CTRL 0x6c
+
+static int snd_ac97_cm9761_linein_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x0400)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int snd_ac97_cm9761_linein_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short vals[2][2] = {
+ { 0x0008, 0x0400 }, /* off, on */
+ { 0x0000, 0x0408 }, /* off, on (9761-82 rev.B) */
+ };
+ return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x0408,
+ vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]);
+}
+
+static int snd_ac97_cm9761_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+ if (ac97->spec.dev_flags) /* 9761-82 rev.B */
+ ucontrol->value.integer.value[0] = !ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int snd_ac97_cm9761_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+ unsigned short vals[2][2] = {
+ { 0x2000, 0x1880 }, /* off, on */
+ { 0x1000, 0x2880 }, /* off, on (9761-82 rev.B) */
+ };
+ return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3880,
+ vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]);
+}
+
+static const snd_kcontrol_new_t snd_ac97_cm9761_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-In As Surround",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_cm9761_linein_rear_get,
+ .put = snd_ac97_cm9761_linein_rear_put,
+ .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic As Center/LFE",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_cm9761_center_mic_get,
+ .put = snd_ac97_cm9761_center_mic_put,
+ .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+ },
+};
+
+static int patch_cm9761_specific(ac97_t * ac97)
+{
+ return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls));
+}
+
+static struct snd_ac97_build_ops patch_cm9761_ops = {
+ .build_specific = patch_cm9761_specific,
+ .build_post_spdif = patch_cm9739_post_spdif /* hope it's identical... */
+};
+
+int patch_cm9761(ac97_t *ac97)
+{
+ unsigned short val;
+
+ /* CM9761 has no PCM volume although the register reacts */
+ /* Master volume seems to have _some_ influence on the analog
+ * input sounds
+ */
+ ac97->flags |= /*AC97_HAS_NO_MASTER_VOL |*/ AC97_HAS_NO_PCM_VOL;
+ snd_ac97_write_cache(ac97, AC97_MASTER, 0x8808);
+ snd_ac97_write_cache(ac97, AC97_PCM, 0x8808);
+
+ ac97->spec.dev_flags = 0; /* 1 = model 82 revision B */
+ if (ac97->id == AC97_ID_CM9761_82) {
+ unsigned short tmp;
+ /* check page 1, reg 0x60 */
+ val = snd_ac97_read(ac97, AC97_INT_PAGING);
+ snd_ac97_write_cache(ac97, AC97_INT_PAGING, (val & ~0x0f) | 0x01);
+ tmp = snd_ac97_read(ac97, 0x60);
+ ac97->spec.dev_flags = tmp & 1; /* revision B? */
+ snd_ac97_write_cache(ac97, AC97_INT_PAGING, val);
+ }
+
+ ac97->build_ops = &patch_cm9761_ops;
+
+ /* enable spdif */
+ /* force the SPDIF bit in ext_id - codec doesn't set this bit! */
+ ac97->ext_id |= AC97_EI_SPDIF;
+ /* to be sure: we overwrite the ext status bits */
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, 0x05c0);
+ /* Don't set 0x0200 here. This results in the silent analog output */
+ snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0009);
+ ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+
+ /* set-up multi channel */
+ /* bit 15: pc master beep off
+ * bit 14: ??
+ * bit 13: vref ctl [= cm9739]
+ * bit 12: center/mic [= cm9739] (reverted on rev B)
+ * bit 11: ?? (mic/center/lfe) (reverted on rev B)
+ * bit 10: suddound/line [= cm9739]
+ * bit 9: mix 2 surround
+ * bit 8: ?
+ * bit 7: ?? (mic/center/lfe)
+ * bit 4: ?? (front)
+ * bit 3: ?? (line-in/rear share) (revereted with rev B)
+ * bit 2: ?? (surround)
+ * bit 1: front mic
+ * bit 0: mic boost
+ */
+
+#if 0
+ if (ac97->spec.dev_flags)
+ val = 0x0214;
+ else
+ val = 0x321c;
+#endif
+ val = snd_ac97_read(ac97, AC97_CM9761_MULTI_CHAN);
+ val |= (1 << 4); /* front on */
+ snd_ac97_write_cache(ac97, AC97_CM9761_MULTI_CHAN, val);
+
+ /* FIXME: set up GPIO */
+ snd_ac97_write_cache(ac97, 0x70, 0x0100);
+ snd_ac97_write_cache(ac97, 0x72, 0x0020);
+
+ return 0;
+}
+
+
+/*
+ * VIA VT1616 codec
+ */
+static const snd_kcontrol_new_t snd_ac97_controls_vt1616[] = {
+AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
+AC97_SINGLE("Alternate Level to Surround Out", 0x5a, 15, 1, 0),
+AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0),
+AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
+};
+
+static int patch_vt1616_specific(ac97_t * ac97)
+{
+ int err;
+
+ if (snd_ac97_try_bit(ac97, 0x5a, 9))
+ if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[0], 1)) < 0)
+ return err;
+ if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
+ return err;
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_vt1616_ops = {
+ .build_specific = patch_vt1616_specific
+};
+
+int patch_vt1616(ac97_t * ac97)
+{
+ ac97->build_ops = &patch_vt1616_ops;
+ return 0;
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_it2646[] = {
+ AC97_SINGLE("Line-In As Surround", 0x76, 9, 1, 0),
+ AC97_SINGLE("Mic As Center/LFE", 0x76, 10, 1, 0),
+};
+
+static const snd_kcontrol_new_t snd_ac97_spdif_controls_it2646[] = {
+ AC97_SINGLE("IEC958 Capture Switch", 0x76, 11, 1, 0),
+ AC97_SINGLE("Analog to IEC958 Output", 0x76, 12, 1, 0),
+ AC97_SINGLE("IEC958 Input Monitor", 0x76, 13, 1, 0),
+};
+
+static int patch_it2646_specific(ac97_t * ac97)
+{
+ int err;
+ if ((err = patch_build_controls(ac97, snd_ac97_controls_it2646, ARRAY_SIZE(snd_ac97_controls_it2646))) < 0)
+ return err;
+ if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_it2646, ARRAY_SIZE(snd_ac97_spdif_controls_it2646))) < 0)
+ return err;
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_it2646_ops = {
+ .build_specific = patch_it2646_specific
+};
+
+int patch_it2646(ac97_t * ac97)
+{
+ ac97->build_ops = &patch_it2646_ops;
+ /* full DAC volume */
+ snd_ac97_write_cache(ac97, 0x5E, 0x0808);
+ snd_ac97_write_cache(ac97, 0x7A, 0x0808);
+ return 0;
+}
+
+/* Si3036/8 specific registers */
+#define AC97_SI3036_CHIP_ID 0x5a
+
+int mpatch_si3036(ac97_t * ac97)
+{
+ //printk("mpatch_si3036: chip id = %x\n", snd_ac97_read(ac97, 0x5a));
+ snd_ac97_write_cache(ac97, 0x5c, 0xf210 );
+ snd_ac97_write_cache(ac97, 0x68, 0);
+ return 0;
+}
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
new file mode 100644
index 00000000000..6db51c96f5d
--- /dev/null
+++ b/sound/pci/ac97/ac97_patch.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal interface for Audio Codec '97
+ *
+ * For more details look to AC '97 component specification revision 2.2
+ * by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+int patch_yamaha_ymf753(ac97_t * ac97);
+int patch_wolfson00(ac97_t * ac97);
+int patch_wolfson03(ac97_t * ac97);
+int patch_wolfson04(ac97_t * ac97);
+int patch_wolfson05(ac97_t * ac97);
+int patch_wolfson11(ac97_t * ac97);
+int patch_wolfson13(ac97_t * ac97);
+int patch_tritech_tr28028(ac97_t * ac97);
+int patch_sigmatel_stac9700(ac97_t * ac97);
+int patch_sigmatel_stac9708(ac97_t * ac97);
+int patch_sigmatel_stac9721(ac97_t * ac97);
+int patch_sigmatel_stac9744(ac97_t * ac97);
+int patch_sigmatel_stac9756(ac97_t * ac97);
+int patch_sigmatel_stac9758(ac97_t * ac97);
+int patch_cirrus_cs4299(ac97_t * ac97);
+int patch_cirrus_spdif(ac97_t * ac97);
+int patch_conexant(ac97_t * ac97);
+int patch_ad1819(ac97_t * ac97);
+int patch_ad1881(ac97_t * ac97);
+int patch_ad1885(ac97_t * ac97);
+int patch_ad1886(ac97_t * ac97);
+int patch_ad1888(ac97_t * ac97);
+int patch_ad1980(ac97_t * ac97);
+int patch_ad1981a(ac97_t * ac97);
+int patch_ad1981b(ac97_t * ac97);
+int patch_ad1985(ac97_t * ac97);
+int patch_alc650(ac97_t * ac97);
+int patch_alc655(ac97_t * ac97);
+int patch_alc850(ac97_t * ac97);
+int patch_cm9738(ac97_t * ac97);
+int patch_cm9739(ac97_t * ac97);
+int patch_cm9761(ac97_t * ac97);
+int patch_vt1616(ac97_t * ac97);
+int patch_it2646(ac97_t * ac97);
+int mpatch_si3036(ac97_t * ac97);
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
new file mode 100644
index 00000000000..dd289b9512e
--- /dev/null
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal interface for Audio Codec '97
+ *
+ * For more details look to AC '97 component specification revision 2.2
+ * by Intel Corporation (http://developer.intel.com) and to datasheets
+ * for specific codecs.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include "ac97_patch.h"
+#include "ac97_id.h"
+#include "ac97_local.h"
+
+/*
+ * PCM support
+ */
+
+static unsigned char rate_reg_tables[2][4][9] = {
+{
+ /* standard rates */
+ {
+ /* 3&4 front, 7&8 rear, 6&9 center/lfe */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 3 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 4 */
+ 0xff, /* slot 5 */
+ AC97_PCM_LFE_DAC_RATE, /* slot 6 */
+ AC97_PCM_SURR_DAC_RATE, /* slot 7 */
+ AC97_PCM_SURR_DAC_RATE, /* slot 8 */
+ AC97_PCM_LFE_DAC_RATE, /* slot 9 */
+ 0xff, /* slot 10 */
+ 0xff, /* slot 11 */
+ },
+ {
+ /* 7&8 front, 6&9 rear, 10&11 center/lfe */
+ 0xff, /* slot 3 */
+ 0xff, /* slot 4 */
+ 0xff, /* slot 5 */
+ AC97_PCM_SURR_DAC_RATE, /* slot 6 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 7 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 8 */
+ AC97_PCM_SURR_DAC_RATE, /* slot 9 */
+ AC97_PCM_LFE_DAC_RATE, /* slot 10 */
+ AC97_PCM_LFE_DAC_RATE, /* slot 11 */
+ },
+ {
+ /* 6&9 front, 10&11 rear, 3&4 center/lfe */
+ AC97_PCM_LFE_DAC_RATE, /* slot 3 */
+ AC97_PCM_LFE_DAC_RATE, /* slot 4 */
+ 0xff, /* slot 5 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 6 */
+ 0xff, /* slot 7 */
+ 0xff, /* slot 8 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 9 */
+ AC97_PCM_SURR_DAC_RATE, /* slot 10 */
+ AC97_PCM_SURR_DAC_RATE, /* slot 11 */
+ },
+ {
+ /* 10&11 front, 3&4 rear, 7&8 center/lfe */
+ AC97_PCM_SURR_DAC_RATE, /* slot 3 */
+ AC97_PCM_SURR_DAC_RATE, /* slot 4 */
+ 0xff, /* slot 5 */
+ 0xff, /* slot 6 */
+ AC97_PCM_LFE_DAC_RATE, /* slot 7 */
+ AC97_PCM_LFE_DAC_RATE, /* slot 8 */
+ 0xff, /* slot 9 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 10 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 11 */
+ },
+},
+{
+ /* double rates */
+ {
+ /* 3&4 front, 7&8 front (t+1) */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 3 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 4 */
+ 0xff, /* slot 5 */
+ 0xff, /* slot 6 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 7 */
+ AC97_PCM_FRONT_DAC_RATE, /* slot 8 */
+ 0xff, /* slot 9 */
+ 0xff, /* slot 10 */
+ 0xff, /* slot 11 */
+ },
+ {
+ /* not specified in the specification */
+ 0xff, /* slot 3 */
+ 0xff, /* slot 4 */
+ 0xff, /* slot 5 */
+ 0xff, /* slot 6 */
+ 0xff, /* slot 7 */
+ 0xff, /* slot 8 */
+ 0xff, /* slot 9 */
+ 0xff, /* slot 10 */
+ 0xff, /* slot 11 */
+ },
+ {
+ 0xff, /* slot 3 */
+ 0xff, /* slot 4 */
+ 0xff, /* slot 5 */
+ 0xff, /* slot 6 */
+ 0xff, /* slot 7 */
+ 0xff, /* slot 8 */
+ 0xff, /* slot 9 */
+ 0xff, /* slot 10 */
+ 0xff, /* slot 11 */
+ },
+ {
+ 0xff, /* slot 3 */
+ 0xff, /* slot 4 */
+ 0xff, /* slot 5 */
+ 0xff, /* slot 6 */
+ 0xff, /* slot 7 */
+ 0xff, /* slot 8 */
+ 0xff, /* slot 9 */
+ 0xff, /* slot 10 */
+ 0xff, /* slot 11 */
+ }
+}};
+
+/* FIXME: more various mappings for ADC? */
+static unsigned char rate_cregs[9] = {
+ AC97_PCM_LR_ADC_RATE, /* 3 */
+ AC97_PCM_LR_ADC_RATE, /* 4 */
+ 0xff, /* 5 */
+ AC97_PCM_MIC_ADC_RATE, /* 6 */
+ 0xff, /* 7 */
+ 0xff, /* 8 */
+ 0xff, /* 9 */
+ 0xff, /* 10 */
+ 0xff, /* 11 */
+};
+
+static unsigned char get_slot_reg(struct ac97_pcm *pcm, unsigned short cidx,
+ unsigned short slot, int dbl)
+{
+ if (slot < 3)
+ return 0xff;
+ if (slot > 11)
+ return 0xff;
+ if (pcm->spdif)
+ return AC97_SPDIF; /* pseudo register */
+ if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return rate_reg_tables[dbl][pcm->r[dbl].rate_table[cidx]][slot - 3];
+ else
+ return rate_cregs[slot - 3];
+}
+
+static int set_spdif_rate(ac97_t *ac97, unsigned short rate)
+{
+ unsigned short old, bits, reg, mask;
+ unsigned int sbits;
+
+ if (! (ac97->ext_id & AC97_EI_SPDIF))
+ return -ENODEV;
+
+ /* TODO: double rate support */
+ if (ac97->flags & AC97_CS_SPDIF) {
+ switch (rate) {
+ case 48000: bits = 0; break;
+ case 44100: bits = 1 << AC97_SC_SPSR_SHIFT; break;
+ default: /* invalid - disable output */
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+ return -EINVAL;
+ }
+ reg = AC97_CSR_SPDIF;
+ mask = 1 << AC97_SC_SPSR_SHIFT;
+ } else {
+ if (ac97->id == AC97_ID_CM9739 && rate != 48000) {
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+ return -EINVAL;
+ }
+ switch (rate) {
+ case 44100: bits = AC97_SC_SPSR_44K; break;
+ case 48000: bits = AC97_SC_SPSR_48K; break;
+ case 32000: bits = AC97_SC_SPSR_32K; break;
+ default: /* invalid - disable output */
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+ return -EINVAL;
+ }
+ reg = AC97_SPDIF;
+ mask = AC97_SC_SPSR_MASK;
+ }
+
+ down(&ac97->reg_mutex);
+ old = snd_ac97_read(ac97, reg) & mask;
+ if (old != bits) {
+ snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+ snd_ac97_update_bits_nolock(ac97, reg, mask, bits);
+ /* update the internal spdif bits */
+ sbits = ac97->spdif_status;
+ if (sbits & IEC958_AES0_PROFESSIONAL) {
+ sbits &= ~IEC958_AES0_PRO_FS;
+ switch (rate) {
+ case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break;
+ case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break;
+ case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break;
+ }
+ } else {
+ sbits &= ~(IEC958_AES3_CON_FS << 24);
+ switch (rate) {
+ case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break;
+ case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break;
+ case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break;
+ }
+ }
+ ac97->spdif_status = sbits;
+ }
+ snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);
+ up(&ac97->reg_mutex);
+ return 0;
+}
+
+/**
+ * snd_ac97_set_rate - change the rate of the given input/output.
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @rate: the sample rate to set
+ *
+ * Changes the rate of the given input/output on the codec.
+ * If the codec doesn't support VAR, the rate must be 48000 (except
+ * for SPDIF).
+ *
+ * The valid registers are AC97_PMC_MIC_ADC_RATE,
+ * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE.
+ * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted
+ * if the codec supports them.
+ * AC97_SPDIF is accepted as a pseudo register to modify the SPDIF
+ * status bits.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned int rate)
+{
+ int dbl;
+ unsigned int tmp;
+
+ dbl = rate > 48000;
+ if (dbl) {
+ if (!(ac97->flags & AC97_DOUBLE_RATE))
+ return -EINVAL;
+ if (reg != AC97_PCM_FRONT_DAC_RATE)
+ return -EINVAL;
+ }
+
+ switch (reg) {
+ case AC97_PCM_MIC_ADC_RATE:
+ if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */
+ if (rate != 48000)
+ return -EINVAL;
+ break;
+ case AC97_PCM_FRONT_DAC_RATE:
+ case AC97_PCM_LR_ADC_RATE:
+ if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0) /* VRA */
+ if (rate != 48000 && rate != 96000)
+ return -EINVAL;
+ break;
+ case AC97_PCM_SURR_DAC_RATE:
+ if (! (ac97->scaps & AC97_SCAP_SURROUND_DAC))
+ return -EINVAL;
+ break;
+ case AC97_PCM_LFE_DAC_RATE:
+ if (! (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
+ return -EINVAL;
+ break;
+ case AC97_SPDIF:
+ /* special case */
+ return set_spdif_rate(ac97, rate);
+ default:
+ return -EINVAL;
+ }
+ if (dbl)
+ rate /= 2;
+ tmp = (rate * ac97->bus->clock) / 48000;
+ if (tmp > 65535)
+ return -EINVAL;
+ if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_DRA, dbl ? AC97_EA_DRA : 0);
+ snd_ac97_update(ac97, reg, tmp & 0xffff);
+ snd_ac97_read(ac97, reg);
+ return 0;
+}
+
+static unsigned short get_pslots(ac97_t *ac97, unsigned char *rate_table, unsigned short *spdif_slots)
+{
+ if (!ac97_is_audio(ac97))
+ return 0;
+ if (ac97_is_rev22(ac97) || ac97_can_amap(ac97)) {
+ unsigned short slots = 0;
+ if (ac97_is_rev22(ac97)) {
+ /* Note: it's simply emulation of AMAP behaviour */
+ u16 es;
+ es = ac97->regs[AC97_EXTENDED_ID] &= ~AC97_EI_DACS_SLOT_MASK;
+ switch (ac97->addr) {
+ case 1:
+ case 2: es |= (1<<AC97_EI_DACS_SLOT_SHIFT); break;
+ case 3: es |= (2<<AC97_EI_DACS_SLOT_SHIFT); break;
+ }
+ snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, es);
+ }
+ switch (ac97->addr) {
+ case 0:
+ slots |= (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+ slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
+ if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+ slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT);
+ else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
+ else
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+ }
+ *rate_table = 0;
+ break;
+ case 1:
+ case 2:
+ slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+ slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
+ else
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+ }
+ *rate_table = 1;
+ break;
+ case 3:
+ slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+ if (ac97->ext_id & AC97_EI_SPDIF)
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+ *rate_table = 2;
+ break;
+ }
+ return slots;
+ } else {
+ unsigned short slots;
+ slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+ slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
+ if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+ slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+ if (ac97->ext_id & AC97_EI_SPDIF) {
+ if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT);
+ else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
+ else
+ *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+ }
+ *rate_table = 0;
+ return slots;
+ }
+}
+
+static unsigned short get_cslots(ac97_t *ac97)
+{
+ unsigned short slots;
+
+ if (!ac97_is_audio(ac97))
+ return 0;
+ slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
+ slots |= (1<<AC97_SLOT_MIC);
+ return slots;
+}
+
+static unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned short slots, int dbl)
+{
+ int i, idx;
+ unsigned int rates = ~0;
+ unsigned char reg;
+
+ for (i = 3; i < 12; i++) {
+ if (!(slots & (1 << i)))
+ continue;
+ reg = get_slot_reg(pcm, cidx, i, dbl);
+ switch (reg) {
+ case AC97_PCM_FRONT_DAC_RATE: idx = AC97_RATES_FRONT_DAC; break;
+ case AC97_PCM_SURR_DAC_RATE: idx = AC97_RATES_SURR_DAC; break;
+ case AC97_PCM_LFE_DAC_RATE: idx = AC97_RATES_LFE_DAC; break;
+ case AC97_PCM_LR_ADC_RATE: idx = AC97_RATES_ADC; break;
+ case AC97_PCM_MIC_ADC_RATE: idx = AC97_RATES_MIC_ADC; break;
+ default: idx = AC97_RATES_SPDIF; break;
+ }
+ rates &= pcm->r[dbl].codec[cidx]->rates[idx];
+ }
+ if (!dbl)
+ rates &= ~(SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000);
+ return rates;
+}
+
+/**
+ * snd_ac97_pcm_assign - assign AC97 slots to given PCM streams
+ * @bus: the ac97 bus instance
+ * @pcms_count: count of PCMs to be assigned
+ * @pcms: PCMs to be assigned
+ *
+ * It assigns available AC97 slots for given PCMs. If none or only
+ * some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members
+ * are reduced and might be zero.
+ */
+int snd_ac97_pcm_assign(ac97_bus_t *bus,
+ unsigned short pcms_count,
+ const struct ac97_pcm *pcms)
+{
+ int i, j, k;
+ const struct ac97_pcm *pcm;
+ struct ac97_pcm *rpcms, *rpcm;
+ unsigned short avail_slots[2][4];
+ unsigned char rate_table[2][4];
+ unsigned short tmp, slots;
+ unsigned short spdif_slots[4];
+ unsigned int rates;
+ ac97_t *codec;
+
+ rpcms = kcalloc(pcms_count, sizeof(struct ac97_pcm), GFP_KERNEL);
+ if (rpcms == NULL)
+ return -ENOMEM;
+ memset(avail_slots, 0, sizeof(avail_slots));
+ memset(rate_table, 0, sizeof(rate_table));
+ memset(spdif_slots, 0, sizeof(spdif_slots));
+ for (i = 0; i < 4; i++) {
+ codec = bus->codec[i];
+ if (!codec)
+ continue;
+ avail_slots[0][i] = get_pslots(codec, &rate_table[0][i], &spdif_slots[i]);
+ avail_slots[1][i] = get_cslots(codec);
+ if (!(codec->scaps & AC97_SCAP_INDEP_SDIN)) {
+ for (j = 0; j < i; j++) {
+ if (bus->codec[j])
+ avail_slots[1][i] &= ~avail_slots[1][j];
+ }
+ }
+ }
+ /* first step - exclusive devices */
+ for (i = 0; i < pcms_count; i++) {
+ pcm = &pcms[i];
+ rpcm = &rpcms[i];
+ /* low-level driver thinks that it's more clever */
+ if (pcm->copy_flag) {
+ *rpcm = *pcm;
+ continue;
+ }
+ rpcm->stream = pcm->stream;
+ rpcm->exclusive = pcm->exclusive;
+ rpcm->spdif = pcm->spdif;
+ rpcm->private_value = pcm->private_value;
+ rpcm->bus = bus;
+ rpcm->rates = ~0;
+ slots = pcm->r[0].slots;
+ for (j = 0; j < 4 && slots; j++) {
+ if (!bus->codec[j])
+ continue;
+ rates = ~0;
+ if (pcm->spdif && pcm->stream == 0)
+ tmp = spdif_slots[j];
+ else
+ tmp = avail_slots[pcm->stream][j];
+ if (pcm->exclusive) {
+ /* exclusive access */
+ tmp &= slots;
+ for (k = 0; k < i; k++) {
+ if (rpcm->stream == rpcms[k].stream)
+ tmp &= ~rpcms[k].r[0].rslots[j];
+ }
+ } else {
+ /* non-exclusive access */
+ tmp &= pcm->r[0].slots;
+ }
+ if (tmp) {
+ rpcm->r[0].rslots[j] = tmp;
+ rpcm->r[0].codec[j] = bus->codec[j];
+ rpcm->r[0].rate_table[j] = rate_table[pcm->stream][j];
+ if (bus->no_vra)
+ rates = SNDRV_PCM_RATE_48000;
+ else
+ rates = get_rates(rpcm, j, tmp, 0);
+ if (pcm->exclusive)
+ avail_slots[pcm->stream][j] &= ~tmp;
+ }
+ slots &= ~tmp;
+ rpcm->r[0].slots |= tmp;
+ rpcm->rates &= rates;
+ }
+ /* for double rate, we check the first codec only */
+ if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ bus->codec[0] && (bus->codec[0]->flags & AC97_DOUBLE_RATE) &&
+ rate_table[pcm->stream][0] == 0) {
+ tmp = (1<<AC97_SLOT_PCM_LEFT) | (1<<AC97_SLOT_PCM_RIGHT) |
+ (1<<AC97_SLOT_PCM_LEFT_0) | (1<<AC97_SLOT_PCM_RIGHT_0);
+ if ((tmp & pcm->r[1].slots) == tmp) {
+ rpcm->r[1].slots = tmp;
+ rpcm->r[1].rslots[0] = tmp;
+ rpcm->r[1].rate_table[0] = 0;
+ rpcm->r[1].codec[0] = bus->codec[0];
+ if (pcm->exclusive)
+ avail_slots[pcm->stream][0] &= ~tmp;
+ if (bus->no_vra)
+ rates = SNDRV_PCM_RATE_96000;
+ else
+ rates = get_rates(rpcm, 0, tmp, 1);
+ rpcm->rates |= rates;
+ }
+ }
+ if (rpcm->rates == ~0)
+ rpcm->rates = 0; /* not used */
+ }
+ bus->pcms_count = pcms_count;
+ bus->pcms = rpcms;
+ return 0;
+}
+
+/**
+ * snd_ac97_pcm_open - opens the given AC97 pcm
+ * @pcm: the ac97 pcm instance
+ * @rate: rate in Hz, if codec does not support VRA, this value must be 48000Hz
+ * @cfg: output stream characteristics
+ * @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm
+ *
+ * It locks the specified slots and sets the given rate to AC97 registers.
+ */
+int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
+ enum ac97_pcm_cfg cfg, unsigned short slots)
+{
+ ac97_bus_t *bus;
+ int i, cidx, r, ok_flag;
+ unsigned int reg_ok[4] = {0,0,0,0};
+ unsigned char reg;
+ int err = 0;
+
+ r = rate > 48000;
+ bus = pcm->bus;
+ if (cfg == AC97_PCM_CFG_SPDIF) {
+ int err;
+ for (cidx = 0; cidx < 4; cidx++)
+ if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) {
+ err = set_spdif_rate(bus->codec[cidx], rate);
+ if (err < 0)
+ return err;
+ }
+ }
+ spin_lock_irq(&pcm->bus->bus_lock);
+ for (i = 3; i < 12; i++) {
+ if (!(slots & (1 << i)))
+ continue;
+ ok_flag = 0;
+ for (cidx = 0; cidx < 4; cidx++) {
+ if (bus->used_slots[pcm->stream][cidx] & (1 << i)) {
+ spin_unlock_irq(&pcm->bus->bus_lock);
+ err = -EBUSY;
+ goto error;
+ }
+ if (pcm->r[r].rslots[cidx] & (1 << i)) {
+ bus->used_slots[pcm->stream][cidx] |= (1 << i);
+ ok_flag++;
+ }
+ }
+ if (!ok_flag) {
+ spin_unlock_irq(&pcm->bus->bus_lock);
+ snd_printk(KERN_ERR "cannot find configuration for AC97 slot %i\n", i);
+ err = -EAGAIN;
+ goto error;
+ }
+ }
+ spin_unlock_irq(&pcm->bus->bus_lock);
+ for (i = 3; i < 12; i++) {
+ if (!(slots & (1 << i)))
+ continue;
+ for (cidx = 0; cidx < 4; cidx++) {
+ if (pcm->r[r].rslots[cidx] & (1 << i)) {
+ reg = get_slot_reg(pcm, cidx, i, r);
+ if (reg == 0xff) {
+ snd_printk(KERN_ERR "invalid AC97 slot %i?\n", i);
+ continue;
+ }
+ if (reg_ok[cidx] & (1 << (reg - AC97_PCM_FRONT_DAC_RATE)))
+ continue;
+ //printk(KERN_DEBUG "setting ac97 reg 0x%x to rate %d\n", reg, rate);
+ err = snd_ac97_set_rate(pcm->r[r].codec[cidx], reg, rate);
+ if (err < 0)
+ snd_printk(KERN_ERR "error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n", cidx, reg, rate, err);
+ else
+ reg_ok[cidx] |= (1 << (reg - AC97_PCM_FRONT_DAC_RATE));
+ }
+ }
+ }
+ pcm->aslots = slots;
+ return 0;
+
+ error:
+ pcm->aslots = slots;
+ snd_ac97_pcm_close(pcm);
+ return err;
+}
+
+/**
+ * snd_ac97_pcm_close - closes the given AC97 pcm
+ * @pcm: the ac97 pcm instance
+ *
+ * It frees the locked AC97 slots.
+ */
+int snd_ac97_pcm_close(struct ac97_pcm *pcm)
+{
+ ac97_bus_t *bus;
+ unsigned short slots = pcm->aslots;
+ int i, cidx;
+
+ bus = pcm->bus;
+ spin_lock_irq(&pcm->bus->bus_lock);
+ for (i = 3; i < 12; i++) {
+ if (!(slots & (1 << i)))
+ continue;
+ for (cidx = 0; cidx < 4; cidx++)
+ bus->used_slots[pcm->stream][cidx] &= ~(1 << i);
+ }
+ pcm->aslots = 0;
+ spin_unlock_irq(&pcm->bus->bus_lock);
+ return 0;
+}
+
+static int double_rate_hw_constraint_rate(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ snd_interval_t *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (channels->min > 2) {
+ static const snd_interval_t single_rates = {
+ .min = 1,
+ .max = 48000,
+ };
+ snd_interval_t *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ return snd_interval_refine(rate, &single_rates);
+ }
+ return 0;
+}
+
+static int double_rate_hw_constraint_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ snd_interval_t *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (rate->min > 48000) {
+ static const snd_interval_t double_rate_channels = {
+ .min = 2,
+ .max = 2,
+ };
+ snd_interval_t *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ return snd_interval_refine(channels, &double_rate_channels);
+ }
+ return 0;
+}
+
+/**
+ * snd_ac97_pcm_double_rate_rules - set double rate constraints
+ * @runtime: the runtime of the ac97 front playback pcm
+ *
+ * Installs the hardware constraint rules to prevent using double rates and
+ * more than two channels at the same time.
+ */
+int snd_ac97_pcm_double_rate_rules(snd_pcm_runtime_t *runtime)
+{
+ int err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ double_rate_hw_constraint_rate, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ double_rate_hw_constraint_channels, NULL,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ return err;
+}
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
new file mode 100644
index 00000000000..a040b2666ed
--- /dev/null
+++ b/sound/pci/ac97/ac97_proc.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal interface for Audio Codec '97
+ *
+ * For more details look to AC '97 component specification revision 2.2
+ * by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include "ac97_local.h"
+#include "ac97_id.h"
+
+/*
+ * proc interface
+ */
+
+static void snd_ac97_proc_read_functions(ac97_t *ac97, snd_info_buffer_t *buffer)
+{
+ int header = 0, function;
+ unsigned short info, sense_info;
+ static const char *function_names[12] = {
+ "Master Out", "AUX Out", "Center/LFE Out", "SPDIF Out",
+ "Phone In", "Mic 1", "Mic 2", "Line In", "CD In", "Video In",
+ "Aux In", "Mono Out"
+ };
+ static const char *locations[8] = {
+ "Rear I/O Panel", "Front Panel", "Motherboard", "Dock/External",
+ "reserved", "reserved", "reserved", "NC/unused"
+ };
+
+ for (function = 0; function < 12; ++function) {
+ snd_ac97_write(ac97, AC97_FUNC_SELECT, function << 1);
+ info = snd_ac97_read(ac97, AC97_FUNC_INFO);
+ if (!(info & 0x0001))
+ continue;
+ if (!header) {
+ snd_iprintf(buffer, "\n Gain Inverted Buffer delay Location\n");
+ header = 1;
+ }
+ sense_info = snd_ac97_read(ac97, AC97_SENSE_INFO);
+ snd_iprintf(buffer, "%-17s: %3d.%d dBV %c %2d/fs %s\n",
+ function_names[function],
+ (info & 0x8000 ? -1 : 1) * ((info & 0x7000) >> 12) * 3 / 2,
+ ((info & 0x0800) >> 11) * 5,
+ info & 0x0400 ? 'X' : '-',
+ (info & 0x03e0) >> 5,
+ locations[sense_info >> 13]);
+ }
+}
+
+static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx)
+{
+ char name[64];
+ unsigned short val, tmp, ext, mext;
+ static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=10/11" };
+ static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" };
+ static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" };
+ static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" };
+
+ snd_ac97_get_name(NULL, ac97->id, name, 0);
+ snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name);
+ if ((ac97->scaps & AC97_SCAP_AUDIO) == 0)
+ goto __modem;
+
+ if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23) {
+ val = snd_ac97_read(ac97, AC97_INT_PAGING);
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+ AC97_PAGE_MASK, AC97_PAGE_1);
+ tmp = snd_ac97_read(ac97, AC97_CODEC_CLASS_REV);
+ snd_iprintf(buffer, "Revision : 0x%02x\n", tmp & 0xff);
+ snd_iprintf(buffer, "Compat. Class : 0x%02x\n", (tmp >> 8) & 0x1f);
+ snd_iprintf(buffer, "Subsys. Vendor ID: 0x%04x\n",
+ snd_ac97_read(ac97, AC97_PCI_SVID));
+ snd_iprintf(buffer, "Subsys. ID : 0x%04x\n\n",
+ snd_ac97_read(ac97, AC97_PCI_SID));
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+ AC97_PAGE_MASK, val & AC97_PAGE_MASK);
+ }
+
+ // val = snd_ac97_read(ac97, AC97_RESET);
+ val = ac97->caps;
+ snd_iprintf(buffer, "Capabilities :%s%s%s%s%s%s\n",
+ val & AC97_BC_DEDICATED_MIC ? " -dedicated MIC PCM IN channel-" : "",
+ val & AC97_BC_RESERVED1 ? " -reserved1-" : "",
+ val & AC97_BC_BASS_TREBLE ? " -bass & treble-" : "",
+ val & AC97_BC_SIM_STEREO ? " -simulated stereo-" : "",
+ val & AC97_BC_HEADPHONE ? " -headphone out-" : "",
+ val & AC97_BC_LOUDNESS ? " -loudness-" : "");
+ tmp = ac97->caps & AC97_BC_DAC_MASK;
+ snd_iprintf(buffer, "DAC resolution : %s%s%s%s\n",
+ tmp == AC97_BC_16BIT_DAC ? "16-bit" : "",
+ tmp == AC97_BC_18BIT_DAC ? "18-bit" : "",
+ tmp == AC97_BC_20BIT_DAC ? "20-bit" : "",
+ tmp == AC97_BC_DAC_MASK ? "???" : "");
+ tmp = ac97->caps & AC97_BC_ADC_MASK;
+ snd_iprintf(buffer, "ADC resolution : %s%s%s%s\n",
+ tmp == AC97_BC_16BIT_ADC ? "16-bit" : "",
+ tmp == AC97_BC_18BIT_ADC ? "18-bit" : "",
+ tmp == AC97_BC_20BIT_ADC ? "20-bit" : "",
+ tmp == AC97_BC_ADC_MASK ? "???" : "");
+ snd_iprintf(buffer, "3D enhancement : %s\n",
+ snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]);
+ snd_iprintf(buffer, "\nCurrent setup\n");
+ val = snd_ac97_read(ac97, AC97_MIC);
+ snd_iprintf(buffer, "Mic gain : %s [%s]\n", val & 0x0040 ? "+20dB" : "+0dB", ac97->regs[AC97_MIC] & 0x0040 ? "+20dB" : "+0dB");
+ val = snd_ac97_read(ac97, AC97_GENERAL_PURPOSE);
+ snd_iprintf(buffer, "POP path : %s 3D\n"
+ "Sim. stereo : %s\n"
+ "3D enhancement : %s\n"
+ "Loudness : %s\n"
+ "Mono output : %s\n"
+ "Mic select : %s\n"
+ "ADC/DAC loopback : %s\n",
+ val & 0x8000 ? "post" : "pre",
+ val & 0x4000 ? "on" : "off",
+ val & 0x2000 ? "on" : "off",
+ val & 0x1000 ? "on" : "off",
+ val & 0x0200 ? "Mic" : "MIX",
+ val & 0x0100 ? "Mic2" : "Mic1",
+ val & 0x0080 ? "on" : "off");
+ if (ac97->ext_id & AC97_EI_DRA)
+ snd_iprintf(buffer, "Double rate slots: %s\n",
+ double_rate_slots[(val >> 10) & 3]);
+
+ ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+ if (ext == 0)
+ goto __modem;
+
+ snd_iprintf(buffer, "Extended ID : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n",
+ (ext & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT,
+ (ext & AC97_EI_REV_MASK) >> AC97_EI_REV_SHIFT,
+ ext & AC97_EI_AMAP ? " AMAP" : "",
+ ext & AC97_EI_LDAC ? " LDAC" : "",
+ ext & AC97_EI_SDAC ? " SDAC" : "",
+ ext & AC97_EI_CDAC ? " CDAC" : "",
+ (ext & AC97_EI_DACS_SLOT_MASK) >> AC97_EI_DACS_SLOT_SHIFT,
+ ext & AC97_EI_VRM ? " VRM" : "",
+ ext & AC97_EI_SPDIF ? " SPDIF" : "",
+ ext & AC97_EI_DRA ? " DRA" : "",
+ ext & AC97_EI_VRA ? " VRA" : "");
+ val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+ snd_iprintf(buffer, "Extended status :%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ val & AC97_EA_PRL ? " PRL" : "",
+ val & AC97_EA_PRK ? " PRK" : "",
+ val & AC97_EA_PRJ ? " PRJ" : "",
+ val & AC97_EA_PRI ? " PRI" : "",
+ val & AC97_EA_SPCV ? " SPCV" : "",
+ val & AC97_EA_MDAC ? " MADC" : "",
+ val & AC97_EA_LDAC ? " LDAC" : "",
+ val & AC97_EA_SDAC ? " SDAC" : "",
+ val & AC97_EA_CDAC ? " CDAC" : "",
+ ext & AC97_EI_SPDIF ? spdif_slots[(val & AC97_EA_SPSA_SLOT_MASK) >> AC97_EA_SPSA_SLOT_SHIFT] : "",
+ val & AC97_EA_VRM ? " VRM" : "",
+ val & AC97_EA_SPDIF ? " SPDIF" : "",
+ val & AC97_EA_DRA ? " DRA" : "",
+ val & AC97_EA_VRA ? " VRA" : "");
+ if (ext & AC97_EI_VRA) { /* VRA */
+ val = snd_ac97_read(ac97, AC97_PCM_FRONT_DAC_RATE);
+ snd_iprintf(buffer, "PCM front DAC : %iHz\n", val);
+ if (ext & AC97_EI_SDAC) {
+ val = snd_ac97_read(ac97, AC97_PCM_SURR_DAC_RATE);
+ snd_iprintf(buffer, "PCM Surr DAC : %iHz\n", val);
+ }
+ if (ext & AC97_EI_LDAC) {
+ val = snd_ac97_read(ac97, AC97_PCM_LFE_DAC_RATE);
+ snd_iprintf(buffer, "PCM LFE DAC : %iHz\n", val);
+ }
+ val = snd_ac97_read(ac97, AC97_PCM_LR_ADC_RATE);
+ snd_iprintf(buffer, "PCM ADC : %iHz\n", val);
+ }
+ if (ext & AC97_EI_VRM) {
+ val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE);
+ snd_iprintf(buffer, "PCM MIC ADC : %iHz\n", val);
+ }
+ if ((ext & AC97_EI_SPDIF) || (ac97->flags & AC97_CS_SPDIF)) {
+ if (ac97->flags & AC97_CS_SPDIF)
+ val = snd_ac97_read(ac97, AC97_CSR_SPDIF);
+ else
+ val = snd_ac97_read(ac97, AC97_SPDIF);
+
+ snd_iprintf(buffer, "SPDIF Control :%s%s%s%s Category=0x%x Generation=%i%s%s%s\n",
+ val & AC97_SC_PRO ? " PRO" : " Consumer",
+ val & AC97_SC_NAUDIO ? " Non-audio" : " PCM",
+ val & AC97_SC_COPY ? "" : " Copyright",
+ val & AC97_SC_PRE ? " Preemph50/15" : "",
+ (val & AC97_SC_CC_MASK) >> AC97_SC_CC_SHIFT,
+ (val & AC97_SC_L) >> 11,
+ (ac97->flags & AC97_CS_SPDIF) ?
+ spdif_rates_cs4205[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT] :
+ spdif_rates[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT],
+ (ac97->flags & AC97_CS_SPDIF) ?
+ (val & AC97_SC_DRS ? " Validity" : "") :
+ (val & AC97_SC_DRS ? " DRS" : ""),
+ (ac97->flags & AC97_CS_SPDIF) ?
+ (val & AC97_SC_V ? " Enabled" : "") :
+ (val & AC97_SC_V ? " Validity" : ""));
+ /* ALC650 specific*/
+ if ((ac97->id & 0xfffffff0) == 0x414c4720 &&
+ (snd_ac97_read(ac97, AC97_ALC650_CLOCK) & 0x01)) {
+ val = snd_ac97_read(ac97, AC97_ALC650_SPDIF_INPUT_STATUS2);
+ if (val & AC97_ALC650_CLOCK_LOCK) {
+ val = snd_ac97_read(ac97, AC97_ALC650_SPDIF_INPUT_STATUS1);
+ snd_iprintf(buffer, "SPDIF In Status :%s%s%s%s Category=0x%x Generation=%i",
+ val & AC97_ALC650_PRO ? " PRO" : " Consumer",
+ val & AC97_ALC650_NAUDIO ? " Non-audio" : " PCM",
+ val & AC97_ALC650_COPY ? "" : " Copyright",
+ val & AC97_ALC650_PRE ? " Preemph50/15" : "",
+ (val & AC97_ALC650_CC_MASK) >> AC97_ALC650_CC_SHIFT,
+ (val & AC97_ALC650_L) >> 15);
+ val = snd_ac97_read(ac97, AC97_ALC650_SPDIF_INPUT_STATUS2);
+ snd_iprintf(buffer, "%s Accuracy=%i%s%s\n",
+ spdif_rates[(val & AC97_ALC650_SPSR_MASK) >> AC97_ALC650_SPSR_SHIFT],
+ (val & AC97_ALC650_CLOCK_ACCURACY) >> AC97_ALC650_CLOCK_SHIFT,
+ (val & AC97_ALC650_CLOCK_LOCK ? " Locked" : " Unlocked"),
+ (val & AC97_ALC650_V ? " Validity?" : ""));
+ } else {
+ snd_iprintf(buffer, "SPDIF In Status : Not Locked\n");
+ }
+ }
+ }
+ if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23) {
+ val = snd_ac97_read(ac97, AC97_INT_PAGING);
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+ AC97_PAGE_MASK, AC97_PAGE_1);
+ snd_ac97_proc_read_functions(ac97, buffer);
+ snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+ AC97_PAGE_MASK, val & AC97_PAGE_MASK);
+ }
+
+
+ __modem:
+ mext = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+ if (mext == 0)
+ return;
+
+ snd_iprintf(buffer, "Extended modem ID: codec=%i%s%s%s%s%s\n",
+ (mext & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT,
+ mext & AC97_MEI_CID2 ? " CID2" : "",
+ mext & AC97_MEI_CID1 ? " CID1" : "",
+ mext & AC97_MEI_HANDSET ? " HSET" : "",
+ mext & AC97_MEI_LINE2 ? " LIN2" : "",
+ mext & AC97_MEI_LINE1 ? " LIN1" : "");
+ val = snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS);
+ snd_iprintf(buffer, "Modem status :%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ val & AC97_MEA_GPIO ? " GPIO" : "",
+ val & AC97_MEA_MREF ? " MREF" : "",
+ val & AC97_MEA_ADC1 ? " ADC1" : "",
+ val & AC97_MEA_DAC1 ? " DAC1" : "",
+ val & AC97_MEA_ADC2 ? " ADC2" : "",
+ val & AC97_MEA_DAC2 ? " DAC2" : "",
+ val & AC97_MEA_HADC ? " HADC" : "",
+ val & AC97_MEA_HDAC ? " HDAC" : "",
+ val & AC97_MEA_PRA ? " PRA(GPIO)" : "",
+ val & AC97_MEA_PRB ? " PRB(res)" : "",
+ val & AC97_MEA_PRC ? " PRC(ADC1)" : "",
+ val & AC97_MEA_PRD ? " PRD(DAC1)" : "",
+ val & AC97_MEA_PRE ? " PRE(ADC2)" : "",
+ val & AC97_MEA_PRF ? " PRF(DAC2)" : "",
+ val & AC97_MEA_PRG ? " PRG(HADC)" : "",
+ val & AC97_MEA_PRH ? " PRH(HDAC)" : "");
+ if (mext & AC97_MEI_LINE1) {
+ val = snd_ac97_read(ac97, AC97_LINE1_RATE);
+ snd_iprintf(buffer, "Line1 rate : %iHz\n", val);
+ }
+ if (mext & AC97_MEI_LINE2) {
+ val = snd_ac97_read(ac97, AC97_LINE2_RATE);
+ snd_iprintf(buffer, "Line2 rate : %iHz\n", val);
+ }
+ if (mext & AC97_MEI_HANDSET) {
+ val = snd_ac97_read(ac97, AC97_HANDSET_RATE);
+ snd_iprintf(buffer, "Headset rate : %iHz\n", val);
+ }
+}
+
+static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ ac97_t *ac97 = entry->private_data;
+
+ down(&ac97->page_mutex);
+ if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86
+ int idx;
+ for (idx = 0; idx < 3; idx++)
+ if (ac97->spec.ad18xx.id[idx]) {
+ /* select single codec */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+ ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]);
+ snd_ac97_proc_read_main(ac97, buffer, idx);
+ snd_iprintf(buffer, "\n\n");
+ }
+ /* select all codecs */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+
+ snd_iprintf(buffer, "\nAD18XX configuration\n");
+ snd_iprintf(buffer, "Unchained : 0x%04x,0x%04x,0x%04x\n",
+ ac97->spec.ad18xx.unchained[0],
+ ac97->spec.ad18xx.unchained[1],
+ ac97->spec.ad18xx.unchained[2]);
+ snd_iprintf(buffer, "Chained : 0x%04x,0x%04x,0x%04x\n",
+ ac97->spec.ad18xx.chained[0],
+ ac97->spec.ad18xx.chained[1],
+ ac97->spec.ad18xx.chained[2]);
+ } else {
+ snd_ac97_proc_read_main(ac97, buffer, 0);
+ }
+ up(&ac97->page_mutex);
+}
+
+#ifdef CONFIG_SND_DEBUG
+/* direct register write for debugging */
+static void snd_ac97_proc_regs_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ ac97_t *ac97 = entry->private_data;
+ char line[64];
+ unsigned int reg, val;
+ down(&ac97->page_mutex);
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x", &reg, &val) != 2)
+ continue;
+ /* register must be even */
+ if (reg < 0x80 && (reg & 1) == 0 && val <= 0xffff)
+ snd_ac97_write_cache(ac97, reg, val);
+ }
+ up(&ac97->page_mutex);
+}
+#endif
+
+static void snd_ac97_proc_regs_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx)
+{
+ int reg, val;
+
+ for (reg = 0; reg < 0x80; reg += 2) {
+ val = snd_ac97_read(ac97, reg);
+ snd_iprintf(buffer, "%i:%02x = %04x\n", subidx, reg, val);
+ }
+}
+
+static void snd_ac97_proc_regs_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ac97_t *ac97 = entry->private_data;
+
+ down(&ac97->page_mutex);
+ if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86
+
+ int idx;
+ for (idx = 0; idx < 3; idx++)
+ if (ac97->spec.ad18xx.id[idx]) {
+ /* select single codec */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+ ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]);
+ snd_ac97_proc_regs_read_main(ac97, buffer, idx);
+ }
+ /* select all codecs */
+ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+ } else {
+ snd_ac97_proc_regs_read_main(ac97, buffer, 0);
+ }
+ up(&ac97->page_mutex);
+}
+
+void snd_ac97_proc_init(ac97_t * ac97)
+{
+ snd_info_entry_t *entry;
+ char name[32];
+ const char *prefix;
+
+ if (ac97->bus->proc == NULL)
+ return;
+ prefix = ac97_is_audio(ac97) ? "ac97" : "mc97";
+ sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num);
+ if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+ snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_read);
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ac97->proc = entry;
+ sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num);
+ if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+ snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_regs_read);
+#ifdef CONFIG_SND_DEBUG
+ entry->mode |= S_IWUSR;
+ entry->c.text.write_size = 1024;
+ entry->c.text.write = snd_ac97_proc_regs_write;
+#endif
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ac97->proc_regs = entry;
+}
+
+void snd_ac97_proc_done(ac97_t * ac97)
+{
+ if (ac97->proc_regs) {
+ snd_info_unregister(ac97->proc_regs);
+ ac97->proc_regs = NULL;
+ }
+ if (ac97->proc) {
+ snd_info_unregister(ac97->proc);
+ ac97->proc = NULL;
+ }
+}
+
+void snd_ac97_bus_proc_init(ac97_bus_t * bus)
+{
+ snd_info_entry_t *entry;
+ char name[32];
+
+ sprintf(name, "codec97#%d", bus->num);
+ if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) {
+ entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ bus->proc = entry;
+}
+
+void snd_ac97_bus_proc_done(ac97_bus_t * bus)
+{
+ if (bus->proc) {
+ snd_info_unregister(bus->proc);
+ bus->proc = NULL;
+ }
+}
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
new file mode 100644
index 00000000000..f9ce0fd2f52
--- /dev/null
+++ b/sound/pci/ac97/ak4531_codec.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Universal routines for AK4531 codec
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ak4531_codec.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Universal routines for AK4531 codec");
+MODULE_LICENSE("GPL");
+
+static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531);
+
+/*
+ *
+ */
+
+#if 0
+
+static void snd_ak4531_dump(ak4531_t *ak4531)
+{
+ int idx;
+
+ for (idx = 0; idx < 0x19; idx++)
+ printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]);
+}
+
+#endif
+
+/*
+ *
+ */
+
+#define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_ak4531_info_single, \
+ .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
+ .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) }
+
+static int snd_ak4531_info_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_ak4531_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 16) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ int val;
+
+ down(&ak4531->reg_mutex);
+ val = (ak4531->regs[reg] >> shift) & mask;
+ up(&ak4531->reg_mutex);
+ if (invert) {
+ val = mask - val;
+ }
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+static int snd_ak4531_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 16) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ int change;
+ int val;
+
+ val = ucontrol->value.integer.value[0] & mask;
+ if (invert) {
+ val = mask - val;
+ }
+ val <<= shift;
+ down(&ak4531->reg_mutex);
+ val = (ak4531->regs[reg] & ~(mask << shift)) | val;
+ change = val != ak4531->regs[reg];
+ ak4531->write(ak4531, reg, ak4531->regs[reg] = val);
+ up(&ak4531->reg_mutex);
+ return change;
+}
+
+#define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_ak4531_info_double, \
+ .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_ak4531_info_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_ak4531_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x07;
+ int right_shift = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ int left, right;
+
+ down(&ak4531->reg_mutex);
+ left = (ak4531->regs[left_reg] >> left_shift) & mask;
+ right = (ak4531->regs[right_reg] >> right_shift) & mask;
+ up(&ak4531->reg_mutex);
+ if (invert) {
+ left = mask - left;
+ right = mask - right;
+ }
+ ucontrol->value.integer.value[0] = left;
+ ucontrol->value.integer.value[1] = right;
+ return 0;
+}
+
+static int snd_ak4531_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x07;
+ int right_shift = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ int change;
+ int left, right;
+
+ left = ucontrol->value.integer.value[0] & mask;
+ right = ucontrol->value.integer.value[1] & mask;
+ if (invert) {
+ left = mask - left;
+ right = mask - right;
+ }
+ left <<= left_shift;
+ right <<= right_shift;
+ down(&ak4531->reg_mutex);
+ if (left_reg == right_reg) {
+ left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right;
+ change = left != ak4531->regs[left_reg];
+ ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
+ } else {
+ left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left;
+ right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right;
+ change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg];
+ ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
+ ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right);
+ }
+ up(&ak4531->reg_mutex);
+ return change;
+}
+
+#define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_ak4531_info_input_sw, \
+ .get = snd_ak4531_get_input_sw, .put = snd_ak4531_put_input_sw, \
+ .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
+
+static int snd_ak4531_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 4;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ak4531_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+ int reg1 = kcontrol->private_value & 0xff;
+ int reg2 = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+ int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+
+ down(&ak4531->reg_mutex);
+ ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1;
+ ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1;
+ ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1;
+ ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1;
+ up(&ak4531->reg_mutex);
+ return 0;
+}
+
+static int snd_ak4531_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+ int reg1 = kcontrol->private_value & 0xff;
+ int reg2 = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+ int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+ int change;
+ int val1, val2;
+
+ down(&ak4531->reg_mutex);
+ val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift));
+ val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift));
+ val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
+ val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
+ val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
+ val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
+ change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2];
+ ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1);
+ ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2);
+ up(&ak4531->reg_mutex);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ak4531_controls[] = {
+
+AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1),
+AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1),
+
+AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1),
+AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1),
+
+AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1),
+AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0),
+AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0),
+
+AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1),
+AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0),
+AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5),
+
+AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1),
+AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0),
+AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1),
+
+AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1),
+AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0),
+AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3),
+
+AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
+AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
+AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
+
+AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
+AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1),
+AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0),
+AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0),
+
+AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1),
+AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1),
+AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0),
+AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0),
+
+AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1),
+AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1),
+AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0),
+AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0),
+
+AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0),
+AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0),
+AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0),
+
+AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0),
+AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0)
+};
+
+static int snd_ak4531_free(ak4531_t *ak4531)
+{
+ if (ak4531) {
+ if (ak4531->private_free)
+ ak4531->private_free(ak4531);
+ kfree(ak4531);
+ }
+ return 0;
+}
+
+static int snd_ak4531_dev_free(snd_device_t *device)
+{
+ ak4531_t *ak4531 = device->device_data;
+ return snd_ak4531_free(ak4531);
+}
+
+static u8 snd_ak4531_initial_map[0x19 + 1] = {
+ 0x9f, /* 00: Master Volume Lch */
+ 0x9f, /* 01: Master Volume Rch */
+ 0x9f, /* 02: Voice Volume Lch */
+ 0x9f, /* 03: Voice Volume Rch */
+ 0x9f, /* 04: FM Volume Lch */
+ 0x9f, /* 05: FM Volume Rch */
+ 0x9f, /* 06: CD Audio Volume Lch */
+ 0x9f, /* 07: CD Audio Volume Rch */
+ 0x9f, /* 08: Line Volume Lch */
+ 0x9f, /* 09: Line Volume Rch */
+ 0x9f, /* 0a: Aux Volume Lch */
+ 0x9f, /* 0b: Aux Volume Rch */
+ 0x9f, /* 0c: Mono1 Volume */
+ 0x9f, /* 0d: Mono2 Volume */
+ 0x9f, /* 0e: Mic Volume */
+ 0x87, /* 0f: Mono-out Volume */
+ 0x00, /* 10: Output Mixer SW1 */
+ 0x00, /* 11: Output Mixer SW2 */
+ 0x00, /* 12: Lch Input Mixer SW1 */
+ 0x00, /* 13: Rch Input Mixer SW1 */
+ 0x00, /* 14: Lch Input Mixer SW2 */
+ 0x00, /* 15: Rch Input Mixer SW2 */
+ 0x00, /* 16: Reset & Power Down */
+ 0x00, /* 17: Clock Select */
+ 0x00, /* 18: AD Input Select */
+ 0x01 /* 19: Mic Amp Setup */
+};
+
+int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531)
+{
+ unsigned int idx;
+ int err;
+ ak4531_t * ak4531;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_ak4531_dev_free,
+ };
+
+ snd_assert(rak4531 != NULL, return -EINVAL);
+ *rak4531 = NULL;
+ snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL);
+ ak4531 = kcalloc(1, sizeof(*ak4531), GFP_KERNEL);
+ if (ak4531 == NULL)
+ return -ENOMEM;
+ *ak4531 = *_ak4531;
+ init_MUTEX(&ak4531->reg_mutex);
+ if ((err = snd_component_add(card, "AK4531")) < 0) {
+ snd_ak4531_free(ak4531);
+ return err;
+ }
+ strcpy(card->mixername, "Asahi Kasei AK4531");
+ ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */
+ udelay(100);
+ ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */
+ for (idx = 0; idx < 0x19; idx++) {
+ if (idx == AK4531_RESET || idx == AK4531_CLOCK)
+ continue;
+ ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */
+ }
+ for (idx = 0; idx < ARRAY_SIZE(snd_ak4531_controls); idx++) {
+ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) {
+ snd_ak4531_free(ak4531);
+ return err;
+ }
+ }
+ snd_ak4531_proc_init(card, ak4531);
+ if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops)) < 0) {
+ snd_ak4531_free(ak4531);
+ return err;
+ }
+
+#if 0
+ snd_ak4531_dump(ak4531);
+#endif
+ *rak4531 = ak4531;
+ return 0;
+}
+
+/*
+
+ */
+
+static void snd_ak4531_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ak4531_t *ak4531 = entry->private_data;
+
+ snd_iprintf(buffer, "Asahi Kasei AK4531\n\n");
+ snd_iprintf(buffer, "Recording source : %s\n"
+ "MIC gain : %s\n",
+ ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer",
+ ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
+}
+
+static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(card, "ak4531", &entry))
+ snd_info_set_text_ops(entry, ak4531, 1024, snd_ak4531_proc_read);
+}
+
+EXPORT_SYMBOL(snd_ak4531_mixer);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_ak4531_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_ak4531_exit(void)
+{
+}
+
+module_init(alsa_ak4531_init)
+module_exit(alsa_ak4531_exit)
diff --git a/sound/pci/ali5451/Makefile b/sound/pci/ali5451/Makefile
new file mode 100644
index 00000000000..2e183159747
--- /dev/null
+++ b/sound/pci/ali5451/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-ali5451-objs := ali5451.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
new file mode 100644
index 00000000000..984d5d4ba4e
--- /dev/null
+++ b/sound/pci/ali5451/ali5451.c
@@ -0,0 +1,2282 @@
+/*
+ * Matt Wu <Matt_Wu@acersoftech.com.cn>
+ * Apr 26, 2001
+ * Routines for control of ALi pci audio M5451
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public Lcodecnse as published by
+ * the Free Software Foundation; either version 2 of the Lcodecnse, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public Lcodecnse for more details.
+ *
+ * You should have received a copy of the GNU General Public Lcodecnse
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");
+MODULE_DESCRIPTION("ALI M5451");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32};
+static int spdif[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ALI 5451 PCI Audio.");
+module_param_array(pcm_channels, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_channels, "PCM Channels");
+module_param_array(spdif, bool, NULL, 0444);
+MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
+
+/*
+ * Debug part definitions
+ */
+
+//#define ALI_DEBUG
+
+#ifdef ALI_DEBUG
+#define snd_ali_printk(format, args...) printk(format, ##args);
+#else
+#define snd_ali_printk(format, args...)
+#endif
+
+/*
+ * Constants definition
+ */
+
+#ifndef PCI_VENDOR_ID_ALI
+#define PCI_VENDOR_ID_ALI 0x10b9
+#endif
+
+#ifndef PCI_DEVICE_ID_ALI_5451
+#define PCI_DEVICE_ID_ALI_5451 0x5451
+#endif
+
+#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_ALI<<16)|PCI_DEVICE_ID_ALI_5451)
+
+
+#define ALI_CHANNELS 32
+
+#define ALI_PCM_IN_CHANNEL 31
+#define ALI_SPDIF_IN_CHANNEL 19
+#define ALI_SPDIF_OUT_CHANNEL 15
+#define ALI_CENTER_CHANNEL 24
+#define ALI_LEF_CHANNEL 23
+#define ALI_SURR_LEFT_CHANNEL 26
+#define ALI_SURR_RIGHT_CHANNEL 25
+
+#define SNDRV_ALI_VOICE_TYPE_PCM 01
+#define SNDRV_ALI_VOICE_TYPE_OTH 02
+
+#define ALI_5451_V02 0x02
+
+/*
+ * Direct Registers
+ */
+
+#define ALI_LEGACY_DMAR0 0x00 // ADR0
+#define ALI_LEGACY_DMAR4 0x04 // CNT0
+#define ALI_LEGACY_DMAR11 0x0b // MOD
+#define ALI_LEGACY_DMAR15 0x0f // MMR
+#define ALI_MPUR0 0x20
+#define ALI_MPUR1 0x21
+#define ALI_MPUR2 0x22
+#define ALI_MPUR3 0x23
+
+#define ALI_AC97_WRITE 0x40
+#define ALI_AC97_READ 0x44
+
+#define ALI_SCTRL 0x48
+#define ALI_SPDIF_OUT_ENABLE 0x20
+#define ALI_AC97_GPIO 0x4c
+#define ALI_SPDIF_CS 0x70
+#define ALI_SPDIF_CTRL 0x74
+#define ALI_SPDIF_IN_FUNC_ENABLE 0x02
+#define ALI_SPDIF_IN_CH_STATUS 0x40
+#define ALI_SPDIF_OUT_CH_STATUS 0xbf
+#define ALI_START 0x80
+#define ALI_STOP 0x84
+#define ALI_CSPF 0x90
+#define ALI_AINT 0x98
+#define ALI_GC_CIR 0xa0
+ #define ENDLP_IE 0x00001000
+ #define MIDLP_IE 0x00002000
+#define ALI_AINTEN 0xa4
+#define ALI_VOLUME 0xa8
+#define ALI_SBDELTA_DELTA_R 0xac
+#define ALI_MISCINT 0xb0
+ #define ADDRESS_IRQ 0x00000020
+ #define TARGET_REACHED 0x00008000
+ #define MIXER_OVERFLOW 0x00000800
+ #define MIXER_UNDERFLOW 0x00000400
+#define ALI_SBBL_SBCL 0xc0
+#define ALI_SBCTRL_SBE2R_SBDD 0xc4
+#define ALI_STIMER 0xc8
+#define ALI_GLOBAL_CONTROL 0xd4
+#define ALI_SPDIF_OUT_SEL_PCM 0x00000400 /* bit 10 */
+#define ALI_SPDIF_IN_SUPPORT 0x00000800 /* bit 11 */
+#define ALI_SPDIF_OUT_CH_ENABLE 0x00008000 /* bit 15 */
+#define ALI_SPDIF_IN_CH_ENABLE 0x00080000 /* bit 19 */
+#define ALI_PCM_IN_ENABLE 0x80000000 /* bit 31 */
+
+#define ALI_CSO_ALPHA_FMS 0xe0
+#define ALI_LBA 0xe4
+#define ALI_ESO_DELTA 0xe8
+#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0
+#define ALI_EBUF1 0xf4
+#define ALI_EBUF2 0xf8
+
+#define ALI_REG(codec, x) ((codec)->port + x)
+
+typedef struct snd_stru_ali ali_t;
+typedef struct snd_ali_stru_voice snd_ali_voice_t;
+
+typedef struct snd_ali_channel_control {
+ // register data
+ struct REGDATA {
+ unsigned int start;
+ unsigned int stop;
+ unsigned int aint;
+ unsigned int ainten;
+ } data;
+
+ // register addresses
+ struct REGS {
+ unsigned int start;
+ unsigned int stop;
+ unsigned int aint;
+ unsigned int ainten;
+ unsigned int ac97read;
+ unsigned int ac97write;
+ } regs;
+
+} snd_ali_channel_control_t;
+
+struct snd_ali_stru_voice {
+ unsigned int number;
+ unsigned int use: 1,
+ pcm: 1,
+ midi: 1,
+ mode: 1,
+ synth: 1;
+
+ /* PCM data */
+ ali_t *codec;
+ snd_pcm_substream_t *substream;
+ snd_ali_voice_t *extra;
+
+ unsigned int running: 1;
+
+ int eso; /* final ESO value for channel */
+ int count; /* runtime->period_size */
+
+ /* --- */
+
+ void *private_data;
+ void (*private_free)(void *private_data);
+};
+
+
+typedef struct snd_stru_alidev {
+
+ snd_ali_voice_t voices[ALI_CHANNELS];
+
+ unsigned int chcnt; /* num of opened channels */
+ unsigned int chmap; /* bitmap for opened channels */
+ unsigned int synthcount;
+
+} alidev_t;
+
+
+#ifdef CONFIG_PM
+#define ALI_GLOBAL_REGS 56
+#define ALI_CHANNEL_REGS 8
+typedef struct snd_ali_image {
+ unsigned long regs[ALI_GLOBAL_REGS];
+ unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
+} ali_image_t;
+#endif
+
+
+struct snd_stru_ali {
+ unsigned long irq;
+ unsigned long port;
+ unsigned char revision;
+
+ unsigned int hw_initialized: 1;
+ unsigned int spdif_support: 1;
+
+ struct pci_dev *pci;
+ struct pci_dev *pci_m1533;
+ struct pci_dev *pci_m7101;
+
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ alidev_t synth;
+ snd_ali_channel_control_t chregs;
+
+ /* S/PDIF Mask */
+ unsigned int spdif_mask;
+
+ unsigned int spurious_irq_count;
+ unsigned int spurious_irq_max_delta;
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97;
+ unsigned short ac97_ext_id;
+ unsigned short ac97_ext_status;
+
+ spinlock_t reg_lock;
+ spinlock_t voice_alloc;
+
+#ifdef CONFIG_PM
+ ali_image_t *image;
+#endif
+};
+
+static struct pci_device_id snd_ali_ids[] = {
+ {0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ {0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_ali_ids);
+
+static void snd_ali_clear_voices(ali_t *, unsigned int, unsigned int);
+static unsigned short snd_ali_codec_peek(ali_t *, int, unsigned short);
+static void snd_ali_codec_poke(ali_t *, int, unsigned short, unsigned short);
+
+/*
+ * Debug Part
+ */
+
+#ifdef ALI_DEBUG
+
+static void ali_read_regs(ali_t *codec, int channel)
+{
+ int i,j;
+ unsigned int dwVal;
+
+ printk("channel %d registers map:\n", channel);
+ outb((unsigned char)(channel & 0x001f), ALI_REG(codec,ALI_GC_CIR));
+
+ printk(" ");
+ for(j=0;j<8;j++)
+ printk("%2.2x ", j*4);
+ printk("\n");
+
+ for (i=0; i<=0xf8/4;i++) {
+ if(i%8 == 0)
+ printk("%2.2x ", (i*4/0x10)*0x10);
+ dwVal = inl(ALI_REG(codec,i*4));
+ printk("%8.8x ", dwVal);
+ if ((i+1)%8 == 0)
+ printk("\n");
+ }
+ printk("\n");
+}
+static void ali_read_cfg(unsigned int vendor, unsigned deviceid)
+{
+ unsigned int dwVal;
+ struct pci_dev *pci_dev = NULL;
+ int i,j;
+
+
+ pci_dev = pci_find_device(vendor, deviceid, pci_dev);
+ if (pci_dev == NULL)
+ return ;
+
+ printk("\nM%x PCI CFG\n", deviceid);
+ printk(" ");
+ for(j=0;j<8;j++)
+ printk("%d ",j);
+ printk("\n");
+
+ for(i=0;i<8;i++) {
+ printk("%d ",i);
+ for(j=0;j<8;j++)
+ {
+ pci_read_config_dword(pci_dev, i*0x20+j*4, &dwVal);
+ printk("%8.8x ", dwVal);
+ }
+ printk("\n");
+ }
+ }
+static void ali_read_ac97regs(ali_t *codec, int secondary)
+{
+ unsigned short i,j;
+ unsigned short wVal;
+
+ printk("\ncodec %d registers map:\n", secondary);
+
+ printk(" ");
+ for(j=0;j<8;j++)
+ printk("%2.2x ",j*2);
+ printk("\n");
+
+ for (i=0; i<64;i++) {
+ if(i%8 == 0)
+ printk("%2.2x ", (i/8)*0x10);
+ wVal = snd_ali_codec_peek(codec, secondary, i*2);
+ printk("%4.4x ", wVal);
+ if ((i+1)%8 == 0)
+ printk("\n");
+ }
+ printk("\n");
+}
+
+#endif
+
+/*
+ * AC97 ACCESS
+ */
+
+static inline unsigned int snd_ali_5451_peek(ali_t *codec,
+ unsigned int port )
+{
+ return (unsigned int)inl(ALI_REG(codec, port));
+}
+
+static inline void snd_ali_5451_poke( ali_t *codec,
+ unsigned int port,
+ unsigned int val )
+{
+ outl((unsigned int)val, ALI_REG(codec, port));
+}
+
+static int snd_ali_codec_ready( ali_t *codec,
+ unsigned int port,
+ int sched )
+{
+ unsigned long end_time;
+ unsigned int res;
+
+ end_time = jiffies + 10 * (HZ >> 2);
+ do {
+ res = snd_ali_5451_peek(codec,port);
+ if (! (res & 0x8000))
+ return 0;
+ if (sched) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ } while (time_after_eq(end_time, jiffies));
+ snd_ali_5451_poke(codec, port, res & ~0x8000);
+ snd_printdd("ali_codec_ready: codec is not ready.\n ");
+ return -EIO;
+}
+
+static int snd_ali_stimer_ready(ali_t *codec, int sched)
+{
+ unsigned long end_time;
+ unsigned long dwChk1,dwChk2;
+
+ dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER);
+ dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+
+ end_time = jiffies + 10 * (HZ >> 2);
+ do {
+ dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+ if (dwChk2 != dwChk1)
+ return 0;
+ if (sched) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk("ali_stimer_read: stimer is not ready.\n");
+ return -EIO;
+}
+
+static void snd_ali_codec_poke(ali_t *codec,int secondary,
+ unsigned short reg,
+ unsigned short val)
+{
+ unsigned int dwVal = 0;
+ unsigned int port = 0;
+
+ if (reg >= 0x80) {
+ snd_printk("ali_codec_poke: reg(%xh) invalid.\n", reg);
+ return;
+ }
+
+ port = codec->chregs.regs.ac97write;
+
+ if (snd_ali_codec_ready(codec, port, 0) < 0)
+ return;
+ if (snd_ali_stimer_ready(codec, 0) < 0)
+ return;
+
+ dwVal = (unsigned int) (reg & 0xff);
+ dwVal |= 0x8000 | (val << 16);
+ if (secondary) dwVal |= 0x0080;
+ if (codec->revision == ALI_5451_V02) dwVal |= 0x0100;
+
+ snd_ali_5451_poke(codec,port,dwVal);
+
+ return ;
+}
+
+static unsigned short snd_ali_codec_peek( ali_t *codec,
+ int secondary,
+ unsigned short reg)
+{
+ unsigned int dwVal = 0;
+ unsigned int port = 0;
+
+ if (reg >= 0x80) {
+ snd_printk("ali_codec_peek: reg(%xh) invalid.\n", reg);
+ return ~0;
+ }
+
+ port = codec->chregs.regs.ac97read;
+
+ if (snd_ali_codec_ready(codec, port, 0) < 0)
+ return ~0;
+ if (snd_ali_stimer_ready(codec, 0) < 0)
+ return ~0;
+
+ dwVal = (unsigned int) (reg & 0xff);
+ dwVal |= 0x8000; /* bit 15*/
+ if (secondary) dwVal |= 0x0080;
+
+ snd_ali_5451_poke(codec, port, dwVal);
+
+ if (snd_ali_stimer_ready(codec, 0) < 0)
+ return ~0;
+ if (snd_ali_codec_ready(codec, port, 0) < 0)
+ return ~0;
+
+ return (snd_ali_5451_peek(codec, port) & 0xffff0000)>>16;
+}
+
+static void snd_ali_codec_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val )
+{
+ ali_t *codec = ac97->private_data;
+
+ snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val);
+ snd_ali_codec_poke(codec, 0, reg, val);
+ return ;
+}
+
+
+static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg)
+{
+ ali_t *codec = ac97->private_data;
+
+ snd_ali_printk("codec_read reg=%xh.\n", reg);
+ return (snd_ali_codec_peek(codec, 0, reg));
+}
+
+/*
+ * AC97 Reset
+ */
+
+static int snd_ali_reset_5451(ali_t *codec)
+{
+ struct pci_dev *pci_dev = NULL;
+ unsigned short wCount, wReg;
+ unsigned int dwVal;
+
+ if ((pci_dev = codec->pci_m1533) != NULL) {
+ pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+ pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+ udelay(5000);
+ pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+ pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+ udelay(5000);
+ }
+
+ pci_dev = codec->pci;
+ pci_read_config_dword(pci_dev, 0x44, &dwVal);
+ pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
+ udelay(500);
+ pci_read_config_dword(pci_dev, 0x44, &dwVal);
+ pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
+ udelay(5000);
+
+ wCount = 200;
+ while(wCount--) {
+ wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN);
+ if((wReg & 0x000f) == 0x000f)
+ return 0;
+ udelay(5000);
+ }
+
+ /* non-fatal if you have a non PM capable codec */
+ /* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */
+ return 0;
+}
+
+#ifdef CODEC_RESET
+
+static int snd_ali_reset_codec(ali_t *codec)
+{
+ struct pci_dev *pci_dev = NULL;
+ unsigned char bVal = 0;
+ unsigned int dwVal;
+ unsigned short wCount, wReg;
+
+ pci_dev = codec->pci_m1533;
+
+ pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+ pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+ udelay(5000);
+ pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+ pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+ udelay(5000);
+
+ bVal = inb(ALI_REG(codec,ALI_SCTRL));
+ bVal |= 0x02;
+ outb(ALI_REG(codec,ALI_SCTRL),bVal);
+ udelay(5000);
+ bVal = inb(ALI_REG(codec,ALI_SCTRL));
+ bVal &= 0xfd;
+ outb(ALI_REG(codec,ALI_SCTRL),bVal);
+ udelay(15000);
+
+ wCount = 200;
+ while(wCount--) {
+ wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN);
+ if((wReg & 0x000f) == 0x000f)
+ return 0;
+ udelay(5000);
+ }
+ return -1;
+}
+
+#endif
+
+/*
+ * ALI 5451 Controller
+ */
+
+static void snd_ali_enable_special_channel(ali_t *codec, unsigned int channel)
+{
+ unsigned long dwVal = 0;
+
+ dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL));
+ dwVal |= 1 << (channel & 0x0000001f);
+ outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_disable_special_channel(ali_t *codec, unsigned int channel)
+{
+ unsigned long dwVal = 0;
+
+ dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL));
+ dwVal &= ~(1 << (channel & 0x0000001f));
+ outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_enable_address_interrupt(ali_t * codec)
+{
+ unsigned int gc;
+
+ gc = inl(ALI_REG(codec, ALI_GC_CIR));
+ gc |= ENDLP_IE;
+ gc |= MIDLP_IE;
+ outl( gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+static void snd_ali_disable_address_interrupt(ali_t * codec)
+{
+ unsigned int gc;
+
+ gc = inl(ALI_REG(codec, ALI_GC_CIR));
+ gc &= ~ENDLP_IE;
+ gc &= ~MIDLP_IE;
+ outl(gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+#if 0 // not used
+static void snd_ali_enable_voice_irq(ali_t *codec, unsigned int channel)
+{
+ unsigned int mask;
+ snd_ali_channel_control_t *pchregs = &(codec->chregs);
+
+ snd_ali_printk("enable_voice_irq channel=%d\n",channel);
+
+ mask = 1 << (channel & 0x1f);
+ pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten));
+ pchregs->data.ainten |= mask;
+ outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten));
+}
+#endif
+
+static void snd_ali_disable_voice_irq(ali_t *codec, unsigned int channel)
+{
+ unsigned int mask;
+ snd_ali_channel_control_t *pchregs = &(codec->chregs);
+
+ snd_ali_printk("disable_voice_irq channel=%d\n",channel);
+
+ mask = 1 << (channel & 0x1f);
+ pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten));
+ pchregs->data.ainten &= ~mask;
+ outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten));
+}
+
+static int snd_ali_alloc_pcm_channel(ali_t *codec, int channel)
+{
+ unsigned int idx = channel & 0x1f;
+
+ if (codec->synth.chcnt >= ALI_CHANNELS){
+ snd_printk("ali_alloc_pcm_channel: no free channels.\n");
+ return -1;
+ }
+
+ if (!(codec->synth.chmap & (1 << idx))) {
+ codec->synth.chmap |= 1 << idx;
+ codec->synth.chcnt++;
+ snd_ali_printk("alloc_pcm_channel no. %d.\n",idx);
+ return idx;
+ }
+ return -1;
+}
+
+static int snd_ali_find_free_channel(ali_t * codec, int rec)
+{
+ int idx;
+ int result = -1;
+
+ snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm");
+
+ // recording
+ if (rec) {
+ if (codec->spdif_support &&
+ (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT))
+ idx = ALI_SPDIF_IN_CHANNEL;
+ else
+ idx = ALI_PCM_IN_CHANNEL;
+
+ if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) {
+ return result;
+ } else {
+ snd_printk("ali_find_free_channel: record channel is busy now.\n");
+ return -1;
+ }
+ }
+
+ //playback...
+ if (codec->spdif_support &&
+ (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE)) {
+ idx = ALI_SPDIF_OUT_CHANNEL;
+ if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) {
+ return result;
+ } else {
+ snd_printk("ali_find_free_channel: S/PDIF out channel is in busy now.\n");
+ }
+ }
+
+ for (idx = 0; idx < ALI_CHANNELS; idx++) {
+ if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0)
+ return result;
+ }
+ snd_printk("ali_find_free_channel: no free channels.\n");
+ return -1;
+}
+
+static void snd_ali_free_channel_pcm(ali_t *codec, int channel)
+{
+ unsigned int idx = channel & 0x0000001f;
+
+ snd_ali_printk("free_channel_pcm channel=%d\n",channel);
+
+ if (channel < 0 || channel >= ALI_CHANNELS)
+ return;
+
+ if (!(codec->synth.chmap & (1 << idx))) {
+ snd_printk("ali_free_channel_pcm: channel %d is not in use.\n",channel);
+ return;
+ } else {
+ codec->synth.chmap &= ~(1 << idx);
+ codec->synth.chcnt--;
+ }
+}
+
+#if 0 // not used
+static void snd_ali_start_voice(ali_t * codec, unsigned int channel)
+{
+ unsigned int mask = 1 << (channel & 0x1f);
+
+ snd_ali_printk("start_voice: channel=%d\n",channel);
+ outl(mask, ALI_REG(codec,codec->chregs.regs.start));
+}
+#endif
+
+static void snd_ali_stop_voice(ali_t * codec, unsigned int channel)
+{
+ unsigned int mask = 1 << (channel & 0x1f);
+
+ snd_ali_printk("stop_voice: channel=%d\n",channel);
+ outl(mask, ALI_REG(codec, codec->chregs.regs.stop));
+}
+
+/*
+ * S/PDIF Part
+ */
+
+static void snd_ali_delay(ali_t *codec,int interval)
+{
+ unsigned long begintimer,currenttimer;
+
+ begintimer = inl(ALI_REG(codec, ALI_STIMER));
+ currenttimer = inl(ALI_REG(codec, ALI_STIMER));
+
+ while (currenttimer < begintimer + interval) {
+ if(snd_ali_stimer_ready(codec, 1) < 0)
+ break;
+ currenttimer = inl(ALI_REG(codec, ALI_STIMER));
+ }
+}
+
+static void snd_ali_detect_spdif_rate(ali_t *codec)
+{
+ u16 wval = 0;
+ u16 count = 0;
+ u8 bval = 0, R1 = 0, R2 = 0;
+
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+ bval |= 0x1F;
+ outb(bval,ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+
+ while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) {
+ count ++;
+ snd_ali_delay(codec, 6);
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+ R1 = bval & 0x1F;
+ }
+
+ if (count > 50000) {
+ snd_printk("ali_detect_spdif_rate: timeout!\n");
+ return;
+ }
+
+ count = 0;
+ while (count++ <= 50000) {
+ snd_ali_delay(codec, 6);
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+ R2 = bval & 0x1F;
+ if (R2 != R1) R1 = R2; else break;
+ }
+
+ if (count > 50000) {
+ snd_printk("ali_detect_spdif_rate: timeout!\n");
+ return;
+ }
+
+ if (R2 >= 0x0b && R2 <= 0x0e) {
+ wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+ wval &= 0xE0F0;
+ wval |= (u16)0x09 << 8 | (u16)0x05;
+ outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0;
+ outb(bval|0x02,ALI_REG(codec,ALI_SPDIF_CS + 3));
+ } else if (R2 == 0x12) {
+ wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+ wval &= 0xE0F0;
+ wval |= (u16)0x0E << 8 | (u16)0x08;
+ outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0;
+ outb(bval|0x03,ALI_REG(codec,ALI_SPDIF_CS + 3));
+ }
+}
+
+static unsigned int snd_ali_get_spdif_in_rate(ali_t *codec)
+{
+ u32 dwRate = 0;
+ u8 bval = 0;
+
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
+ bval &= 0x7F;
+ bval |= 0x40;
+ outb(bval, ALI_REG(codec,ALI_SPDIF_CTRL));
+
+ snd_ali_detect_spdif_rate(codec);
+
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CS + 3));
+ bval &= 0x0F;
+
+ if (bval == 0) dwRate = 44100;
+ if (bval == 1) dwRate = 48000;
+ if (bval == 2) dwRate = 32000;
+
+ return dwRate;
+}
+
+static void snd_ali_enable_spdif_in(ali_t *codec)
+{
+ unsigned int dwVal;
+
+ dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+ dwVal |= ALI_SPDIF_IN_SUPPORT;
+ outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+ dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+ dwVal |= 0x02;
+ outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+
+ snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_in(ali_t *codec)
+{
+ unsigned int dwVal;
+
+ dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+ dwVal &= ~ALI_SPDIF_IN_SUPPORT;
+ outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+ snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
+}
+
+
+static void snd_ali_set_spdif_out_rate(ali_t *codec, unsigned int rate)
+{
+ unsigned char bVal;
+ unsigned int dwRate = 0;
+
+ if (rate == 32000) dwRate = 0x300;
+ if (rate == 44100) dwRate = 0;
+ if (rate == 48000) dwRate = 0x200;
+
+ bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+ bVal &= (unsigned char)(~(1<<6));
+
+ bVal |= 0x80; //select right
+ outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+ outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2));
+
+ bVal &= (~0x80); //select left
+ outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+ outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2));
+}
+
+static void snd_ali_enable_spdif_out(ali_t *codec)
+{
+ unsigned short wVal;
+ unsigned char bVal;
+
+ struct pci_dev *pci_dev = NULL;
+
+ pci_dev = codec->pci_m1533;
+ if (pci_dev == NULL)
+ return;
+ pci_read_config_byte(pci_dev, 0x61, &bVal);
+ bVal |= 0x40;
+ pci_write_config_byte(pci_dev, 0x61, bVal);
+ pci_read_config_byte(pci_dev, 0x7d, &bVal);
+ bVal |= 0x01;
+ pci_write_config_byte(pci_dev, 0x7d, bVal);
+
+ pci_read_config_byte(pci_dev, 0x7e, &bVal);
+ bVal &= (~0x20);
+ bVal |= 0x10;
+ pci_write_config_byte(pci_dev, 0x7e, bVal);
+
+ bVal = inb(ALI_REG(codec, ALI_SCTRL));
+ outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
+
+ bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+ outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL));
+
+ {
+ wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+ wVal |= ALI_SPDIF_OUT_SEL_PCM;
+ outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+ snd_ali_disable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL);
+ }
+}
+
+static void snd_ali_enable_spdif_chnout(ali_t *codec)
+{
+ unsigned short wVal = 0;
+
+ wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+ wVal &= ~ALI_SPDIF_OUT_SEL_PCM;
+ outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+/*
+ wVal = inw(ALI_REG(codec, ALI_SPDIF_CS));
+ if (flag & ALI_SPDIF_OUT_NON_PCM)
+ wVal |= 0x0002;
+ else
+ wVal &= (~0x0002);
+ outw(wVal, ALI_REG(codec, ALI_SPDIF_CS));
+*/
+ snd_ali_enable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_chnout(ali_t *codec)
+{
+ unsigned short wVal = 0;
+ wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+ wVal |= ALI_SPDIF_OUT_SEL_PCM;
+ outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+ snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_out(ali_t *codec)
+{
+ unsigned char bVal;
+
+ bVal = inb(ALI_REG(codec, ALI_SCTRL));
+ outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
+
+ snd_ali_disable_spdif_chnout(codec);
+}
+
+static void snd_ali_update_ptr(ali_t *codec,int channel)
+{
+ snd_ali_voice_t *pvoice = NULL;
+ snd_pcm_runtime_t *runtime;
+ snd_ali_channel_control_t *pchregs = NULL;
+ unsigned int old, mask;
+#ifdef ALI_DEBUG
+ unsigned int temp, cspf;
+#endif
+
+ pchregs = &(codec->chregs);
+
+ // check if interrupt occurred for channel
+ old = pchregs->data.aint;
+ mask = ((unsigned int) 1L) << (channel & 0x1f);
+
+ if (!(old & mask))
+ return;
+
+ pvoice = &codec->synth.voices[channel];
+ runtime = pvoice->substream->runtime;
+
+ udelay(100);
+ spin_lock(&codec->reg_lock);
+
+ if (pvoice->pcm && pvoice->substream) {
+ /* pcm interrupt */
+#ifdef ALI_DEBUG
+ outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR));
+ temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+ cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask;
+#endif
+ if (pvoice->running) {
+ snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",(u16)temp,cspf);
+ spin_unlock(&codec->reg_lock);
+ snd_pcm_period_elapsed(pvoice->substream);
+ spin_lock(&codec->reg_lock);
+ } else {
+ snd_ali_stop_voice(codec, channel);
+ snd_ali_disable_voice_irq(codec, channel);
+ }
+ } else if (codec->synth.voices[channel].synth) {
+ /* synth interrupt */
+ } else if (codec->synth.voices[channel].midi) {
+ /* midi interrupt */
+ } else {
+ /* unknown interrupt */
+ snd_ali_stop_voice(codec, channel);
+ snd_ali_disable_voice_irq(codec, channel);
+ }
+ spin_unlock(&codec->reg_lock);
+ outl(mask,ALI_REG(codec,pchregs->regs.aint));
+ pchregs->data.aint = old & (~mask);
+}
+
+static void snd_ali_interrupt(ali_t * codec)
+{
+ int channel;
+ unsigned int audio_int;
+ snd_ali_channel_control_t *pchregs = NULL;
+ pchregs = &(codec->chregs);
+
+ audio_int = inl(ALI_REG(codec, ALI_MISCINT));
+ if (audio_int & ADDRESS_IRQ) {
+ // get interrupt status for all channels
+ pchregs->data.aint = inl(ALI_REG(codec,pchregs->regs.aint));
+ for (channel = 0; channel < ALI_CHANNELS; channel++) {
+ snd_ali_update_ptr(codec, channel);
+ }
+ }
+ outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
+ ALI_REG(codec,ALI_MISCINT));
+}
+
+
+static irqreturn_t snd_ali_card_interrupt(int irq,
+ void *dev_id,
+ struct pt_regs *regs)
+{
+ ali_t *codec = dev_id;
+
+ if (codec == NULL)
+ return IRQ_NONE;
+ snd_ali_interrupt(codec);
+ return IRQ_HANDLED;
+}
+
+
+static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec)
+{
+ snd_ali_voice_t *pvoice = NULL;
+ unsigned long flags;
+ int idx;
+
+ snd_ali_printk("alloc_voice: type=%d rec=%d\n",type,rec);
+
+ spin_lock_irqsave(&codec->voice_alloc, flags);
+ if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
+ idx = snd_ali_find_free_channel(codec,rec);
+ if(idx < 0) {
+ snd_printk("ali_alloc_voice: err.\n");
+ spin_unlock_irqrestore(&codec->voice_alloc, flags);
+ return NULL;
+ }
+ pvoice = &(codec->synth.voices[idx]);
+ pvoice->use = 1;
+ pvoice->pcm = 1;
+ pvoice->mode = rec;
+ spin_unlock_irqrestore(&codec->voice_alloc, flags);
+ return pvoice;
+ }
+ spin_unlock_irqrestore(&codec->voice_alloc, flags);
+ return NULL;
+}
+
+
+static void snd_ali_free_voice(ali_t * codec, snd_ali_voice_t *pvoice)
+{
+ unsigned long flags;
+ void (*private_free)(void *);
+ void *private_data;
+
+ snd_ali_printk("free_voice: channel=%d\n",pvoice->number);
+ if (pvoice == NULL || !pvoice->use)
+ return;
+ snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
+ spin_lock_irqsave(&codec->voice_alloc, flags);
+ private_free = pvoice->private_free;
+ private_data = pvoice->private_data;
+ pvoice->private_free = NULL;
+ pvoice->private_data = NULL;
+ if (pvoice->pcm) {
+ snd_ali_free_channel_pcm(codec, pvoice->number);
+ }
+ pvoice->use = pvoice->pcm = pvoice->synth = 0;
+ pvoice->substream = NULL;
+ spin_unlock_irqrestore(&codec->voice_alloc, flags);
+ if (private_free)
+ private_free(private_data);
+}
+
+
+static void snd_ali_clear_voices(ali_t * codec,
+ unsigned int v_min,
+ unsigned int v_max)
+{
+ unsigned int i;
+
+ for (i = v_min; i <= v_max; i++) {
+ snd_ali_stop_voice(codec, i);
+ snd_ali_disable_voice_irq(codec, i);
+ }
+}
+
+static void snd_ali_write_voice_regs(ali_t * codec,
+ unsigned int Channel,
+ unsigned int LBA,
+ unsigned int CSO,
+ unsigned int ESO,
+ unsigned int DELTA,
+ unsigned int ALPHA_FMS,
+ unsigned int GVSEL,
+ unsigned int PAN,
+ unsigned int VOL,
+ unsigned int CTRL,
+ unsigned int EC)
+{
+ unsigned int ctlcmds[4];
+
+ outb((unsigned char)(Channel & 0x001f),ALI_REG(codec,ALI_GC_CIR));
+
+ ctlcmds[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff);
+ ctlcmds[1] = LBA;
+ ctlcmds[2] = (ESO << 16) | (DELTA & 0x0ffff);
+ ctlcmds[3] = (GVSEL << 31) |
+ ((PAN & 0x0000007f) << 24) |
+ ((VOL & 0x000000ff) << 16) |
+ ((CTRL & 0x0000000f) << 12) |
+ (EC & 0x00000fff);
+
+ outb(Channel, ALI_REG(codec, ALI_GC_CIR));
+
+ outl(ctlcmds[0], ALI_REG(codec,ALI_CSO_ALPHA_FMS));
+ outl(ctlcmds[1], ALI_REG(codec,ALI_LBA));
+ outl(ctlcmds[2], ALI_REG(codec,ALI_ESO_DELTA));
+ outl(ctlcmds[3], ALI_REG(codec,ALI_GVSEL_PAN_VOC_CTRL_EC));
+
+ outl(0x30000000, ALI_REG(codec, ALI_EBUF1)); /* Still Mode */
+ outl(0x30000000, ALI_REG(codec, ALI_EBUF2)); /* Still Mode */
+}
+
+static unsigned int snd_ali_convert_rate(unsigned int rate, int rec)
+{
+ unsigned int delta;
+
+ if (rate < 4000) rate = 4000;
+ if (rate > 48000) rate = 48000;
+
+ if (rec) {
+ if (rate == 44100)
+ delta = 0x116a;
+ else if (rate == 8000)
+ delta = 0x6000;
+ else if (rate == 48000)
+ delta = 0x1000;
+ else
+ delta = ((48000 << 12) / rate) & 0x0000ffff;
+ } else {
+ if (rate == 44100)
+ delta = 0xeb3;
+ else if (rate == 8000)
+ delta = 0x2ab;
+ else if (rate == 48000)
+ delta = 0x1000;
+ else
+ delta = (((rate << 12) + rate) / 48000) & 0x0000ffff;
+ }
+
+ return delta;
+}
+
+static unsigned int snd_ali_control_mode(snd_pcm_substream_t *substream)
+{
+ unsigned int CTRL;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ /* set ctrl mode
+ CTRL default: 8-bit (unsigned) mono, loop mode enabled
+ */
+ CTRL = 0x00000001;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ CTRL |= 0x00000008; // 16-bit data
+ if (!snd_pcm_format_unsigned(runtime->format))
+ CTRL |= 0x00000002; // signed data
+ if (runtime->channels > 1)
+ CTRL |= 0x00000004; // stereo data
+ return CTRL;
+}
+
+/*
+ * PCM part
+ */
+
+static int snd_ali_ioctl(snd_pcm_substream_t * substream,
+ unsigned int cmd, void *arg)
+{
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_ali_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ unsigned int what, whati, capture_flag;
+ snd_ali_voice_t *pvoice = NULL, *evoice = NULL;
+ unsigned int val;
+ int do_start;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ do_start = 1; break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ do_start = 0; break;
+ default:
+ return -EINVAL;
+ }
+
+ what = whati = capture_flag = 0;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if ((ali_t *) snd_pcm_substream_chip(s) == codec) {
+ pvoice = (snd_ali_voice_t *) s->runtime->private_data;
+ evoice = pvoice->extra;
+ what |= 1 << (pvoice->number & 0x1f);
+ if (evoice == NULL) {
+ whati |= 1 << (pvoice->number & 0x1f);
+ } else {
+ whati |= 1 << (evoice->number & 0x1f);
+ what |= 1 << (evoice->number & 0x1f);
+ }
+ if (do_start) {
+ pvoice->running = 1;
+ if (evoice != NULL)
+ evoice->running = 1;
+ } else {
+ pvoice->running = 0;
+ if (evoice != NULL)
+ evoice->running = 0;
+ }
+ snd_pcm_trigger_done(s, substream);
+ if (pvoice->mode)
+ capture_flag = 1;
+ }
+ }
+ spin_lock(&codec->reg_lock);
+ if (! do_start) {
+ outl(what, ALI_REG(codec, ALI_STOP));
+ }
+ val = inl(ALI_REG(codec, ALI_AINTEN));
+ if (do_start) {
+ val |= whati;
+ } else {
+ val &= ~whati;
+ }
+ outl(val, ALI_REG(codec, ALI_AINTEN));
+ if (do_start) {
+ outl(what, ALI_REG(codec, ALI_START));
+ }
+ snd_ali_printk("trigger: what=%xh whati=%xh\n",what,whati);
+ spin_unlock(&codec->reg_lock);
+
+ return 0;
+}
+
+static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+ snd_ali_voice_t *evoice = pvoice->extra;
+ int err;
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0) return err;
+
+ /* voice management */
+
+ if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) {
+ if (evoice == NULL) {
+ evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+ if (evoice == NULL)
+ return -ENOMEM;
+ pvoice->extra = evoice;
+ evoice->substream = substream;
+ }
+ } else {
+ if (evoice != NULL) {
+ snd_ali_free_voice(codec, evoice);
+ pvoice->extra = evoice = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+ snd_ali_voice_t *evoice = pvoice ? pvoice->extra : NULL;
+
+ snd_pcm_lib_free_pages(substream);
+ if (evoice != NULL) {
+ snd_ali_free_voice(codec, evoice);
+ pvoice->extra = NULL;
+ }
+ return 0;
+}
+
+static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ali_playback_prepare(snd_pcm_substream_t * substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+ snd_ali_voice_t *evoice = pvoice->extra;
+ unsigned long flags;
+
+ unsigned int LBA;
+ unsigned int Delta;
+ unsigned int ESO;
+ unsigned int CTRL;
+ unsigned int GVSEL;
+ unsigned int PAN;
+ unsigned int VOL;
+ unsigned int EC;
+
+ snd_ali_printk("playback_prepare ...\n");
+
+ spin_lock_irqsave(&codec->reg_lock, flags);
+
+ /* set Delta (rate) value */
+ Delta = snd_ali_convert_rate(runtime->rate, 0);
+
+ if ((pvoice->number == ALI_SPDIF_IN_CHANNEL) ||
+ (pvoice->number == ALI_PCM_IN_CHANNEL))
+ snd_ali_disable_special_channel(codec, pvoice->number);
+ else if (codec->spdif_support &&
+ (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE)
+ && (pvoice->number == ALI_SPDIF_OUT_CHANNEL)) {
+ snd_ali_set_spdif_out_rate(codec, runtime->rate);
+ Delta = 0x1000;
+ }
+
+ /* set Loop Back Address */
+ LBA = runtime->dma_addr;
+
+ /* set interrupt count size */
+ pvoice->count = runtime->period_size;
+
+ /* set target ESO for channel */
+ pvoice->eso = runtime->buffer_size;
+
+ snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",pvoice->eso,pvoice->count);
+
+ /* set ESO to capture first MIDLP interrupt */
+ ESO = pvoice->eso -1;
+ /* set ctrl mode */
+ CTRL = snd_ali_control_mode(substream);
+
+ GVSEL = 1;
+ PAN = 0;
+ VOL = 0;
+ EC = 0;
+ snd_ali_printk("playback_prepare:\n ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL);
+ snd_ali_write_voice_regs( codec,
+ pvoice->number,
+ LBA,
+ 0, /* cso */
+ ESO,
+ Delta,
+ 0, /* alpha */
+ GVSEL,
+ PAN,
+ VOL,
+ CTRL,
+ EC);
+ if (evoice != NULL) {
+ evoice->count = pvoice->count;
+ evoice->eso = pvoice->count << 1;
+ ESO = evoice->eso - 1;
+ snd_ali_write_voice_regs(codec,
+ evoice->number,
+ LBA,
+ 0, /* cso */
+ ESO,
+ Delta,
+ 0, /* alpha */
+ GVSEL,
+ (unsigned int)0x7f,
+ (unsigned int)0x3ff,
+ CTRL,
+ EC);
+ }
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+ return 0;
+}
+
+
+static int snd_ali_capture_prepare(snd_pcm_substream_t * substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+ unsigned long flags;
+ unsigned int LBA;
+ unsigned int Delta;
+ unsigned int ESO;
+ unsigned int CTRL;
+ unsigned int GVSEL;
+ unsigned int PAN;
+ unsigned int VOL;
+ unsigned int EC;
+ u8 bValue;
+
+ spin_lock_irqsave(&codec->reg_lock, flags);
+
+ snd_ali_printk("capture_prepare...\n");
+
+ snd_ali_enable_special_channel(codec,pvoice->number);
+
+ Delta = snd_ali_convert_rate(runtime->rate, 1);
+
+ // Prepare capture intr channel
+ if (pvoice->number == ALI_SPDIF_IN_CHANNEL) {
+
+ unsigned int rate;
+
+ if (codec->revision != ALI_5451_V02) {
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+ return -1;
+ }
+ rate = snd_ali_get_spdif_in_rate(codec);
+ if (rate == 0) {
+ snd_printk("ali_capture_preapre: spdif rate detect err!\n");
+ rate = 48000;
+ }
+ bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
+ if (bValue & 0x10) {
+ outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL));
+ printk("clear SPDIF parity error flag.\n");
+ }
+
+ if (rate != 48000)
+ Delta = ((rate << 12)/runtime->rate)&0x00ffff;
+ }
+
+ // set target ESO for channel
+ pvoice->eso = runtime->buffer_size;
+
+ // set interrupt count size
+ pvoice->count = runtime->period_size;
+
+ // set Loop Back Address
+ LBA = runtime->dma_addr;
+
+ // set ESO to capture first MIDLP interrupt
+ ESO = pvoice->eso - 1;
+ CTRL = snd_ali_control_mode(substream);
+ GVSEL = 0;
+ PAN = 0x00;
+ VOL = 0x00;
+ EC = 0;
+
+ snd_ali_write_voice_regs( codec,
+ pvoice->number,
+ LBA,
+ 0, /* cso */
+ ESO,
+ Delta,
+ 0, /* alpha */
+ GVSEL,
+ PAN,
+ VOL,
+ CTRL,
+ EC);
+
+
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+ return 0;
+}
+
+
+static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+ unsigned int cso;
+
+ spin_lock(&codec->reg_lock);
+ if (!pvoice->running) {
+ spin_unlock(&codec->reg_lock);
+ return 0;
+ }
+ outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+ cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+ spin_unlock(&codec->reg_lock);
+ snd_ali_printk("playback pointer returned cso=%xh.\n", cso);
+
+ return cso;
+}
+
+
+static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+ unsigned int cso;
+ unsigned long flags;
+
+ spin_lock_irqsave(&codec->reg_lock, flags);
+ if (!pvoice->running) {
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+ return 0;
+ }
+ outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+ cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+ return cso;
+}
+
+static snd_pcm_hardware_t snd_ali_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (256*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (256*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/*
+ * Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_ali_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .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 void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ unsigned long flags;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+ ali_t *codec;
+
+ if (pvoice) {
+ codec = pvoice->codec;
+ spin_lock_irqsave(&codec->reg_lock, flags);
+ snd_ali_free_voice(pvoice->codec, pvoice);
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+ }
+}
+
+static int snd_ali_playback_open(snd_pcm_substream_t * substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&codec->reg_lock, flags);
+ pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+ if (pvoice == NULL) {
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+ return -EAGAIN;
+ }
+ pvoice->codec = codec;
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+ pvoice->substream = substream;
+ runtime->private_data = pvoice;
+ runtime->private_free = snd_ali_pcm_free_substream;
+
+ runtime->hw = snd_ali_playback;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+ return 0;
+}
+
+
+static int snd_ali_capture_open(snd_pcm_substream_t * substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice;
+ unsigned long flags;
+
+ spin_lock_irqsave(&codec->reg_lock, flags);
+ pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1);
+ if (pvoice == NULL) {
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+ return -EAGAIN;
+ }
+ pvoice->codec = codec;
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+ pvoice->substream = substream;
+ runtime->private_data = pvoice;
+ runtime->private_free = snd_ali_pcm_free_substream;
+ runtime->hw = snd_ali_capture;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+ return 0;
+}
+
+
+static int snd_ali_playback_close(snd_pcm_substream_t * substream)
+{
+ return 0;
+}
+
+static int snd_ali_capture_close(snd_pcm_substream_t * substream)
+{
+ ali_t *codec = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+
+ snd_ali_disable_special_channel(codec,pvoice->number);
+
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ali_playback_ops = {
+ .open = snd_ali_playback_open,
+ .close = snd_ali_playback_close,
+ .ioctl = snd_ali_ioctl,
+ .hw_params = snd_ali_playback_hw_params,
+ .hw_free = snd_ali_playback_hw_free,
+ .prepare = snd_ali_playback_prepare,
+ .trigger = snd_ali_trigger,
+ .pointer = snd_ali_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ali_capture_ops = {
+ .open = snd_ali_capture_open,
+ .close = snd_ali_capture_close,
+ .ioctl = snd_ali_ioctl,
+ .hw_params = snd_ali_capture_hw_params,
+ .hw_free = snd_ali_capture_hw_free,
+ .prepare = snd_ali_capture_prepare,
+ .trigger = snd_ali_trigger,
+ .pointer = snd_ali_capture_pointer,
+};
+
+
+static void snd_ali_pcm_free(snd_pcm_t *pcm)
+{
+ ali_t *codec = pcm->private_data;
+ codec->pcm = NULL;
+}
+
+static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm) *rpcm = NULL;
+ err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm);
+ if (err < 0) {
+ snd_printk("snd_ali_pcm: err called snd_pcm_new.\n");
+ return err;
+ }
+ pcm->private_data = codec;
+ pcm->private_free = snd_ali_pcm_free;
+ pcm->info_flags = 0;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops);
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(codec->pci), 64*1024, 128*1024);
+
+ pcm->info_flags = 0;
+ pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+ strcpy(pcm->name, "ALI 5451");
+ codec->pcm = pcm;
+ if (rpcm) *rpcm = pcm;
+ return 0;
+}
+
+#define ALI5451_SPDIF(xname, xindex, value) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
+.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \
+.put = snd_ali5451_spdif_put, .private_value = value}
+
+static int snd_ali5451_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ali5451_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ ali_t *codec = kcontrol->private_data;
+ unsigned int enable;
+
+ enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+ spin_lock_irqsave(&codec->reg_lock, flags);
+ switch(kcontrol->private_value) {
+ case 0:
+ enable = (codec->spdif_mask & 0x02) ? 1 : 0;
+ break;
+ case 1:
+ enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0;
+ break;
+ case 2:
+ enable = (codec->spdif_mask & 0x01) ? 1 : 0;
+ break;
+ default:
+ break;
+ }
+ ucontrol->value.integer.value[0] = enable;
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+ return 0;
+}
+
+static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ ali_t *codec = kcontrol->private_data;
+ unsigned int change = 0, enable = 0;
+
+ enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+ spin_lock_irqsave(&codec->reg_lock, flags);
+ switch (kcontrol->private_value) {
+ case 0:
+ change = (codec->spdif_mask & 0x02) ? 1 : 0;
+ change = change ^ enable;
+ if (change) {
+ if (enable) {
+ codec->spdif_mask |= 0x02;
+ snd_ali_enable_spdif_out(codec);
+ } else {
+ codec->spdif_mask &= ~(0x02);
+ codec->spdif_mask &= ~(0x04);
+ snd_ali_disable_spdif_out(codec);
+ }
+ }
+ break;
+ case 1:
+ change = (codec->spdif_mask & 0x04) ? 1 : 0;
+ change = change ^ enable;
+ if (change && (codec->spdif_mask & 0x02)) {
+ if (enable) {
+ codec->spdif_mask |= 0x04;
+ snd_ali_enable_spdif_chnout(codec);
+ } else {
+ codec->spdif_mask &= ~(0x04);
+ snd_ali_disable_spdif_chnout(codec);
+ }
+ }
+ break;
+ case 2:
+ change = (codec->spdif_mask & 0x01) ? 1 : 0;
+ change = change ^ enable;
+ if (change) {
+ if (enable) {
+ codec->spdif_mask |= 0x01;
+ snd_ali_enable_spdif_in(codec);
+ } else {
+ codec->spdif_mask &= ~(0x01);
+ snd_ali_disable_spdif_in(codec);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ali5451_mixer_spdif[] __devinitdata = {
+ /* spdif aplayback switch */
+ /* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */
+ ALI5451_SPDIF("IEC958 Output switch", 0, 0),
+ /* spdif out to spdif channel */
+ ALI5451_SPDIF("IEC958 Channel Output Switch", 0, 1),
+ /* spdif in from spdif channel */
+ ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2)
+};
+
+static void snd_ali_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ ali_t *codec = bus->private_data;
+ codec->ac97_bus = NULL;
+}
+
+static void snd_ali_mixer_free_ac97(ac97_t *ac97)
+{
+ ali_t *codec = ac97->private_data;
+ codec->ac97 = NULL;
+}
+
+static int __devinit snd_ali_mixer(ali_t * codec)
+{
+ ac97_template_t ac97;
+ unsigned int idx;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_ali_codec_write,
+ .read = snd_ali_codec_read,
+ };
+
+ if ((err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus)) < 0)
+ return err;
+ codec->ac97_bus->private_free = snd_ali_mixer_free_ac97_bus;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = codec;
+ ac97.private_free = snd_ali_mixer_free_ac97;
+ if ((err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97)) < 0) {
+ snd_printk("ali mixer creating error.\n");
+ return err;
+ }
+ if (codec->spdif_support) {
+ for(idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) {
+ err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec));
+ if (err < 0) return err;
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ali_suspend(snd_card_t *card, pm_message_t state)
+{
+ ali_t *chip = card->pm_private_data;
+ ali_image_t *im;
+ int i, j;
+
+ im = chip->image;
+ if (! im)
+ return 0;
+
+ snd_pcm_suspend_all(chip->pcm);
+ snd_ac97_suspend(chip->ac97);
+
+ spin_lock_irq(&chip->reg_lock);
+
+ im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
+ // im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START));
+ im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP));
+
+ // disable all IRQ bits
+ outl(0, ALI_REG(chip, ALI_MISCINT));
+
+ for (i = 0; i < ALI_GLOBAL_REGS; i++) {
+ if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP))
+ continue;
+ im->regs[i] = inl(ALI_REG(chip, i*4));
+ }
+
+ for (i = 0; i < ALI_CHANNELS; i++) {
+ outb(i, ALI_REG(chip, ALI_GC_CIR));
+ for (j = 0; j < ALI_CHANNEL_REGS; j++)
+ im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0));
+ }
+
+ // stop all HW channel
+ outl(0xffffffff, ALI_REG(chip, ALI_STOP));
+
+ spin_unlock_irq(&chip->reg_lock);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int ali_resume(snd_card_t *card)
+{
+ ali_t *chip = card->pm_private_data;
+ ali_image_t *im;
+ int i, j;
+
+ im = chip->image;
+ if (! im)
+ return 0;
+
+ pci_enable_device(chip->pci);
+
+ spin_lock_irq(&chip->reg_lock);
+
+ for (i = 0; i < ALI_CHANNELS; i++) {
+ outb(i, ALI_REG(chip, ALI_GC_CIR));
+ for (j = 0; j < ALI_CHANNEL_REGS; j++)
+ outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
+ }
+
+ for (i = 0; i < ALI_GLOBAL_REGS; i++) {
+ if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || (i*4 == ALI_START))
+ continue;
+ outl(im->regs[i], ALI_REG(chip, i*4));
+ }
+
+ // start HW channel
+ outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
+ // restore IRQ enable bits
+ outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ snd_ac97_resume(chip->ac97);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_ali_free(ali_t * codec)
+{
+ if (codec->hw_initialized)
+ snd_ali_disable_address_interrupt(codec);
+ if (codec->irq >= 0) {
+ synchronize_irq(codec->irq);
+ free_irq(codec->irq, (void *)codec);
+ }
+ if (codec->port)
+ pci_release_regions(codec->pci);
+ pci_disable_device(codec->pci);
+#ifdef CONFIG_PM
+ kfree(codec->image);
+#endif
+ kfree(codec);
+ return 0;
+}
+
+static int snd_ali_chip_init(ali_t *codec)
+{
+ unsigned int legacy;
+ unsigned char temp;
+ struct pci_dev *pci_dev = NULL;
+
+ snd_ali_printk("chip initializing ... \n");
+
+ if (snd_ali_reset_5451(codec)) {
+ snd_printk("ali_chip_init: reset 5451 error.\n");
+ return -1;
+ }
+
+ if (codec->revision == ALI_5451_V02) {
+ pci_dev = codec->pci_m1533;
+ pci_read_config_byte(pci_dev, 0x59, &temp);
+ temp |= 0x80;
+ pci_write_config_byte(pci_dev, 0x59, temp);
+
+ pci_dev = codec->pci_m7101;
+ pci_read_config_byte(pci_dev, 0xb8, &temp);
+ temp |= 0x20;
+ pci_write_config_byte(pci_dev, 0xB8, temp);
+ }
+
+ pci_read_config_dword(codec->pci, 0x44, &legacy);
+ legacy &= 0xff00ff00;
+ legacy |= 0x000800aa;
+ pci_write_config_dword(codec->pci, 0x44, legacy);
+
+ outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+ outl(0x00000000, ALI_REG(codec, ALI_AINTEN));
+ outl(0xffffffff, ALI_REG(codec, ALI_AINT));
+ outl(0x00000000, ALI_REG(codec, ALI_VOLUME));
+ outb(0x10, ALI_REG(codec, ALI_MPUR2));
+
+ codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID);
+ codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_STATUS);
+ if (codec->spdif_support) {
+ snd_ali_enable_spdif_out(codec);
+ codec->spdif_mask = 0x00000002;
+ }
+
+ snd_ali_printk("chip initialize succeed.\n");
+ return 0;
+
+}
+
+static int __devinit snd_ali_resources(ali_t *codec)
+{
+ int err;
+
+ snd_ali_printk("resouces allocation ...\n");
+ if ((err = pci_request_regions(codec->pci, "ALI 5451")) < 0)
+ return err;
+ codec->port = pci_resource_start(codec->pci, 0);
+
+ if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) {
+ snd_printk("Unable to request irq.\n");
+ return -EBUSY;
+ }
+ codec->irq = codec->pci->irq;
+ snd_ali_printk("resouces allocated.\n");
+ return 0;
+}
+static int snd_ali_dev_free(snd_device_t *device)
+{
+ ali_t *codec=device->device_data;
+ snd_ali_free(codec);
+ return 0;
+}
+
+static int __devinit snd_ali_create(snd_card_t * card,
+ struct pci_dev *pci,
+ int pcm_streams,
+ int spdif_support,
+ ali_t ** r_ali)
+{
+ ali_t *codec;
+ int i, err;
+ unsigned short cmdw = 0;
+ struct pci_dev *pci_dev = NULL;
+ static snd_device_ops_t ops = {
+ (snd_dev_free_t *)snd_ali_dev_free,
+ NULL,
+ NULL
+ };
+
+ *r_ali = NULL;
+
+ snd_ali_printk("creating ...\n");
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ /* check, if we can restrict PCI DMA transfers to 31 bits */
+ if (pci_set_dma_mask(pci, 0x7fffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x7fffffff) < 0) {
+ snd_printk("architecture does not support 31bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ if ((codec = kcalloc(1, sizeof(*codec), GFP_KERNEL)) == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&codec->reg_lock);
+ spin_lock_init(&codec->voice_alloc);
+
+ codec->card = card;
+ codec->pci = pci;
+ codec->irq = -1;
+ pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision);
+ codec->spdif_support = spdif_support;
+
+ if (pcm_streams < 1)
+ pcm_streams = 1;
+ if (pcm_streams > 32)
+ pcm_streams = 32;
+
+ pci_set_master(pci);
+ pci_read_config_word(pci, PCI_COMMAND, &cmdw);
+ if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) {
+ cmdw |= PCI_COMMAND_IO;
+ pci_write_config_word(pci, PCI_COMMAND, cmdw);
+ }
+ pci_set_master(pci);
+
+ if (snd_ali_resources(codec)) {
+ snd_ali_free(codec);
+ return -EBUSY;
+ }
+
+ synchronize_irq(pci->irq);
+
+ codec->synth.chmap = 0;
+ codec->synth.chcnt = 0;
+ codec->spdif_mask = 0;
+ codec->synth.synthcount = 0;
+
+ if (codec->revision == ALI_5451_V02)
+ codec->chregs.regs.ac97read = ALI_AC97_WRITE;
+ else
+ codec->chregs.regs.ac97read = ALI_AC97_READ;
+ codec->chregs.regs.ac97write = ALI_AC97_WRITE;
+
+ codec->chregs.regs.start = ALI_START;
+ codec->chregs.regs.stop = ALI_STOP;
+ codec->chregs.regs.aint = ALI_AINT;
+ codec->chregs.regs.ainten = ALI_AINTEN;
+
+ codec->chregs.data.start = 0x00;
+ codec->chregs.data.stop = 0x00;
+ codec->chregs.data.aint = 0x00;
+ codec->chregs.data.ainten = 0x00;
+
+ /* M1533: southbridge */
+ pci_dev = pci_find_device(0x10b9, 0x1533, NULL);
+ codec->pci_m1533 = pci_dev;
+ if (! codec->pci_m1533) {
+ snd_printk(KERN_ERR "ali5451: cannot find ALi 1533 chip.\n");
+ snd_ali_free(codec);
+ return -ENODEV;
+ }
+ /* M7101: power management */
+ pci_dev = pci_find_device(0x10b9, 0x7101, NULL);
+ codec->pci_m7101 = pci_dev;
+ if (! codec->pci_m7101 && codec->revision == ALI_5451_V02) {
+ snd_printk(KERN_ERR "ali5451: cannot find ALi 7101 chip.\n");
+ snd_ali_free(codec);
+ return -ENODEV;
+ }
+
+ snd_ali_printk("snd_device_new is called.\n");
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) {
+ snd_ali_free(codec);
+ return err;
+ }
+
+ /* initialise synth voices*/
+ for (i = 0; i < ALI_CHANNELS; i++ ) {
+ codec->synth.voices[i].number = i;
+ }
+
+ if ((err = snd_ali_chip_init(codec)) < 0) {
+ snd_printk("ali create: chip init error.\n");
+ return err;
+ }
+
+#ifdef CONFIG_PM
+ codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
+ if (! codec->image)
+ snd_printk(KERN_WARNING "can't allocate apm buffer\n");
+ else
+ snd_card_set_pm_callback(card, ali_suspend, ali_resume, codec);
+#endif
+
+ snd_ali_enable_address_interrupt(codec);
+ codec->hw_initialized = 1;
+
+ *r_ali = codec;
+ snd_ali_printk("created.\n");
+ return 0;
+}
+
+static int __devinit snd_ali_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ ali_t *codec;
+ int err;
+
+ snd_ali_printk("probe ...\n");
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_ali_create(card, pci, pcm_channels[dev], spdif[dev], &codec)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_ali_printk("mixer building ...\n");
+ if ((err = snd_ali_mixer(codec)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_ali_printk("pcm building ...\n");
+ if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "ALI5451");
+ strcpy(card->shortname, "ALI 5451");
+
+ sprintf(card->longname, "%s at 0x%lx, irq %li",
+ card->shortname, codec->port, codec->irq);
+
+ snd_ali_printk("register card.\n");
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_ali_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ALI 5451",
+ .id_table = snd_ali_ids,
+ .probe = snd_ali_probe,
+ .remove = __devexit_p(snd_ali_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_ali_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_ali_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ali_init)
+module_exit(alsa_card_ali_exit)
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
new file mode 100644
index 00000000000..f1a5f5723ee
--- /dev/null
+++ b/sound/pci/als4000.c
@@ -0,0 +1,789 @@
+/*
+ * card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
+ * Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
+ * Jaroslav Kysela <perex@suse.cz>
+ * Copyright (C) 2002 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
+ *
+ * Framework borrowed from Massimo Piccioni's card-als100.c.
+ *
+ * NOTES
+ *
+ * Since Avance does not provide any meaningful documentation, and I
+ * bought an ALS4000 based soundcard, I was forced to base this driver
+ * on reverse engineering.
+ *
+ * Note: this is no longer true. Pretty verbose chip docu (ALS4000a.PDF)
+ * can be found on the ALSA web site.
+ *
+ * The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an
+ * ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport
+ * interface. These subsystems can be mapped into ISA io-port space,
+ * using the PCI-interface. In addition, the PCI-bit provides DMA and IRQ
+ * services to the subsystems.
+ *
+ * While ALS4000 is very similar to a SoundBlaster, the differences in
+ * DMA and capturing require more changes to the SoundBlaster than
+ * desirable, so I made this separate driver.
+ *
+ * The ALS4000 can do real full duplex playback/capture.
+ *
+ * FMDAC:
+ * - 0x4f -> port 0x14
+ * - port 0x15 |= 1
+ *
+ * Enable/disable 3D sound:
+ * - 0x50 -> port 0x14
+ * - change bit 6 (0x40) of port 0x15
+ *
+ * Set QSound:
+ * - 0xdb -> port 0x14
+ * - set port 0x15:
+ * 0x3e (mode 3), 0x3c (mode 2), 0x3a (mode 1), 0x38 (mode 0)
+ *
+ * Set KSound:
+ * - value -> some port 0x0c0d
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>");
+MODULE_DESCRIPTION("Avance Logic ALS4000");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+#ifdef SUPPORT_JOYSTICK
+static int joystick_port[SNDRV_CARDS];
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ALS4000 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ALS4000 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard.");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(joystick_port, int, NULL, 0444);
+MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
+#endif
+
+typedef struct {
+ struct pci_dev *pci;
+ unsigned long gcr;
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+} snd_card_als4000_t;
+
+static struct pci_device_id snd_als4000_ids[] = {
+ { 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_als4000_ids);
+
+static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val)
+{
+ outb(reg, port+0x0c);
+ outl(val, port+0x08);
+}
+
+static inline void snd_als4000_gcr_write(sb_t *sb, u32 reg, u32 val)
+{
+ snd_als4000_gcr_write_addr(sb->alt_port, reg, val);
+}
+
+static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg)
+{
+ outb(reg, port+0x0c);
+ return inl(port+0x08);
+}
+
+static inline u32 snd_als4000_gcr_read(sb_t *sb, u32 reg)
+{
+ return snd_als4000_gcr_read_addr(sb->alt_port, reg);
+}
+
+static void snd_als4000_set_rate(sb_t *chip, unsigned int rate)
+{
+ if (!(chip->mode & SB_RATE_LOCK)) {
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT);
+ snd_sbdsp_command(chip, rate>>8);
+ snd_sbdsp_command(chip, rate);
+ }
+}
+
+static void snd_als4000_set_capture_dma(sb_t *chip, dma_addr_t addr, unsigned size)
+{
+ snd_als4000_gcr_write(chip, 0xa2, addr);
+ snd_als4000_gcr_write(chip, 0xa3, (size-1));
+}
+
+static void snd_als4000_set_playback_dma(sb_t *chip, dma_addr_t addr, unsigned size)
+{
+ snd_als4000_gcr_write(chip, 0x91, addr);
+ snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000);
+}
+
+#define ALS4000_FORMAT_SIGNED (1<<0)
+#define ALS4000_FORMAT_16BIT (1<<1)
+#define ALS4000_FORMAT_STEREO (1<<2)
+
+static int snd_als4000_get_format(snd_pcm_runtime_t *runtime)
+{
+ int result;
+
+ result = 0;
+ if (snd_pcm_format_signed(runtime->format))
+ result |= ALS4000_FORMAT_SIGNED;
+ if (snd_pcm_format_physical_width(runtime->format) == 16)
+ result |= ALS4000_FORMAT_16BIT;
+ if (runtime->channels > 1)
+ result |= ALS4000_FORMAT_STEREO;
+ return result;
+}
+
+/* structure for setting up playback */
+static struct {
+ unsigned char dsp_cmd, dma_on, dma_off, format;
+} playback_cmd_vals[]={
+/* ALS4000_FORMAT_U8_MONO */
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_MONO },
+/* ALS4000_FORMAT_S8_MONO */
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_MONO },
+/* ALS4000_FORMAT_U16L_MONO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_MONO },
+/* ALS4000_FORMAT_S16L_MONO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_MONO },
+/* ALS4000_FORMAT_U8_STEREO */
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_STEREO },
+/* ALS4000_FORMAT_S8_STEREO */
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_STEREO },
+/* ALS4000_FORMAT_U16L_STEREO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_STEREO },
+/* ALS4000_FORMAT_S16L_STEREO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_STEREO },
+};
+#define playback_cmd(chip) (playback_cmd_vals[(chip)->playback_format])
+
+/* structure for setting up capture */
+enum { CMD_WIDTH8=0x04, CMD_SIGNED=0x10, CMD_MONO=0x80, CMD_STEREO=0xA0 };
+static unsigned char capture_cmd_vals[]=
+{
+CMD_WIDTH8|CMD_MONO, /* ALS4000_FORMAT_U8_MONO */
+CMD_WIDTH8|CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S8_MONO */
+CMD_MONO, /* ALS4000_FORMAT_U16L_MONO */
+CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S16L_MONO */
+CMD_WIDTH8|CMD_STEREO, /* ALS4000_FORMAT_U8_STEREO */
+CMD_WIDTH8|CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S8_STEREO */
+CMD_STEREO, /* ALS4000_FORMAT_U16L_STEREO */
+CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S16L_STEREO */
+};
+#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format])
+
+static int snd_als4000_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_als4000_hw_free(snd_pcm_substream_t * substream)
+{
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_als4000_capture_prepare(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned long size;
+ unsigned count;
+
+ chip->capture_format = snd_als4000_get_format(runtime);
+
+ size = snd_pcm_lib_buffer_bytes(substream);
+ count = snd_pcm_lib_period_bytes(substream);
+
+ if (chip->capture_format & ALS4000_FORMAT_16BIT)
+ count >>=1;
+ count--;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_als4000_set_rate(chip, runtime->rate);
+ snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ spin_lock_irqsave(&chip->mixer_lock, flags );
+ snd_sbmixer_write(chip, 0xdc, count);
+ snd_sbmixer_write(chip, 0xdd, count>>8);
+ spin_unlock_irqrestore(&chip->mixer_lock, flags );
+ return 0;
+}
+
+static int snd_als4000_playback_prepare(snd_pcm_substream_t *substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned long size;
+ unsigned count;
+
+ chip->playback_format = snd_als4000_get_format(runtime);
+
+ size = snd_pcm_lib_buffer_bytes(substream);
+ count = snd_pcm_lib_period_bytes(substream);
+
+ if (chip->playback_format & ALS4000_FORMAT_16BIT)
+ count >>=1;
+ count--;
+
+ /* FIXME: from second playback on, there's a lot more clicks and pops
+ * involved here than on first playback. Fiddling with
+ * tons of different settings didn't help (DMA, speaker on/off,
+ * reordering, ...). Something seems to get enabled on playback
+ * that I haven't found out how to disable again, which then causes
+ * the switching pops to reach the speakers the next time here. */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_als4000_set_rate(chip, runtime->rate);
+ snd_als4000_set_playback_dma(chip, runtime->dma_addr, size);
+
+ /* SPEAKER_ON not needed, since dma_on seems to also enable speaker */
+ /* snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); */
+ snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd);
+ snd_sbdsp_command(chip, playback_cmd(chip).format);
+ snd_sbdsp_command(chip, count);
+ snd_sbdsp_command(chip, count>>8);
+ snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ return 0;
+}
+
+static int snd_als4000_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ int result = 0;
+
+ spin_lock(&chip->mixer_lock);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ chip->mode |= SB_RATE_LOCK_CAPTURE;
+ snd_sbmixer_write(chip, 0xde, capture_cmd(chip));
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ chip->mode &= ~SB_RATE_LOCK_CAPTURE;
+ snd_sbmixer_write(chip, 0xde, 0);
+ } else {
+ result = -EINVAL;
+ }
+ spin_unlock(&chip->mixer_lock);
+ return result;
+}
+
+static int snd_als4000_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ int result = 0;
+
+ spin_lock(&chip->reg_lock);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ chip->mode |= SB_RATE_LOCK_PLAYBACK;
+ snd_sbdsp_command(chip, playback_cmd(chip).dma_on);
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
+ chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
+ } else {
+ result = -EINVAL;
+ }
+ spin_unlock(&chip->reg_lock);
+ return result;
+}
+
+static snd_pcm_uframes_t snd_als4000_capture_pointer(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int result;
+
+ spin_lock(&chip->reg_lock);
+ result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff;
+ spin_unlock(&chip->reg_lock);
+ return bytes_to_frames( substream->runtime, result );
+}
+
+static snd_pcm_uframes_t snd_als4000_playback_pointer(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ unsigned result;
+
+ spin_lock(&chip->reg_lock);
+ result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff;
+ spin_unlock(&chip->reg_lock);
+ return bytes_to_frames( substream->runtime, result );
+}
+
+static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ sb_t *chip = dev_id;
+ unsigned gcr_status;
+ unsigned sb_status;
+
+ /* find out which bit of the ALS4000 produced the interrupt */
+ gcr_status = inb(chip->alt_port + 0xe);
+
+ if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */
+ snd_pcm_period_elapsed(chip->playback_substream);
+ if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */
+ snd_pcm_period_elapsed(chip->capture_substream);
+ if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */
+ snd_mpu401_uart_interrupt(irq, chip->rmidi, regs);
+ /* release the gcr */
+ outb(gcr_status, chip->alt_port + 0xe);
+
+ spin_lock(&chip->mixer_lock);
+ sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+ spin_unlock(&chip->mixer_lock);
+
+ if (sb_status & SB_IRQTYPE_8BIT)
+ snd_sb_ack_8bit(chip);
+ if (sb_status & SB_IRQTYPE_16BIT)
+ snd_sb_ack_16bit(chip);
+ if (sb_status & SB_IRQTYPE_MPUIN)
+ inb(chip->mpu_port);
+ if (sb_status & 0x20)
+ inb(SBP(chip, RESET));
+ return IRQ_HANDLED;
+}
+
+/*****************************************************************/
+
+static snd_pcm_hardware_t snd_als4000_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 64,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0
+};
+
+static snd_pcm_hardware_t snd_als4000_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 64,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0
+};
+
+/*****************************************************************/
+
+static int snd_als4000_playback_open(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ chip->playback_substream = substream;
+ runtime->hw = snd_als4000_playback;
+ return 0;
+}
+
+static int snd_als4000_playback_close(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->playback_substream = NULL;
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_als4000_capture_open(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ chip->capture_substream = substream;
+ runtime->hw = snd_als4000_capture;
+ return 0;
+}
+
+static int snd_als4000_capture_close(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->capture_substream = NULL;
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+/******************************************************************/
+
+static snd_pcm_ops_t snd_als4000_playback_ops = {
+ .open = snd_als4000_playback_open,
+ .close = snd_als4000_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_als4000_hw_params,
+ .hw_free = snd_als4000_hw_free,
+ .prepare = snd_als4000_playback_prepare,
+ .trigger = snd_als4000_playback_trigger,
+ .pointer = snd_als4000_playback_pointer
+};
+
+static snd_pcm_ops_t snd_als4000_capture_ops = {
+ .open = snd_als4000_capture_open,
+ .close = snd_als4000_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_als4000_hw_params,
+ .hw_free = snd_als4000_hw_free,
+ .prepare = snd_als4000_capture_prepare,
+ .trigger = snd_als4000_capture_trigger,
+ .pointer = snd_als4000_capture_pointer
+};
+
+static void snd_als4000_pcm_free(snd_pcm_t *pcm)
+{
+ sb_t *chip = pcm->private_data;
+ chip->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_als4000_pcm(sb_t *chip, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0)
+ return err;
+ pcm->private_free = snd_als4000_pcm_free;
+ pcm->private_data = chip;
+ pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ 64*1024, 64*1024);
+
+ chip->pcm = pcm;
+
+ return 0;
+}
+
+/******************************************************************/
+
+static void snd_als4000_set_addr(unsigned long gcr,
+ unsigned int sb,
+ unsigned int mpu,
+ unsigned int opl,
+ unsigned int game)
+{
+ u32 confA = 0;
+ u32 confB = 0;
+
+ if (mpu > 0)
+ confB |= (mpu | 1) << 16;
+ if (sb > 0)
+ confB |= (sb | 1);
+ if (game > 0)
+ confA |= (game | 1) << 16;
+ if (opl > 0)
+ confA |= (opl | 1);
+ snd_als4000_gcr_write_addr(gcr, 0xa8, confA);
+ snd_als4000_gcr_write_addr(gcr, 0xa9, confB);
+}
+
+static void __devinit snd_als4000_configure(sb_t *chip)
+{
+ unsigned tmp;
+ int i;
+
+ /* do some more configuration */
+ spin_lock_irq(&chip->mixer_lock);
+ tmp = snd_sbmixer_read(chip, 0xc0);
+ snd_sbmixer_write(chip, 0xc0, tmp|0x80);
+ /* always select DMA channel 0, since we do not actually use DMA */
+ snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
+ snd_sbmixer_write(chip, 0xc0, tmp&0x7f);
+ spin_unlock_irq(&chip->mixer_lock);
+
+ spin_lock_irq(&chip->reg_lock);
+ /* magic number. Enables interrupts(?) */
+ snd_als4000_gcr_write(chip, 0x8c, 0x28000);
+ for(i = 0x91; i <= 0x96; ++i)
+ snd_als4000_gcr_write(chip, i, 0);
+
+ snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99));
+ spin_unlock_irq(&chip->reg_lock);
+}
+
+#ifdef SUPPORT_JOYSTICK
+static int __devinit snd_als4000_create_gameport(snd_card_als4000_t *acard, int dev)
+{
+ struct gameport *gp;
+ struct resource *r;
+ int io_port;
+
+ if (joystick_port[dev] == 0)
+ return -ENODEV;
+
+ if (joystick_port[dev] == 1) { /* auto-detect */
+ for (io_port = 0x200; io_port <= 0x218; io_port += 8) {
+ r = request_region(io_port, 8, "ALS4000 gameport");
+ if (r)
+ break;
+ }
+ } else {
+ io_port = joystick_port[dev];
+ r = request_region(io_port, 8, "ALS4000 gameport");
+ }
+
+ if (!r) {
+ printk(KERN_WARNING "als4000: cannot reserve joystick ports\n");
+ return -EBUSY;
+ }
+
+ acard->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "als4000: cannot allocate memory for gameport\n");
+ release_resource(r);
+ kfree_nocheck(r);
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "ALS4000 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(acard->pci));
+ gameport_set_dev_parent(gp, &acard->pci->dev);
+ gp->io = io_port;
+ gameport_set_port_data(gp, r);
+
+ /* Enable legacy joystick port */
+ snd_als4000_set_addr(acard->gcr, 0, 0, 0, 1);
+
+ gameport_register_port(acard->gameport);
+
+ return 0;
+}
+
+static void snd_als4000_free_gameport(snd_card_als4000_t *acard)
+{
+ if (acard->gameport) {
+ struct resource *r = gameport_get_port_data(acard->gameport);
+
+ gameport_unregister_port(acard->gameport);
+ acard->gameport = NULL;
+
+ snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0); /* disable joystick */
+ release_resource(r);
+ kfree_nocheck(r);
+ }
+}
+#else
+static inline int snd_als4000_create_gameport(snd_card_als4000_t *acard, int dev) { return -ENOSYS; }
+static inline void snd_als4000_free_gameport(snd_card_als4000_t *acard) { }
+#endif
+
+static void snd_card_als4000_free( snd_card_t *card )
+{
+ snd_card_als4000_t * acard = (snd_card_als4000_t *)card->private_data;
+
+ /* make sure that interrupts are disabled */
+ snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0);
+ /* free resources */
+ snd_als4000_free_gameport(acard);
+ pci_release_regions(acard->pci);
+ pci_disable_device(acard->pci);
+}
+
+static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ snd_card_als4000_t *acard;
+ unsigned long gcr;
+ sb_t *chip;
+ opl3_t *opl3;
+ unsigned short word;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0) {
+ return err;
+ }
+ /* check, if we can restrict PCI DMA transfers to 24 bits */
+ if (pci_set_dma_mask(pci, 0x00ffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) {
+ snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ if ((err = pci_request_regions(pci, "ALS4000")) < 0) {
+ pci_disable_device(pci);
+ return err;
+ }
+ gcr = pci_resource_start(pci, 0);
+
+ pci_read_config_word(pci, PCI_COMMAND, &word);
+ pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
+ pci_set_master(pci);
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof( snd_card_als4000_t ) );
+ if (card == NULL) {
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ acard = (snd_card_als4000_t *)card->private_data;
+ acard->pci = pci;
+ acard->gcr = gcr;
+ card->private_free = snd_card_als4000_free;
+
+ /* disable all legacy ISA stuff */
+ snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0);
+
+ if ((err = snd_sbdsp_create(card,
+ gcr + 0x10,
+ pci->irq,
+ snd_als4000_interrupt,
+ -1,
+ -1,
+ SB_HW_ALS4000,
+ &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ chip->pci = pci;
+ chip->alt_port = gcr;
+ snd_card_set_dev(card, &pci->dev);
+
+ snd_als4000_configure(chip);
+
+ strcpy(card->driver, "ALS4000");
+ strcpy(card->shortname, "Avance Logic ALS4000");
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, chip->alt_port, chip->irq);
+
+ if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
+ gcr+0x30, 1, pci->irq, 0,
+ &chip->rmidi)) < 0) {
+ snd_card_free(card);
+ printk(KERN_ERR "als4000: no MPU-401device at 0x%lx ?\n", gcr+0x30);
+ return err;
+ }
+
+ if ((err = snd_als4000_pcm(chip, 0)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_sbmixer_new(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (snd_opl3_create(card, gcr+0x10, gcr+0x12,
+ OPL3_HW_AUTO, 1, &opl3) < 0) {
+ printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx ?\n",
+ gcr+0x10, gcr+0x12 );
+ } else {
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+
+ snd_als4000_create_gameport(acard, dev);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_card_als4000_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ALS4000",
+ .id_table = snd_als4000_ids,
+ .probe = snd_card_als4000_probe,
+ .remove = __devexit_p(snd_card_als4000_remove),
+};
+
+static int __init alsa_card_als4000_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_als4000_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_als4000_init)
+module_exit(alsa_card_als4000_exit)
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
new file mode 100644
index 00000000000..6b04c0acc6f
--- /dev/null
+++ b/sound/pci/atiixp.c
@@ -0,0 +1,1657 @@
+/*
+ * ALSA driver for ATI IXP 150/200/250/300 AC97 controllers
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ATI IXP AC97 controller");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+static char *ac97_quirk[SNDRV_CARDS];
+static int spdif_aclink[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ATI IXP controller.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ATI IXP controller.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable audio part of ATI IXP controller.");
+module_param_array(ac97_clock, int, NULL, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
+module_param_array(ac97_quirk, charp, NULL, 0444);
+MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
+module_param_array(spdif_aclink, bool, NULL, 0444);
+MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
+
+
+/*
+ */
+
+#define ATI_REG_ISR 0x00 /* interrupt source */
+#define ATI_REG_ISR_IN_XRUN (1U<<0)
+#define ATI_REG_ISR_IN_STATUS (1U<<1)
+#define ATI_REG_ISR_OUT_XRUN (1U<<2)
+#define ATI_REG_ISR_OUT_STATUS (1U<<3)
+#define ATI_REG_ISR_SPDF_XRUN (1U<<4)
+#define ATI_REG_ISR_SPDF_STATUS (1U<<5)
+#define ATI_REG_ISR_PHYS_INTR (1U<<8)
+#define ATI_REG_ISR_PHYS_MISMATCH (1U<<9)
+#define ATI_REG_ISR_CODEC0_NOT_READY (1U<<10)
+#define ATI_REG_ISR_CODEC1_NOT_READY (1U<<11)
+#define ATI_REG_ISR_CODEC2_NOT_READY (1U<<12)
+#define ATI_REG_ISR_NEW_FRAME (1U<<13)
+
+#define ATI_REG_IER 0x04 /* interrupt enable */
+#define ATI_REG_IER_IN_XRUN_EN (1U<<0)
+#define ATI_REG_IER_IO_STATUS_EN (1U<<1)
+#define ATI_REG_IER_OUT_XRUN_EN (1U<<2)
+#define ATI_REG_IER_OUT_XRUN_COND (1U<<3)
+#define ATI_REG_IER_SPDF_XRUN_EN (1U<<4)
+#define ATI_REG_IER_SPDF_STATUS_EN (1U<<5)
+#define ATI_REG_IER_PHYS_INTR_EN (1U<<8)
+#define ATI_REG_IER_PHYS_MISMATCH_EN (1U<<9)
+#define ATI_REG_IER_CODEC0_INTR_EN (1U<<10)
+#define ATI_REG_IER_CODEC1_INTR_EN (1U<<11)
+#define ATI_REG_IER_CODEC2_INTR_EN (1U<<12)
+#define ATI_REG_IER_NEW_FRAME_EN (1U<<13) /* (RO */
+#define ATI_REG_IER_SET_BUS_BUSY (1U<<14) /* (WO) audio is running */
+
+#define ATI_REG_CMD 0x08 /* command */
+#define ATI_REG_CMD_POWERDOWN (1U<<0)
+#define ATI_REG_CMD_RECEIVE_EN (1U<<1)
+#define ATI_REG_CMD_SEND_EN (1U<<2)
+#define ATI_REG_CMD_STATUS_MEM (1U<<3)
+#define ATI_REG_CMD_SPDF_OUT_EN (1U<<4)
+#define ATI_REG_CMD_SPDF_STATUS_MEM (1U<<5)
+#define ATI_REG_CMD_SPDF_THRESHOLD (3U<<6)
+#define ATI_REG_CMD_SPDF_THRESHOLD_SHIFT 6
+#define ATI_REG_CMD_IN_DMA_EN (1U<<8)
+#define ATI_REG_CMD_OUT_DMA_EN (1U<<9)
+#define ATI_REG_CMD_SPDF_DMA_EN (1U<<10)
+#define ATI_REG_CMD_SPDF_OUT_STOPPED (1U<<11)
+#define ATI_REG_CMD_SPDF_CONFIG_MASK (7U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_34 (1U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_78 (2U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_69 (3U<<12)
+#define ATI_REG_CMD_SPDF_CONFIG_01 (4U<<12)
+#define ATI_REG_CMD_INTERLEAVE_SPDF (1U<<16)
+#define ATI_REG_CMD_AUDIO_PRESENT (1U<<20)
+#define ATI_REG_CMD_INTERLEAVE_IN (1U<<21)
+#define ATI_REG_CMD_INTERLEAVE_OUT (1U<<22)
+#define ATI_REG_CMD_LOOPBACK_EN (1U<<23)
+#define ATI_REG_CMD_PACKED_DIS (1U<<24)
+#define ATI_REG_CMD_BURST_EN (1U<<25)
+#define ATI_REG_CMD_PANIC_EN (1U<<26)
+#define ATI_REG_CMD_MODEM_PRESENT (1U<<27)
+#define ATI_REG_CMD_ACLINK_ACTIVE (1U<<28)
+#define ATI_REG_CMD_AC_SOFT_RESET (1U<<29)
+#define ATI_REG_CMD_AC_SYNC (1U<<30)
+#define ATI_REG_CMD_AC_RESET (1U<<31)
+
+#define ATI_REG_PHYS_OUT_ADDR 0x0c
+#define ATI_REG_PHYS_OUT_CODEC_MASK (3U<<0)
+#define ATI_REG_PHYS_OUT_RW (1U<<2)
+#define ATI_REG_PHYS_OUT_ADDR_EN (1U<<8)
+#define ATI_REG_PHYS_OUT_ADDR_SHIFT 9
+#define ATI_REG_PHYS_OUT_DATA_SHIFT 16
+
+#define ATI_REG_PHYS_IN_ADDR 0x10
+#define ATI_REG_PHYS_IN_READ_FLAG (1U<<8)
+#define ATI_REG_PHYS_IN_ADDR_SHIFT 9
+#define ATI_REG_PHYS_IN_DATA_SHIFT 16
+
+#define ATI_REG_SLOTREQ 0x14
+
+#define ATI_REG_COUNTER 0x18
+#define ATI_REG_COUNTER_SLOT (3U<<0) /* slot # */
+#define ATI_REG_COUNTER_BITCLOCK (31U<<8)
+
+#define ATI_REG_IN_FIFO_THRESHOLD 0x1c
+
+#define ATI_REG_IN_DMA_LINKPTR 0x20
+#define ATI_REG_IN_DMA_DT_START 0x24 /* RO */
+#define ATI_REG_IN_DMA_DT_NEXT 0x28 /* RO */
+#define ATI_REG_IN_DMA_DT_CUR 0x2c /* RO */
+#define ATI_REG_IN_DMA_DT_SIZE 0x30
+
+#define ATI_REG_OUT_DMA_SLOT 0x34
+#define ATI_REG_OUT_DMA_SLOT_BIT(x) (1U << ((x) - 3))
+#define ATI_REG_OUT_DMA_SLOT_MASK 0x1ff
+#define ATI_REG_OUT_DMA_THRESHOLD_MASK 0xf800
+#define ATI_REG_OUT_DMA_THRESHOLD_SHIFT 11
+
+#define ATI_REG_OUT_DMA_LINKPTR 0x38
+#define ATI_REG_OUT_DMA_DT_START 0x3c /* RO */
+#define ATI_REG_OUT_DMA_DT_NEXT 0x40 /* RO */
+#define ATI_REG_OUT_DMA_DT_CUR 0x44 /* RO */
+#define ATI_REG_OUT_DMA_DT_SIZE 0x48
+
+#define ATI_REG_SPDF_CMD 0x4c
+#define ATI_REG_SPDF_CMD_LFSR (1U<<4)
+#define ATI_REG_SPDF_CMD_SINGLE_CH (1U<<5)
+#define ATI_REG_SPDF_CMD_LFSR_ACC (0xff<<8) /* RO */
+
+#define ATI_REG_SPDF_DMA_LINKPTR 0x50
+#define ATI_REG_SPDF_DMA_DT_START 0x54 /* RO */
+#define ATI_REG_SPDF_DMA_DT_NEXT 0x58 /* RO */
+#define ATI_REG_SPDF_DMA_DT_CUR 0x5c /* RO */
+#define ATI_REG_SPDF_DMA_DT_SIZE 0x60
+
+#define ATI_REG_MODEM_MIRROR 0x7c
+#define ATI_REG_AUDIO_MIRROR 0x80
+
+#define ATI_REG_6CH_REORDER 0x84 /* reorder slots for 6ch */
+#define ATI_REG_6CH_REORDER_EN (1U<<0) /* 3,4,7,8,6,9 -> 3,4,6,9,7,8 */
+
+#define ATI_REG_FIFO_FLUSH 0x88
+#define ATI_REG_FIFO_OUT_FLUSH (1U<<0)
+#define ATI_REG_FIFO_IN_FLUSH (1U<<1)
+
+/* LINKPTR */
+#define ATI_REG_LINKPTR_EN (1U<<0)
+
+/* [INT|OUT|SPDIF]_DMA_DT_SIZE */
+#define ATI_REG_DMA_DT_SIZE (0xffffU<<0)
+#define ATI_REG_DMA_FIFO_USED (0x1fU<<16)
+#define ATI_REG_DMA_FIFO_FREE (0x1fU<<21)
+#define ATI_REG_DMA_STATE (7U<<26)
+
+
+#define ATI_MAX_DESCRIPTORS 256 /* max number of descriptor packets */
+
+
+/*
+ */
+
+typedef struct snd_atiixp atiixp_t;
+typedef struct snd_atiixp_dma atiixp_dma_t;
+typedef struct snd_atiixp_dma_ops atiixp_dma_ops_t;
+
+
+/*
+ * DMA packate descriptor
+ */
+
+typedef struct atiixp_dma_desc {
+ u32 addr; /* DMA buffer address */
+ u16 status; /* status bits */
+ u16 size; /* size of the packet in dwords */
+ u32 next; /* address of the next packet descriptor */
+} atiixp_dma_desc_t;
+
+/*
+ * stream enum
+ */
+enum { ATI_DMA_PLAYBACK, ATI_DMA_CAPTURE, ATI_DMA_SPDIF, NUM_ATI_DMAS }; /* DMAs */
+enum { ATI_PCM_OUT, ATI_PCM_IN, ATI_PCM_SPDIF, NUM_ATI_PCMS }; /* AC97 pcm slots */
+enum { ATI_PCMDEV_ANALOG, ATI_PCMDEV_DIGITAL, NUM_ATI_PCMDEVS }; /* pcm devices */
+
+#define NUM_ATI_CODECS 3
+
+
+/*
+ * constants and callbacks for each DMA type
+ */
+struct snd_atiixp_dma_ops {
+ int type; /* ATI_DMA_XXX */
+ unsigned int llp_offset; /* LINKPTR offset */
+ unsigned int dt_cur; /* DT_CUR offset */
+ void (*enable_dma)(atiixp_t *chip, int on); /* called from open callback */
+ void (*enable_transfer)(atiixp_t *chip, int on); /* called from trigger (START/STOP) */
+ void (*flush_dma)(atiixp_t *chip); /* called from trigger (STOP only) */
+};
+
+/*
+ * DMA stream
+ */
+struct snd_atiixp_dma {
+ const atiixp_dma_ops_t *ops;
+ struct snd_dma_buffer desc_buf;
+ snd_pcm_substream_t *substream; /* assigned PCM substream */
+ unsigned int buf_addr, buf_bytes; /* DMA buffer address, bytes */
+ unsigned int period_bytes, periods;
+ int opened;
+ int running;
+ int pcm_open_flag;
+ int ac97_pcm_type; /* index # of ac97_pcm to access, -1 = not used */
+ unsigned int saved_curptr;
+};
+
+/*
+ * ATI IXP chip
+ */
+struct snd_atiixp {
+ snd_card_t *card;
+ struct pci_dev *pci;
+
+ unsigned long addr;
+ void __iomem *remap_addr;
+ int irq;
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97[NUM_ATI_CODECS];
+
+ spinlock_t reg_lock;
+
+ atiixp_dma_t dmas[NUM_ATI_DMAS];
+ struct ac97_pcm *pcms[NUM_ATI_PCMS];
+ snd_pcm_t *pcmdevs[NUM_ATI_PCMDEVS];
+
+ int max_channels; /* max. channels for PCM out */
+
+ unsigned int codec_not_ready_bits; /* for codec detection */
+
+ int spdif_over_aclink; /* passed from the module option */
+ struct semaphore open_mutex; /* playback open mutex */
+};
+
+
+/*
+ */
+static struct pci_device_id snd_atiixp_ids[] = {
+ { 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
+ { 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */
+ { 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);
+
+
+/*
+ * lowlevel functions
+ */
+
+/*
+ * update the bits of the given register.
+ * return 1 if the bits changed.
+ */
+static int snd_atiixp_update_bits(atiixp_t *chip, unsigned int reg,
+ unsigned int mask, unsigned int value)
+{
+ void __iomem *addr = chip->remap_addr + reg;
+ unsigned int data, old_data;
+ old_data = data = readl(addr);
+ data &= ~mask;
+ data |= value;
+ if (old_data == data)
+ return 0;
+ writel(data, addr);
+ return 1;
+}
+
+/*
+ * macros for easy use
+ */
+#define atiixp_write(chip,reg,value) \
+ writel(value, chip->remap_addr + ATI_REG_##reg)
+#define atiixp_read(chip,reg) \
+ readl(chip->remap_addr + ATI_REG_##reg)
+#define atiixp_update(chip,reg,mask,val) \
+ snd_atiixp_update_bits(chip, ATI_REG_##reg, mask, val)
+
+/* delay for one tick */
+#define do_delay() do { \
+ set_current_state(TASK_UNINTERRUPTIBLE); \
+ schedule_timeout(1); \
+} while (0)
+
+
+/*
+ * handling DMA packets
+ *
+ * we allocate a linear buffer for the DMA, and split it to each packet.
+ * in a future version, a scatter-gather buffer should be implemented.
+ */
+
+#define ATI_DESC_LIST_SIZE \
+ PAGE_ALIGN(ATI_MAX_DESCRIPTORS * sizeof(atiixp_dma_desc_t))
+
+/*
+ * build packets ring for the given buffer size.
+ *
+ * IXP handles the buffer descriptors, which are connected as a linked
+ * list. although we can change the list dynamically, in this version,
+ * a static RING of buffer descriptors is used.
+ *
+ * the ring is built in this function, and is set up to the hardware.
+ */
+static int atiixp_build_dma_packets(atiixp_t *chip, atiixp_dma_t *dma,
+ snd_pcm_substream_t *substream,
+ unsigned int periods,
+ unsigned int period_bytes)
+{
+ unsigned int i;
+ u32 addr, desc_addr;
+ unsigned long flags;
+
+ if (periods > ATI_MAX_DESCRIPTORS)
+ return -ENOMEM;
+
+ if (dma->desc_buf.area == NULL) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0)
+ return -ENOMEM;
+ dma->period_bytes = dma->periods = 0; /* clear */
+ }
+
+ if (dma->periods == periods && dma->period_bytes == period_bytes)
+ return 0;
+
+ /* reset DMA before changing the descriptor table */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ dma->ops->enable_dma(chip, 0);
+ dma->ops->enable_dma(chip, 1);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ /* fill the entries */
+ addr = (u32)substream->runtime->dma_addr;
+ desc_addr = (u32)dma->desc_buf.addr;
+ for (i = 0; i < periods; i++) {
+ atiixp_dma_desc_t *desc = &((atiixp_dma_desc_t *)dma->desc_buf.area)[i];
+ desc->addr = cpu_to_le32(addr);
+ desc->status = 0;
+ desc->size = period_bytes >> 2; /* in dwords */
+ desc_addr += sizeof(atiixp_dma_desc_t);
+ if (i == periods - 1)
+ desc->next = cpu_to_le32((u32)dma->desc_buf.addr);
+ else
+ desc->next = cpu_to_le32(desc_addr);
+ addr += period_bytes;
+ }
+
+ writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
+ chip->remap_addr + dma->ops->llp_offset);
+
+ dma->period_bytes = period_bytes;
+ dma->periods = periods;
+
+ return 0;
+}
+
+/*
+ * remove the ring buffer and release it if assigned
+ */
+static void atiixp_clear_dma_packets(atiixp_t *chip, atiixp_dma_t *dma, snd_pcm_substream_t *substream)
+{
+ if (dma->desc_buf.area) {
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ snd_dma_free_pages(&dma->desc_buf);
+ dma->desc_buf.area = NULL;
+ }
+}
+
+/*
+ * AC97 interface
+ */
+static int snd_atiixp_acquire_codec(atiixp_t *chip)
+{
+ int timeout = 1000;
+
+ while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {
+ if (! timeout--) {
+ snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n");
+ return -EBUSY;
+ }
+ udelay(1);
+ }
+ return 0;
+}
+
+static unsigned short snd_atiixp_codec_read(atiixp_t *chip, unsigned short codec, unsigned short reg)
+{
+ unsigned int data;
+ int timeout;
+
+ if (snd_atiixp_acquire_codec(chip) < 0)
+ return 0xffff;
+ data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+ ATI_REG_PHYS_OUT_ADDR_EN |
+ ATI_REG_PHYS_OUT_RW |
+ codec;
+ atiixp_write(chip, PHYS_OUT_ADDR, data);
+ if (snd_atiixp_acquire_codec(chip) < 0)
+ return 0xffff;
+ timeout = 1000;
+ do {
+ data = atiixp_read(chip, PHYS_IN_ADDR);
+ if (data & ATI_REG_PHYS_IN_READ_FLAG)
+ return data >> ATI_REG_PHYS_IN_DATA_SHIFT;
+ udelay(1);
+ } while (--timeout);
+ /* time out may happen during reset */
+ if (reg < 0x7c)
+ snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg);
+ return 0xffff;
+}
+
+
+static void snd_atiixp_codec_write(atiixp_t *chip, unsigned short codec, unsigned short reg, unsigned short val)
+{
+ unsigned int data;
+
+ if (snd_atiixp_acquire_codec(chip) < 0)
+ return;
+ data = ((unsigned int)val << ATI_REG_PHYS_OUT_DATA_SHIFT) |
+ ((unsigned int)reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+ ATI_REG_PHYS_OUT_ADDR_EN | codec;
+ atiixp_write(chip, PHYS_OUT_ADDR, data);
+}
+
+
+static unsigned short snd_atiixp_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ atiixp_t *chip = ac97->private_data;
+ return snd_atiixp_codec_read(chip, ac97->num, reg);
+
+}
+
+static void snd_atiixp_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+ atiixp_t *chip = ac97->private_data;
+ snd_atiixp_codec_write(chip, ac97->num, reg, val);
+}
+
+/*
+ * reset AC link
+ */
+static int snd_atiixp_aclink_reset(atiixp_t *chip)
+{
+ int timeout;
+
+ /* reset powerdoewn */
+ if (atiixp_update(chip, CMD, ATI_REG_CMD_POWERDOWN, 0))
+ udelay(10);
+
+ /* perform a software reset */
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, ATI_REG_CMD_AC_SOFT_RESET);
+ atiixp_read(chip, CMD);
+ udelay(10);
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, 0);
+
+ timeout = 10;
+ while (! (atiixp_read(chip, CMD) & ATI_REG_CMD_ACLINK_ACTIVE)) {
+ /* do a hard reset */
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,
+ ATI_REG_CMD_AC_SYNC);
+ atiixp_read(chip, CMD);
+ do_delay();
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
+ if (--timeout) {
+ snd_printk(KERN_ERR "atiixp: codec reset timeout\n");
+ break;
+ }
+ }
+
+ /* deassert RESET and assert SYNC to make sure */
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,
+ ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_atiixp_aclink_down(atiixp_t *chip)
+{
+ // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
+ // return -EBUSY;
+ atiixp_update(chip, CMD,
+ ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET,
+ ATI_REG_CMD_POWERDOWN);
+ return 0;
+}
+#endif
+
+/*
+ * auto-detection of codecs
+ *
+ * the IXP chip can generate interrupts for the non-existing codecs.
+ * NEW_FRAME interrupt is used to make sure that the interrupt is generated
+ * even if all three codecs are connected.
+ */
+
+#define ALL_CODEC_NOT_READY \
+ (ATI_REG_ISR_CODEC0_NOT_READY |\
+ ATI_REG_ISR_CODEC1_NOT_READY |\
+ ATI_REG_ISR_CODEC2_NOT_READY)
+#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
+
+static int snd_atiixp_codec_detect(atiixp_t *chip)
+{
+ int timeout;
+
+ chip->codec_not_ready_bits = 0;
+ atiixp_write(chip, IER, CODEC_CHECK_BITS);
+ /* wait for the interrupts */
+ timeout = HZ / 10;
+ while (timeout-- > 0) {
+ do_delay();
+ if (chip->codec_not_ready_bits)
+ break;
+ }
+ atiixp_write(chip, IER, 0); /* disable irqs */
+
+ if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {
+ snd_printk(KERN_ERR "atiixp: no codec detected!\n");
+ return -ENXIO;
+ }
+ return 0;
+}
+
+
+/*
+ * enable DMA and irqs
+ */
+static int snd_atiixp_chip_start(atiixp_t *chip)
+{
+ unsigned int reg;
+
+ /* set up spdif, enable burst mode */
+ reg = atiixp_read(chip, CMD);
+ reg |= 0x02 << ATI_REG_CMD_SPDF_THRESHOLD_SHIFT;
+ reg |= ATI_REG_CMD_BURST_EN;
+ atiixp_write(chip, CMD, reg);
+
+ reg = atiixp_read(chip, SPDF_CMD);
+ reg &= ~(ATI_REG_SPDF_CMD_LFSR|ATI_REG_SPDF_CMD_SINGLE_CH);
+ atiixp_write(chip, SPDF_CMD, reg);
+
+ /* clear all interrupt source */
+ atiixp_write(chip, ISR, 0xffffffff);
+ /* enable irqs */
+ atiixp_write(chip, IER,
+ ATI_REG_IER_IO_STATUS_EN |
+ ATI_REG_IER_IN_XRUN_EN |
+ ATI_REG_IER_OUT_XRUN_EN |
+ ATI_REG_IER_SPDF_XRUN_EN |
+ ATI_REG_IER_SPDF_STATUS_EN);
+ return 0;
+}
+
+
+/*
+ * disable DMA and IRQs
+ */
+static int snd_atiixp_chip_stop(atiixp_t *chip)
+{
+ /* clear interrupt source */
+ atiixp_write(chip, ISR, atiixp_read(chip, ISR));
+ /* disable irqs */
+ atiixp_write(chip, IER, 0);
+ return 0;
+}
+
+
+/*
+ * PCM section
+ */
+
+/*
+ * pointer callback simplly reads XXX_DMA_DT_CUR register as the current
+ * position. when SG-buffer is implemented, the offset must be calculated
+ * correctly...
+ */
+static snd_pcm_uframes_t snd_atiixp_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ atiixp_dma_t *dma = (atiixp_dma_t *)runtime->private_data;
+ unsigned int curptr;
+ int timeout = 1000;
+
+ while (timeout--) {
+ curptr = readl(chip->remap_addr + dma->ops->dt_cur);
+ if (curptr < dma->buf_addr)
+ continue;
+ curptr -= dma->buf_addr;
+ if (curptr >= dma->buf_bytes)
+ continue;
+ return bytes_to_frames(runtime, curptr);
+ }
+ snd_printd("atiixp: invalid DMA pointer read 0x%x (buf=%x)\n",
+ readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);
+ return 0;
+}
+
+/*
+ * XRUN detected, and stop the PCM substream
+ */
+static void snd_atiixp_xrun_dma(atiixp_t *chip, atiixp_dma_t *dma)
+{
+ if (! dma->substream || ! dma->running)
+ return;
+ snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type);
+ snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
+}
+
+/*
+ * the period ack. update the substream.
+ */
+static void snd_atiixp_update_dma(atiixp_t *chip, atiixp_dma_t *dma)
+{
+ if (! dma->substream || ! dma->running)
+ return;
+ snd_pcm_period_elapsed(dma->substream);
+}
+
+/* set BUS_BUSY interrupt bit if any DMA is running */
+/* call with spinlock held */
+static void snd_atiixp_check_bus_busy(atiixp_t *chip)
+{
+ unsigned int bus_busy;
+ if (atiixp_read(chip, CMD) & (ATI_REG_CMD_SEND_EN |
+ ATI_REG_CMD_RECEIVE_EN |
+ ATI_REG_CMD_SPDF_OUT_EN))
+ bus_busy = ATI_REG_IER_SET_BUS_BUSY;
+ else
+ bus_busy = 0;
+ atiixp_update(chip, IER, ATI_REG_IER_SET_BUS_BUSY, bus_busy);
+}
+
+/* common trigger callback
+ * calling the lowlevel callbacks in it
+ */
+static int snd_atiixp_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;
+ int err = 0;
+
+ snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ dma->ops->enable_transfer(chip, 1);
+ dma->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dma->ops->enable_transfer(chip, 0);
+ dma->running = 0;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ if (! err) {
+ snd_atiixp_check_bus_busy(chip);
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ dma->ops->flush_dma(chip);
+ snd_atiixp_check_bus_busy(chip);
+ }
+ }
+ spin_unlock(&chip->reg_lock);
+ return err;
+}
+
+
+/*
+ * lowlevel callbacks for each DMA type
+ *
+ * every callback is supposed to be called in chip->reg_lock spinlock
+ */
+
+/* flush FIFO of analog OUT DMA */
+static void atiixp_out_flush_dma(atiixp_t *chip)
+{
+ atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_OUT_FLUSH);
+}
+
+/* enable/disable analog OUT DMA */
+static void atiixp_out_enable_dma(atiixp_t *chip, int on)
+{
+ unsigned int data;
+ data = atiixp_read(chip, CMD);
+ if (on) {
+ if (data & ATI_REG_CMD_OUT_DMA_EN)
+ return;
+ atiixp_out_flush_dma(chip);
+ data |= ATI_REG_CMD_OUT_DMA_EN;
+ } else
+ data &= ~ATI_REG_CMD_OUT_DMA_EN;
+ atiixp_write(chip, CMD, data);
+}
+
+/* start/stop transfer over OUT DMA */
+static void atiixp_out_enable_transfer(atiixp_t *chip, int on)
+{
+ atiixp_update(chip, CMD, ATI_REG_CMD_SEND_EN,
+ on ? ATI_REG_CMD_SEND_EN : 0);
+}
+
+/* enable/disable analog IN DMA */
+static void atiixp_in_enable_dma(atiixp_t *chip, int on)
+{
+ atiixp_update(chip, CMD, ATI_REG_CMD_IN_DMA_EN,
+ on ? ATI_REG_CMD_IN_DMA_EN : 0);
+}
+
+/* start/stop analog IN DMA */
+static void atiixp_in_enable_transfer(atiixp_t *chip, int on)
+{
+ if (on) {
+ unsigned int data = atiixp_read(chip, CMD);
+ if (! (data & ATI_REG_CMD_RECEIVE_EN)) {
+ data |= ATI_REG_CMD_RECEIVE_EN;
+#if 0 /* FIXME: this causes the endless loop */
+ /* wait until slot 3/4 are finished */
+ while ((atiixp_read(chip, COUNTER) &
+ ATI_REG_COUNTER_SLOT) != 5)
+ ;
+#endif
+ atiixp_write(chip, CMD, data);
+ }
+ } else
+ atiixp_update(chip, CMD, ATI_REG_CMD_RECEIVE_EN, 0);
+}
+
+/* flush FIFO of analog IN DMA */
+static void atiixp_in_flush_dma(atiixp_t *chip)
+{
+ atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_IN_FLUSH);
+}
+
+/* enable/disable SPDIF OUT DMA */
+static void atiixp_spdif_enable_dma(atiixp_t *chip, int on)
+{
+ atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_DMA_EN,
+ on ? ATI_REG_CMD_SPDF_DMA_EN : 0);
+}
+
+/* start/stop SPDIF OUT DMA */
+static void atiixp_spdif_enable_transfer(atiixp_t *chip, int on)
+{
+ unsigned int data;
+ data = atiixp_read(chip, CMD);
+ if (on)
+ data |= ATI_REG_CMD_SPDF_OUT_EN;
+ else
+ data &= ~ATI_REG_CMD_SPDF_OUT_EN;
+ atiixp_write(chip, CMD, data);
+}
+
+/* flush FIFO of SPDIF OUT DMA */
+static void atiixp_spdif_flush_dma(atiixp_t *chip)
+{
+ int timeout;
+
+ /* DMA off, transfer on */
+ atiixp_spdif_enable_dma(chip, 0);
+ atiixp_spdif_enable_transfer(chip, 1);
+
+ timeout = 100;
+ do {
+ if (! (atiixp_read(chip, SPDF_DMA_DT_SIZE) & ATI_REG_DMA_FIFO_USED))
+ break;
+ udelay(1);
+ } while (timeout-- > 0);
+
+ atiixp_spdif_enable_transfer(chip, 0);
+}
+
+/* set up slots and formats for SPDIF OUT */
+static int snd_atiixp_spdif_prepare(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ if (chip->spdif_over_aclink) {
+ unsigned int data;
+ /* enable slots 10/11 */
+ atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK,
+ ATI_REG_CMD_SPDF_CONFIG_01);
+ data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;
+ data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |
+ ATI_REG_OUT_DMA_SLOT_BIT(11);
+ data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
+ atiixp_write(chip, OUT_DMA_SLOT, data);
+ atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT,
+ substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
+ ATI_REG_CMD_INTERLEAVE_OUT : 0);
+ } else {
+ atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0);
+ atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+/* set up slots and formats for analog OUT */
+static int snd_atiixp_playback_prepare(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int data;
+
+ spin_lock_irq(&chip->reg_lock);
+ data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;
+ switch (substream->runtime->channels) {
+ case 8:
+ data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |
+ ATI_REG_OUT_DMA_SLOT_BIT(11);
+ /* fallthru */
+ case 6:
+ data |= ATI_REG_OUT_DMA_SLOT_BIT(7) |
+ ATI_REG_OUT_DMA_SLOT_BIT(8);
+ /* fallthru */
+ case 4:
+ data |= ATI_REG_OUT_DMA_SLOT_BIT(6) |
+ ATI_REG_OUT_DMA_SLOT_BIT(9);
+ /* fallthru */
+ default:
+ data |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
+ ATI_REG_OUT_DMA_SLOT_BIT(4);
+ break;
+ }
+
+ /* set output threshold */
+ data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
+ atiixp_write(chip, OUT_DMA_SLOT, data);
+
+ atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT,
+ substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
+ ATI_REG_CMD_INTERLEAVE_OUT : 0);
+
+ /*
+ * enable 6 channel re-ordering bit if needed
+ */
+ atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN,
+ substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0);
+
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+/* set up slots and formats for analog IN */
+static int snd_atiixp_capture_prepare(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN,
+ substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
+ ATI_REG_CMD_INTERLEAVE_IN : 0);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+/*
+ * hw_params - allocate the buffer and set up buffer descriptors
+ */
+static int snd_atiixp_pcm_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *hw_params)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;
+ int err;
+
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+ dma->buf_addr = substream->runtime->dma_addr;
+ dma->buf_bytes = params_buffer_bytes(hw_params);
+
+ err = atiixp_build_dma_packets(chip, dma, substream,
+ params_periods(hw_params),
+ params_period_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (dma->ac97_pcm_type >= 0) {
+ struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type];
+ /* PCM is bound to AC97 codec(s)
+ * set up the AC97 codecs
+ */
+ if (dma->pcm_open_flag) {
+ snd_ac97_pcm_close(pcm);
+ dma->pcm_open_flag = 0;
+ }
+ err = snd_ac97_pcm_open(pcm, params_rate(hw_params),
+ params_channels(hw_params),
+ pcm->r[0].slots);
+ if (err >= 0)
+ dma->pcm_open_flag = 1;
+ }
+
+ return err;
+}
+
+static int snd_atiixp_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;
+
+ if (dma->pcm_open_flag) {
+ struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type];
+ snd_ac97_pcm_close(pcm);
+ dma->pcm_open_flag = 0;
+ }
+ atiixp_clear_dma_packets(chip, dma, substream);
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+
+/*
+ * pcm hardware definition, identical for all DMA types
+ */
+static snd_pcm_hardware_t snd_atiixp_pcm_hw =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 256 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = ATI_MAX_DESCRIPTORS,
+};
+
+static int snd_atiixp_pcm_open(snd_pcm_substream_t *substream, atiixp_dma_t *dma, int pcm_type)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+
+ if (dma->opened)
+ return -EBUSY;
+ dma->substream = substream;
+ runtime->hw = snd_atiixp_pcm_hw;
+ dma->ac97_pcm_type = pcm_type;
+ if (pcm_type >= 0) {
+ runtime->hw.rates = chip->pcms[pcm_type]->rates;
+ snd_pcm_limit_hw_rates(runtime);
+ } else {
+ /* direct SPDIF */
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ }
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ runtime->private_data = dma;
+
+ /* enable DMA bits */
+ spin_lock_irq(&chip->reg_lock);
+ dma->ops->enable_dma(chip, 1);
+ spin_unlock_irq(&chip->reg_lock);
+ dma->opened = 1;
+
+ return 0;
+}
+
+static int snd_atiixp_pcm_close(snd_pcm_substream_t *substream, atiixp_dma_t *dma)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ /* disable DMA bits */
+ snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+ spin_lock_irq(&chip->reg_lock);
+ dma->ops->enable_dma(chip, 0);
+ spin_unlock_irq(&chip->reg_lock);
+ dma->substream = NULL;
+ dma->opened = 0;
+ return 0;
+}
+
+/*
+ */
+static int snd_atiixp_playback_open(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ down(&chip->open_mutex);
+ err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
+ up(&chip->open_mutex);
+ if (err < 0)
+ return err;
+ substream->runtime->hw.channels_max = chip->max_channels;
+ if (chip->max_channels > 2)
+ /* channels must be even */
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ return 0;
+}
+
+static int snd_atiixp_playback_close(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+ down(&chip->open_mutex);
+ err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
+ up(&chip->open_mutex);
+ return err;
+}
+
+static int snd_atiixp_capture_open(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1);
+}
+
+static int snd_atiixp_capture_close(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]);
+}
+
+static int snd_atiixp_spdif_open(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+ down(&chip->open_mutex);
+ if (chip->spdif_over_aclink) /* share DMA_PLAYBACK */
+ err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2);
+ else
+ err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1);
+ up(&chip->open_mutex);
+ return err;
+}
+
+static int snd_atiixp_spdif_close(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+ down(&chip->open_mutex);
+ if (chip->spdif_over_aclink)
+ err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
+ else
+ err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]);
+ up(&chip->open_mutex);
+ return err;
+}
+
+/* AC97 playback */
+static snd_pcm_ops_t snd_atiixp_playback_ops = {
+ .open = snd_atiixp_playback_open,
+ .close = snd_atiixp_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_atiixp_pcm_hw_params,
+ .hw_free = snd_atiixp_pcm_hw_free,
+ .prepare = snd_atiixp_playback_prepare,
+ .trigger = snd_atiixp_pcm_trigger,
+ .pointer = snd_atiixp_pcm_pointer,
+};
+
+/* AC97 capture */
+static snd_pcm_ops_t snd_atiixp_capture_ops = {
+ .open = snd_atiixp_capture_open,
+ .close = snd_atiixp_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_atiixp_pcm_hw_params,
+ .hw_free = snd_atiixp_pcm_hw_free,
+ .prepare = snd_atiixp_capture_prepare,
+ .trigger = snd_atiixp_pcm_trigger,
+ .pointer = snd_atiixp_pcm_pointer,
+};
+
+/* SPDIF playback */
+static snd_pcm_ops_t snd_atiixp_spdif_ops = {
+ .open = snd_atiixp_spdif_open,
+ .close = snd_atiixp_spdif_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_atiixp_pcm_hw_params,
+ .hw_free = snd_atiixp_pcm_hw_free,
+ .prepare = snd_atiixp_spdif_prepare,
+ .trigger = snd_atiixp_pcm_trigger,
+ .pointer = snd_atiixp_pcm_pointer,
+};
+
+static struct ac97_pcm atiixp_pcm_defs[] __devinitdata = {
+ /* front PCM */
+ {
+ .exclusive = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_PCM_LEFT) |
+ (1 << AC97_SLOT_PCM_RIGHT) |
+ (1 << AC97_SLOT_PCM_CENTER) |
+ (1 << AC97_SLOT_PCM_SLEFT) |
+ (1 << AC97_SLOT_PCM_SRIGHT) |
+ (1 << AC97_SLOT_LFE)
+ }
+ }
+ },
+ /* PCM IN #1 */
+ {
+ .stream = 1,
+ .exclusive = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_PCM_LEFT) |
+ (1 << AC97_SLOT_PCM_RIGHT)
+ }
+ }
+ },
+ /* S/PDIF OUT (optional) */
+ {
+ .exclusive = 1,
+ .spdif = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_SPDIF_LEFT2) |
+ (1 << AC97_SLOT_SPDIF_RIGHT2)
+ }
+ }
+ },
+};
+
+static atiixp_dma_ops_t snd_atiixp_playback_dma_ops = {
+ .type = ATI_DMA_PLAYBACK,
+ .llp_offset = ATI_REG_OUT_DMA_LINKPTR,
+ .dt_cur = ATI_REG_OUT_DMA_DT_CUR,
+ .enable_dma = atiixp_out_enable_dma,
+ .enable_transfer = atiixp_out_enable_transfer,
+ .flush_dma = atiixp_out_flush_dma,
+};
+
+static atiixp_dma_ops_t snd_atiixp_capture_dma_ops = {
+ .type = ATI_DMA_CAPTURE,
+ .llp_offset = ATI_REG_IN_DMA_LINKPTR,
+ .dt_cur = ATI_REG_IN_DMA_DT_CUR,
+ .enable_dma = atiixp_in_enable_dma,
+ .enable_transfer = atiixp_in_enable_transfer,
+ .flush_dma = atiixp_in_flush_dma,
+};
+
+static atiixp_dma_ops_t snd_atiixp_spdif_dma_ops = {
+ .type = ATI_DMA_SPDIF,
+ .llp_offset = ATI_REG_SPDF_DMA_LINKPTR,
+ .dt_cur = ATI_REG_SPDF_DMA_DT_CUR,
+ .enable_dma = atiixp_spdif_enable_dma,
+ .enable_transfer = atiixp_spdif_enable_transfer,
+ .flush_dma = atiixp_spdif_flush_dma,
+};
+
+
+static int __devinit snd_atiixp_pcm_new(atiixp_t *chip)
+{
+ snd_pcm_t *pcm;
+ ac97_bus_t *pbus = chip->ac97_bus;
+ int err, i, num_pcms;
+
+ /* initialize constants */
+ chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops;
+ chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops;
+ if (! chip->spdif_over_aclink)
+ chip->dmas[ATI_DMA_SPDIF].ops = &snd_atiixp_spdif_dma_ops;
+
+ /* assign AC97 pcm */
+ if (chip->spdif_over_aclink)
+ num_pcms = 3;
+ else
+ num_pcms = 2;
+ err = snd_ac97_pcm_assign(pbus, num_pcms, atiixp_pcm_defs);
+ if (err < 0)
+ return err;
+ for (i = 0; i < num_pcms; i++)
+ chip->pcms[i] = &pbus->pcms[i];
+
+ chip->max_channels = 2;
+ if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+ if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_LFE))
+ chip->max_channels = 6;
+ else
+ chip->max_channels = 4;
+ }
+
+ /* PCM #0: analog I/O */
+ err = snd_pcm_new(chip->card, "ATI IXP AC97", ATI_PCMDEV_ANALOG, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, "ATI IXP AC97");
+ chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024);
+
+ /* no SPDIF support on codec? */
+ if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
+ return 0;
+
+ /* FIXME: non-48k sample rate doesn't work on my test machine with AD1888 */
+ if (chip->pcms[ATI_PCM_SPDIF])
+ chip->pcms[ATI_PCM_SPDIF]->rates = SNDRV_PCM_RATE_48000;
+
+ /* PCM #1: spdif playback */
+ err = snd_pcm_new(chip->card, "ATI IXP IEC958", ATI_PCMDEV_DIGITAL, 1, 0, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops);
+ pcm->private_data = chip;
+ if (chip->spdif_over_aclink)
+ strcpy(pcm->name, "ATI IXP IEC958 (AC97)");
+ else
+ strcpy(pcm->name, "ATI IXP IEC958 (Direct)");
+ chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024);
+
+ /* pre-select AC97 SPDIF slots 10/11 */
+ for (i = 0; i < NUM_ATI_CODECS; i++) {
+ if (chip->ac97[i])
+ snd_ac97_update_bits(chip->ac97[i], AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4);
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ atiixp_t *chip = dev_id;
+ unsigned int status;
+
+ status = atiixp_read(chip, ISR);
+
+ if (! status)
+ return IRQ_NONE;
+
+ /* process audio DMA */
+ if (status & ATI_REG_ISR_OUT_XRUN)
+ snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]);
+ else if (status & ATI_REG_ISR_OUT_STATUS)
+ snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]);
+ if (status & ATI_REG_ISR_IN_XRUN)
+ snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]);
+ else if (status & ATI_REG_ISR_IN_STATUS)
+ snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]);
+ if (! chip->spdif_over_aclink) {
+ if (status & ATI_REG_ISR_SPDF_XRUN)
+ snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_SPDIF]);
+ else if (status & ATI_REG_ISR_SPDF_STATUS)
+ snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_SPDIF]);
+ }
+
+ /* for codec detection */
+ if (status & CODEC_CHECK_BITS) {
+ unsigned int detected;
+ detected = status & CODEC_CHECK_BITS;
+ spin_lock(&chip->reg_lock);
+ chip->codec_not_ready_bits |= detected;
+ atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
+ spin_unlock(&chip->reg_lock);
+ }
+
+ /* ack */
+ atiixp_write(chip, ISR, status);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * ac97 mixer section
+ */
+
+static struct ac97_quirk ac97_quirks[] __devinitdata = {
+ {
+ .vendor = 0x103c,
+ .device = 0x006b,
+ .name = "HP Pavilion ZV5030US",
+ .type = AC97_TUNE_MUTE_LED
+ },
+ { } /* terminator */
+};
+
+static int __devinit snd_atiixp_mixer_new(atiixp_t *chip, int clock, const char *quirk_override)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int i, err;
+ int codec_count;
+ static ac97_bus_ops_t ops = {
+ .write = snd_atiixp_ac97_write,
+ .read = snd_atiixp_ac97_read,
+ };
+ static unsigned int codec_skip[NUM_ATI_CODECS] = {
+ ATI_REG_ISR_CODEC0_NOT_READY,
+ ATI_REG_ISR_CODEC1_NOT_READY,
+ ATI_REG_ISR_CODEC2_NOT_READY,
+ };
+
+ if (snd_atiixp_codec_detect(chip) < 0)
+ return -ENXIO;
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ return err;
+ pbus->clock = clock;
+ pbus->shared_type = AC97_SHARED_TYPE_ATIIXP; /* shared with modem driver */
+ chip->ac97_bus = pbus;
+
+ codec_count = 0;
+ for (i = 0; i < NUM_ATI_CODECS; i++) {
+ if (chip->codec_not_ready_bits & codec_skip[i])
+ continue;
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.pci = chip->pci;
+ ac97.num = i;
+ ac97.scaps = AC97_SCAP_SKIP_MODEM;
+ if (! chip->spdif_over_aclink)
+ ac97.scaps |= AC97_SCAP_NO_SPDIF;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ chip->ac97[i] = NULL; /* to be sure */
+ snd_printdd("atiixp: codec %d not available for audio\n", i);
+ continue;
+ }
+ codec_count++;
+ }
+
+ if (! codec_count) {
+ snd_printk(KERN_ERR "atiixp: no codec available\n");
+ return -ENODEV;
+ }
+
+ snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, quirk_override);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int snd_atiixp_suspend(snd_card_t *card, pm_message_t state)
+{
+ atiixp_t *chip = card->pm_private_data;
+ int i;
+
+ for (i = 0; i < NUM_ATI_PCMDEVS; i++)
+ if (chip->pcmdevs[i]) {
+ atiixp_dma_t *dma = &chip->dmas[i];
+ if (dma->substream && dma->running)
+ dma->saved_curptr = readl(chip->remap_addr + dma->ops->dt_cur);
+ snd_pcm_suspend_all(chip->pcmdevs[i]);
+ }
+ for (i = 0; i < NUM_ATI_CODECS; i++)
+ if (chip->ac97[i])
+ snd_ac97_suspend(chip->ac97[i]);
+ snd_atiixp_aclink_down(chip);
+ snd_atiixp_chip_stop(chip);
+
+ pci_set_power_state(chip->pci, 3);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int snd_atiixp_resume(snd_card_t *card)
+{
+ atiixp_t *chip = card->pm_private_data;
+ int i;
+
+ pci_enable_device(chip->pci);
+ pci_set_power_state(chip->pci, 0);
+ pci_set_master(chip->pci);
+
+ snd_atiixp_aclink_reset(chip);
+ snd_atiixp_chip_start(chip);
+
+ for (i = 0; i < NUM_ATI_CODECS; i++)
+ if (chip->ac97[i])
+ snd_ac97_resume(chip->ac97[i]);
+
+ for (i = 0; i < NUM_ATI_PCMDEVS; i++)
+ if (chip->pcmdevs[i]) {
+ atiixp_dma_t *dma = &chip->dmas[i];
+ if (dma->substream && dma->running) {
+ dma->ops->enable_dma(chip, 1);
+ writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
+ chip->remap_addr + dma->ops->llp_offset);
+ writel(dma->saved_curptr, chip->remap_addr + dma->ops->dt_cur);
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ * proc interface for register dump
+ */
+
+static void snd_atiixp_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ atiixp_t *chip = entry->private_data;
+ int i;
+
+ for (i = 0; i < 256; i += 4)
+ snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i));
+}
+
+static void __devinit snd_atiixp_proc_init(atiixp_t *chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(chip->card, "atiixp", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read);
+}
+
+
+
+/*
+ * destructor
+ */
+
+static int snd_atiixp_free(atiixp_t *chip)
+{
+ if (chip->irq < 0)
+ goto __hw_end;
+ snd_atiixp_chip_stop(chip);
+ synchronize_irq(chip->irq);
+ __hw_end:
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->remap_addr)
+ iounmap(chip->remap_addr);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_atiixp_dev_free(snd_device_t *device)
+{
+ atiixp_t *chip = device->device_data;
+ return snd_atiixp_free(chip);
+}
+
+/*
+ * constructor for chip instance
+ */
+static int __devinit snd_atiixp_create(snd_card_t *card,
+ struct pci_dev *pci,
+ atiixp_t **r_chip)
+{
+ static snd_device_ops_t ops = {
+ .dev_free = snd_atiixp_dev_free,
+ };
+ atiixp_t *chip;
+ int err;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ init_MUTEX(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) {
+ pci_disable_device(pci);
+ kfree(chip);
+ return err;
+ }
+ chip->addr = pci_resource_start(pci, 0);
+ chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci, 0));
+ if (chip->remap_addr == NULL) {
+ snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
+ snd_atiixp_free(chip);
+ return -EIO;
+ }
+
+ if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ snd_atiixp_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
+ synchronize_irq(chip->irq);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_atiixp_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_chip = chip;
+ return 0;
+}
+
+
+static int __devinit snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ atiixp_t *chip;
+ unsigned char revision;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+
+ strcpy(card->driver, spdif_aclink[dev] ? "ATIIXP" : "ATIIXP-SPDMA");
+ strcpy(card->shortname, "ATI IXP");
+ if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
+ goto __error;
+
+ if ((err = snd_atiixp_aclink_reset(chip)) < 0)
+ goto __error;
+
+ chip->spdif_over_aclink = spdif_aclink[dev];
+
+ if ((err = snd_atiixp_mixer_new(chip, ac97_clock[dev], ac97_quirk[dev])) < 0)
+ goto __error;
+
+ if ((err = snd_atiixp_pcm_new(chip)) < 0)
+ goto __error;
+
+ snd_atiixp_proc_init(chip);
+
+ snd_atiixp_chip_start(chip);
+
+ snprintf(card->longname, sizeof(card->longname),
+ "%s rev %x with %s at %#lx, irq %i", card->shortname, revision,
+ chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?",
+ chip->addr, chip->irq);
+
+ snd_card_set_pm_callback(card, snd_atiixp_suspend, snd_atiixp_resume, chip);
+
+ if ((err = snd_card_register(card)) < 0)
+ goto __error;
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+
+ __error:
+ snd_card_free(card);
+ return err;
+}
+
+static void __devexit snd_atiixp_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ATI IXP AC97 controller",
+ .id_table = snd_atiixp_ids,
+ .probe = snd_atiixp_probe,
+ .remove = __devexit_p(snd_atiixp_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+
+static int __init alsa_card_atiixp_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_atiixp_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_atiixp_init)
+module_exit(alsa_card_atiixp_exit)
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
new file mode 100644
index 00000000000..5d3e537339f
--- /dev/null
+++ b/sound/pci/atiixp_modem.c
@@ -0,0 +1,1344 @@
+/*
+ * ALSA driver for ATI IXP 150/200/250 AC97 modem controllers
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ATI IXP MC97 controller");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250}}");
+
+static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ATI IXP controller.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ATI IXP controller.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable audio part of ATI IXP controller.");
+module_param_array(ac97_clock, int, NULL, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
+
+
+/*
+ */
+
+#define ATI_REG_ISR 0x00 /* interrupt source */
+#define ATI_REG_ISR_MODEM_IN_XRUN (1U<<0)
+#define ATI_REG_ISR_MODEM_IN_STATUS (1U<<1)
+#define ATI_REG_ISR_MODEM_OUT1_XRUN (1U<<2)
+#define ATI_REG_ISR_MODEM_OUT1_STATUS (1U<<3)
+#define ATI_REG_ISR_MODEM_OUT2_XRUN (1U<<4)
+#define ATI_REG_ISR_MODEM_OUT2_STATUS (1U<<5)
+#define ATI_REG_ISR_MODEM_OUT3_XRUN (1U<<6)
+#define ATI_REG_ISR_MODEM_OUT3_STATUS (1U<<7)
+#define ATI_REG_ISR_PHYS_INTR (1U<<8)
+#define ATI_REG_ISR_PHYS_MISMATCH (1U<<9)
+#define ATI_REG_ISR_CODEC0_NOT_READY (1U<<10)
+#define ATI_REG_ISR_CODEC1_NOT_READY (1U<<11)
+#define ATI_REG_ISR_CODEC2_NOT_READY (1U<<12)
+#define ATI_REG_ISR_NEW_FRAME (1U<<13)
+#define ATI_REG_ISR_MODEM_GPIO_DATA (1U<<14)
+
+#define ATI_REG_IER 0x04 /* interrupt enable */
+#define ATI_REG_IER_MODEM_IN_XRUN_EN (1U<<0)
+#define ATI_REG_IER_MODEM_STATUS_EN (1U<<1)
+#define ATI_REG_IER_MODEM_OUT1_XRUN_EN (1U<<2)
+#define ATI_REG_IER_MODEM_OUT2_XRUN_EN (1U<<4)
+#define ATI_REG_IER_MODEM_OUT3_XRUN_EN (1U<<6)
+#define ATI_REG_IER_PHYS_INTR_EN (1U<<8)
+#define ATI_REG_IER_PHYS_MISMATCH_EN (1U<<9)
+#define ATI_REG_IER_CODEC0_INTR_EN (1U<<10)
+#define ATI_REG_IER_CODEC1_INTR_EN (1U<<11)
+#define ATI_REG_IER_CODEC2_INTR_EN (1U<<12)
+#define ATI_REG_IER_NEW_FRAME_EN (1U<<13) /* (RO */
+#define ATI_REG_IER_MODEM_GPIO_DATA_EN (1U<<14) /* (WO) modem is running */
+#define ATI_REG_IER_MODEM_SET_BUS_BUSY (1U<<15)
+
+#define ATI_REG_CMD 0x08 /* command */
+#define ATI_REG_CMD_POWERDOWN (1U<<0)
+#define ATI_REG_CMD_MODEM_RECEIVE_EN (1U<<1) /* modem only */
+#define ATI_REG_CMD_MODEM_SEND1_EN (1U<<2) /* modem only */
+#define ATI_REG_CMD_MODEM_SEND2_EN (1U<<3) /* modem only */
+#define ATI_REG_CMD_MODEM_SEND3_EN (1U<<4) /* modem only */
+#define ATI_REG_CMD_MODEM_STATUS_MEM (1U<<5) /* modem only */
+#define ATI_REG_CMD_MODEM_IN_DMA_EN (1U<<8) /* modem only */
+#define ATI_REG_CMD_MODEM_OUT_DMA1_EN (1U<<9) /* modem only */
+#define ATI_REG_CMD_MODEM_OUT_DMA2_EN (1U<<10) /* modem only */
+#define ATI_REG_CMD_MODEM_OUT_DMA3_EN (1U<<11) /* modem only */
+#define ATI_REG_CMD_AUDIO_PRESENT (1U<<20)
+#define ATI_REG_CMD_MODEM_GPIO_THRU_DMA (1U<<22) /* modem only */
+#define ATI_REG_CMD_LOOPBACK_EN (1U<<23)
+#define ATI_REG_CMD_PACKED_DIS (1U<<24)
+#define ATI_REG_CMD_BURST_EN (1U<<25)
+#define ATI_REG_CMD_PANIC_EN (1U<<26)
+#define ATI_REG_CMD_MODEM_PRESENT (1U<<27)
+#define ATI_REG_CMD_ACLINK_ACTIVE (1U<<28)
+#define ATI_REG_CMD_AC_SOFT_RESET (1U<<29)
+#define ATI_REG_CMD_AC_SYNC (1U<<30)
+#define ATI_REG_CMD_AC_RESET (1U<<31)
+
+#define ATI_REG_PHYS_OUT_ADDR 0x0c
+#define ATI_REG_PHYS_OUT_CODEC_MASK (3U<<0)
+#define ATI_REG_PHYS_OUT_RW (1U<<2)
+#define ATI_REG_PHYS_OUT_ADDR_EN (1U<<8)
+#define ATI_REG_PHYS_OUT_ADDR_SHIFT 9
+#define ATI_REG_PHYS_OUT_DATA_SHIFT 16
+
+#define ATI_REG_PHYS_IN_ADDR 0x10
+#define ATI_REG_PHYS_IN_READ_FLAG (1U<<8)
+#define ATI_REG_PHYS_IN_ADDR_SHIFT 9
+#define ATI_REG_PHYS_IN_DATA_SHIFT 16
+
+#define ATI_REG_SLOTREQ 0x14
+
+#define ATI_REG_COUNTER 0x18
+#define ATI_REG_COUNTER_SLOT (3U<<0) /* slot # */
+#define ATI_REG_COUNTER_BITCLOCK (31U<<8)
+
+#define ATI_REG_IN_FIFO_THRESHOLD 0x1c
+
+#define ATI_REG_MODEM_IN_DMA_LINKPTR 0x20
+#define ATI_REG_MODEM_IN_DMA_DT_START 0x24 /* RO */
+#define ATI_REG_MODEM_IN_DMA_DT_NEXT 0x28 /* RO */
+#define ATI_REG_MODEM_IN_DMA_DT_CUR 0x2c /* RO */
+#define ATI_REG_MODEM_IN_DMA_DT_SIZE 0x30
+#define ATI_REG_MODEM_OUT_FIFO 0x34 /* output threshold */
+#define ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK (0xf<<16)
+#define ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT 16
+#define ATI_REG_MODEM_OUT_DMA1_LINKPTR 0x38
+#define ATI_REG_MODEM_OUT_DMA2_LINKPTR 0x3c
+#define ATI_REG_MODEM_OUT_DMA3_LINKPTR 0x40
+#define ATI_REG_MODEM_OUT_DMA1_DT_START 0x44
+#define ATI_REG_MODEM_OUT_DMA1_DT_NEXT 0x48
+#define ATI_REG_MODEM_OUT_DMA1_DT_CUR 0x4c
+#define ATI_REG_MODEM_OUT_DMA2_DT_START 0x50
+#define ATI_REG_MODEM_OUT_DMA2_DT_NEXT 0x54
+#define ATI_REG_MODEM_OUT_DMA2_DT_CUR 0x58
+#define ATI_REG_MODEM_OUT_DMA3_DT_START 0x5c
+#define ATI_REG_MODEM_OUT_DMA3_DT_NEXT 0x60
+#define ATI_REG_MODEM_OUT_DMA3_DT_CUR 0x64
+#define ATI_REG_MODEM_OUT_DMA12_DT_SIZE 0x68
+#define ATI_REG_MODEM_OUT_DMA3_DT_SIZE 0x6c
+#define ATI_REG_MODEM_OUT_FIFO_USED 0x70
+#define ATI_REG_MODEM_OUT_GPIO 0x74
+#define ATI_REG_MODEM_OUT_GPIO_EN 1
+#define ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT 5
+#define ATI_REG_MODEM_IN_GPIO 0x78
+
+#define ATI_REG_MODEM_MIRROR 0x7c
+#define ATI_REG_AUDIO_MIRROR 0x80
+
+#define ATI_REG_MODEM_FIFO_FLUSH 0x88
+#define ATI_REG_MODEM_FIFO_OUT1_FLUSH (1U<<0)
+#define ATI_REG_MODEM_FIFO_OUT2_FLUSH (1U<<1)
+#define ATI_REG_MODEM_FIFO_OUT3_FLUSH (1U<<2)
+#define ATI_REG_MODEM_FIFO_IN_FLUSH (1U<<3)
+
+/* LINKPTR */
+#define ATI_REG_LINKPTR_EN (1U<<0)
+
+#define ATI_MAX_DESCRIPTORS 256 /* max number of descriptor packets */
+
+
+/*
+ */
+
+typedef struct snd_atiixp atiixp_t;
+typedef struct snd_atiixp_dma atiixp_dma_t;
+typedef struct snd_atiixp_dma_ops atiixp_dma_ops_t;
+
+
+/*
+ * DMA packate descriptor
+ */
+
+typedef struct atiixp_dma_desc {
+ u32 addr; /* DMA buffer address */
+ u16 status; /* status bits */
+ u16 size; /* size of the packet in dwords */
+ u32 next; /* address of the next packet descriptor */
+} atiixp_dma_desc_t;
+
+/*
+ * stream enum
+ */
+enum { ATI_DMA_PLAYBACK, ATI_DMA_CAPTURE, NUM_ATI_DMAS }; /* DMAs */
+enum { ATI_PCM_OUT, ATI_PCM_IN, NUM_ATI_PCMS }; /* AC97 pcm slots */
+enum { ATI_PCMDEV_ANALOG, NUM_ATI_PCMDEVS }; /* pcm devices */
+
+#define NUM_ATI_CODECS 3
+
+
+/*
+ * constants and callbacks for each DMA type
+ */
+struct snd_atiixp_dma_ops {
+ int type; /* ATI_DMA_XXX */
+ unsigned int llp_offset; /* LINKPTR offset */
+ unsigned int dt_cur; /* DT_CUR offset */
+ void (*enable_dma)(atiixp_t *chip, int on); /* called from open callback */
+ void (*enable_transfer)(atiixp_t *chip, int on); /* called from trigger (START/STOP) */
+ void (*flush_dma)(atiixp_t *chip); /* called from trigger (STOP only) */
+};
+
+/*
+ * DMA stream
+ */
+struct snd_atiixp_dma {
+ const atiixp_dma_ops_t *ops;
+ struct snd_dma_buffer desc_buf;
+ snd_pcm_substream_t *substream; /* assigned PCM substream */
+ unsigned int buf_addr, buf_bytes; /* DMA buffer address, bytes */
+ unsigned int period_bytes, periods;
+ int opened;
+ int running;
+ int pcm_open_flag;
+ int ac97_pcm_type; /* index # of ac97_pcm to access, -1 = not used */
+};
+
+/*
+ * ATI IXP chip
+ */
+struct snd_atiixp {
+ snd_card_t *card;
+ struct pci_dev *pci;
+
+ struct resource *res; /* memory i/o */
+ unsigned long addr;
+ void __iomem *remap_addr;
+ int irq;
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97[NUM_ATI_CODECS];
+
+ spinlock_t reg_lock;
+
+ atiixp_dma_t dmas[NUM_ATI_DMAS];
+ struct ac97_pcm *pcms[NUM_ATI_PCMS];
+ snd_pcm_t *pcmdevs[NUM_ATI_PCMDEVS];
+
+ int max_channels; /* max. channels for PCM out */
+
+ unsigned int codec_not_ready_bits; /* for codec detection */
+
+ int spdif_over_aclink; /* passed from the module option */
+ struct semaphore open_mutex; /* playback open mutex */
+};
+
+
+/*
+ */
+static struct pci_device_id snd_atiixp_ids[] = {
+ { 0x1002, 0x434d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);
+
+
+/*
+ * lowlevel functions
+ */
+
+/*
+ * update the bits of the given register.
+ * return 1 if the bits changed.
+ */
+static int snd_atiixp_update_bits(atiixp_t *chip, unsigned int reg,
+ unsigned int mask, unsigned int value)
+{
+ void __iomem *addr = chip->remap_addr + reg;
+ unsigned int data, old_data;
+ old_data = data = readl(addr);
+ data &= ~mask;
+ data |= value;
+ if (old_data == data)
+ return 0;
+ writel(data, addr);
+ return 1;
+}
+
+/*
+ * macros for easy use
+ */
+#define atiixp_write(chip,reg,value) \
+ writel(value, chip->remap_addr + ATI_REG_##reg)
+#define atiixp_read(chip,reg) \
+ readl(chip->remap_addr + ATI_REG_##reg)
+#define atiixp_update(chip,reg,mask,val) \
+ snd_atiixp_update_bits(chip, ATI_REG_##reg, mask, val)
+
+/* delay for one tick */
+#define do_delay() do { \
+ set_current_state(TASK_UNINTERRUPTIBLE); \
+ schedule_timeout(1); \
+} while (0)
+
+
+/*
+ * handling DMA packets
+ *
+ * we allocate a linear buffer for the DMA, and split it to each packet.
+ * in a future version, a scatter-gather buffer should be implemented.
+ */
+
+#define ATI_DESC_LIST_SIZE \
+ PAGE_ALIGN(ATI_MAX_DESCRIPTORS * sizeof(atiixp_dma_desc_t))
+
+/*
+ * build packets ring for the given buffer size.
+ *
+ * IXP handles the buffer descriptors, which are connected as a linked
+ * list. although we can change the list dynamically, in this version,
+ * a static RING of buffer descriptors is used.
+ *
+ * the ring is built in this function, and is set up to the hardware.
+ */
+static int atiixp_build_dma_packets(atiixp_t *chip, atiixp_dma_t *dma,
+ snd_pcm_substream_t *substream,
+ unsigned int periods,
+ unsigned int period_bytes)
+{
+ unsigned int i;
+ u32 addr, desc_addr;
+ unsigned long flags;
+
+ if (periods > ATI_MAX_DESCRIPTORS)
+ return -ENOMEM;
+
+ if (dma->desc_buf.area == NULL) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0)
+ return -ENOMEM;
+ dma->period_bytes = dma->periods = 0; /* clear */
+ }
+
+ if (dma->periods == periods && dma->period_bytes == period_bytes)
+ return 0;
+
+ /* reset DMA before changing the descriptor table */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ dma->ops->enable_dma(chip, 0);
+ dma->ops->enable_dma(chip, 1);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ /* fill the entries */
+ addr = (u32)substream->runtime->dma_addr;
+ desc_addr = (u32)dma->desc_buf.addr;
+ for (i = 0; i < periods; i++) {
+ atiixp_dma_desc_t *desc = &((atiixp_dma_desc_t *)dma->desc_buf.area)[i];
+ desc->addr = cpu_to_le32(addr);
+ desc->status = 0;
+ desc->size = period_bytes >> 2; /* in dwords */
+ desc_addr += sizeof(atiixp_dma_desc_t);
+ if (i == periods - 1)
+ desc->next = cpu_to_le32((u32)dma->desc_buf.addr);
+ else
+ desc->next = cpu_to_le32(desc_addr);
+ addr += period_bytes;
+ }
+
+ writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
+ chip->remap_addr + dma->ops->llp_offset);
+
+ dma->period_bytes = period_bytes;
+ dma->periods = periods;
+
+ return 0;
+}
+
+/*
+ * remove the ring buffer and release it if assigned
+ */
+static void atiixp_clear_dma_packets(atiixp_t *chip, atiixp_dma_t *dma, snd_pcm_substream_t *substream)
+{
+ if (dma->desc_buf.area) {
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ snd_dma_free_pages(&dma->desc_buf);
+ dma->desc_buf.area = NULL;
+ }
+}
+
+/*
+ * AC97 interface
+ */
+static int snd_atiixp_acquire_codec(atiixp_t *chip)
+{
+ int timeout = 1000;
+
+ while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {
+ if (! timeout--) {
+ snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n");
+ return -EBUSY;
+ }
+ udelay(1);
+ }
+ return 0;
+}
+
+static unsigned short snd_atiixp_codec_read(atiixp_t *chip, unsigned short codec, unsigned short reg)
+{
+ unsigned int data;
+ int timeout;
+
+ if (snd_atiixp_acquire_codec(chip) < 0)
+ return 0xffff;
+ data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+ ATI_REG_PHYS_OUT_ADDR_EN |
+ ATI_REG_PHYS_OUT_RW |
+ codec;
+ atiixp_write(chip, PHYS_OUT_ADDR, data);
+ if (snd_atiixp_acquire_codec(chip) < 0)
+ return 0xffff;
+ timeout = 1000;
+ do {
+ data = atiixp_read(chip, PHYS_IN_ADDR);
+ if (data & ATI_REG_PHYS_IN_READ_FLAG)
+ return data >> ATI_REG_PHYS_IN_DATA_SHIFT;
+ udelay(1);
+ } while (--timeout);
+ /* time out may happen during reset */
+ if (reg < 0x7c)
+ snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg);
+ return 0xffff;
+}
+
+
+static void snd_atiixp_codec_write(atiixp_t *chip, unsigned short codec, unsigned short reg, unsigned short val)
+{
+ unsigned int data;
+
+ if (snd_atiixp_acquire_codec(chip) < 0)
+ return;
+ data = ((unsigned int)val << ATI_REG_PHYS_OUT_DATA_SHIFT) |
+ ((unsigned int)reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+ ATI_REG_PHYS_OUT_ADDR_EN | codec;
+ atiixp_write(chip, PHYS_OUT_ADDR, data);
+}
+
+
+static unsigned short snd_atiixp_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ atiixp_t *chip = ac97->private_data;
+ return snd_atiixp_codec_read(chip, ac97->num, reg);
+
+}
+
+static void snd_atiixp_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+ atiixp_t *chip = ac97->private_data;
+ snd_atiixp_codec_write(chip, ac97->num, reg, val);
+}
+
+/*
+ * reset AC link
+ */
+static int snd_atiixp_aclink_reset(atiixp_t *chip)
+{
+ int timeout;
+
+ /* reset powerdoewn */
+ if (atiixp_update(chip, CMD, ATI_REG_CMD_POWERDOWN, 0))
+ udelay(10);
+
+ /* perform a software reset */
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, ATI_REG_CMD_AC_SOFT_RESET);
+ atiixp_read(chip, CMD);
+ udelay(10);
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, 0);
+
+ timeout = 10;
+ while (! (atiixp_read(chip, CMD) & ATI_REG_CMD_ACLINK_ACTIVE)) {
+ /* do a hard reset */
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,
+ ATI_REG_CMD_AC_SYNC);
+ atiixp_read(chip, CMD);
+ do_delay();
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
+ if (--timeout) {
+ snd_printk(KERN_ERR "atiixp: codec reset timeout\n");
+ break;
+ }
+ }
+
+ /* deassert RESET and assert SYNC to make sure */
+ atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,
+ ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_atiixp_aclink_down(atiixp_t *chip)
+{
+ // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
+ // return -EBUSY;
+ atiixp_update(chip, CMD,
+ ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET,
+ ATI_REG_CMD_POWERDOWN);
+ return 0;
+}
+#endif
+
+/*
+ * auto-detection of codecs
+ *
+ * the IXP chip can generate interrupts for the non-existing codecs.
+ * NEW_FRAME interrupt is used to make sure that the interrupt is generated
+ * even if all three codecs are connected.
+ */
+
+#define ALL_CODEC_NOT_READY \
+ (ATI_REG_ISR_CODEC0_NOT_READY |\
+ ATI_REG_ISR_CODEC1_NOT_READY |\
+ ATI_REG_ISR_CODEC2_NOT_READY)
+#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
+
+static int snd_atiixp_codec_detect(atiixp_t *chip)
+{
+ int timeout;
+
+ chip->codec_not_ready_bits = 0;
+ atiixp_write(chip, IER, CODEC_CHECK_BITS);
+ /* wait for the interrupts */
+ timeout = HZ / 10;
+ while (timeout-- > 0) {
+ do_delay();
+ if (chip->codec_not_ready_bits)
+ break;
+ }
+ atiixp_write(chip, IER, 0); /* disable irqs */
+
+ if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {
+ snd_printk(KERN_ERR "atiixp: no codec detected!\n");
+ return -ENXIO;
+ }
+ return 0;
+}
+
+
+/*
+ * enable DMA and irqs
+ */
+static int snd_atiixp_chip_start(atiixp_t *chip)
+{
+ unsigned int reg;
+
+ /* set up spdif, enable burst mode */
+ reg = atiixp_read(chip, CMD);
+ reg |= ATI_REG_CMD_BURST_EN;
+ if(!(reg & ATI_REG_CMD_MODEM_PRESENT))
+ reg |= ATI_REG_CMD_MODEM_PRESENT;
+ atiixp_write(chip, CMD, reg);
+
+ /* clear all interrupt source */
+ atiixp_write(chip, ISR, 0xffffffff);
+ /* enable irqs */
+ atiixp_write(chip, IER,
+ ATI_REG_IER_MODEM_STATUS_EN |
+ ATI_REG_IER_MODEM_IN_XRUN_EN |
+ ATI_REG_IER_MODEM_OUT1_XRUN_EN);
+ return 0;
+}
+
+
+/*
+ * disable DMA and IRQs
+ */
+static int snd_atiixp_chip_stop(atiixp_t *chip)
+{
+ /* clear interrupt source */
+ atiixp_write(chip, ISR, atiixp_read(chip, ISR));
+ /* disable irqs */
+ atiixp_write(chip, IER, 0);
+ return 0;
+}
+
+
+/*
+ * PCM section
+ */
+
+/*
+ * pointer callback simplly reads XXX_DMA_DT_CUR register as the current
+ * position. when SG-buffer is implemented, the offset must be calculated
+ * correctly...
+ */
+static snd_pcm_uframes_t snd_atiixp_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ atiixp_dma_t *dma = (atiixp_dma_t *)runtime->private_data;
+ unsigned int curptr;
+ int timeout = 1000;
+
+ while (timeout--) {
+ curptr = readl(chip->remap_addr + dma->ops->dt_cur);
+ if (curptr < dma->buf_addr)
+ continue;
+ curptr -= dma->buf_addr;
+ if (curptr >= dma->buf_bytes)
+ continue;
+ return bytes_to_frames(runtime, curptr);
+ }
+ snd_printd("atiixp-modem: invalid DMA pointer read 0x%x (buf=%x)\n",
+ readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);
+ return 0;
+}
+
+/*
+ * XRUN detected, and stop the PCM substream
+ */
+static void snd_atiixp_xrun_dma(atiixp_t *chip, atiixp_dma_t *dma)
+{
+ if (! dma->substream || ! dma->running)
+ return;
+ snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type);
+ snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
+}
+
+/*
+ * the period ack. update the substream.
+ */
+static void snd_atiixp_update_dma(atiixp_t *chip, atiixp_dma_t *dma)
+{
+ if (! dma->substream || ! dma->running)
+ return;
+ snd_pcm_period_elapsed(dma->substream);
+}
+
+/* set BUS_BUSY interrupt bit if any DMA is running */
+/* call with spinlock held */
+static void snd_atiixp_check_bus_busy(atiixp_t *chip)
+{
+ unsigned int bus_busy;
+ if (atiixp_read(chip, CMD) & (ATI_REG_CMD_MODEM_SEND1_EN |
+ ATI_REG_CMD_MODEM_RECEIVE_EN))
+ bus_busy = ATI_REG_IER_MODEM_SET_BUS_BUSY;
+ else
+ bus_busy = 0;
+ atiixp_update(chip, IER, ATI_REG_IER_MODEM_SET_BUS_BUSY, bus_busy);
+}
+
+/* common trigger callback
+ * calling the lowlevel callbacks in it
+ */
+static int snd_atiixp_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;
+ unsigned int reg = 0;
+ int i;
+
+ snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
+
+ if (cmd != SNDRV_PCM_TRIGGER_START && cmd != SNDRV_PCM_TRIGGER_STOP)
+ return -EINVAL;
+
+ spin_lock(&chip->reg_lock);
+
+ /* hook off/on: via GPIO_OUT */
+ for (i = 0; i < NUM_ATI_CODECS; i++) {
+ if (chip->ac97[i]) {
+ reg = snd_ac97_read(chip->ac97[i], AC97_GPIO_STATUS);
+ break;
+ }
+ }
+ if(cmd == SNDRV_PCM_TRIGGER_START)
+ reg |= AC97_GPIO_LINE1_OH;
+ else
+ reg &= ~AC97_GPIO_LINE1_OH;
+ reg = (reg << ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT) | ATI_REG_MODEM_OUT_GPIO_EN ;
+ atiixp_write(chip, MODEM_OUT_GPIO, reg);
+
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ dma->ops->enable_transfer(chip, 1);
+ dma->running = 1;
+ } else {
+ dma->ops->enable_transfer(chip, 0);
+ dma->running = 0;
+ }
+ snd_atiixp_check_bus_busy(chip);
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ dma->ops->flush_dma(chip);
+ snd_atiixp_check_bus_busy(chip);
+ }
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+
+/*
+ * lowlevel callbacks for each DMA type
+ *
+ * every callback is supposed to be called in chip->reg_lock spinlock
+ */
+
+/* flush FIFO of analog OUT DMA */
+static void atiixp_out_flush_dma(atiixp_t *chip)
+{
+ atiixp_write(chip, MODEM_FIFO_FLUSH, ATI_REG_MODEM_FIFO_OUT1_FLUSH);
+}
+
+/* enable/disable analog OUT DMA */
+static void atiixp_out_enable_dma(atiixp_t *chip, int on)
+{
+ unsigned int data;
+ data = atiixp_read(chip, CMD);
+ if (on) {
+ if (data & ATI_REG_CMD_MODEM_OUT_DMA1_EN)
+ return;
+ atiixp_out_flush_dma(chip);
+ data |= ATI_REG_CMD_MODEM_OUT_DMA1_EN;
+ } else
+ data &= ~ATI_REG_CMD_MODEM_OUT_DMA1_EN;
+ atiixp_write(chip, CMD, data);
+}
+
+/* start/stop transfer over OUT DMA */
+static void atiixp_out_enable_transfer(atiixp_t *chip, int on)
+{
+ atiixp_update(chip, CMD, ATI_REG_CMD_MODEM_SEND1_EN,
+ on ? ATI_REG_CMD_MODEM_SEND1_EN : 0);
+}
+
+/* enable/disable analog IN DMA */
+static void atiixp_in_enable_dma(atiixp_t *chip, int on)
+{
+ atiixp_update(chip, CMD, ATI_REG_CMD_MODEM_IN_DMA_EN,
+ on ? ATI_REG_CMD_MODEM_IN_DMA_EN : 0);
+}
+
+/* start/stop analog IN DMA */
+static void atiixp_in_enable_transfer(atiixp_t *chip, int on)
+{
+ if (on) {
+ unsigned int data = atiixp_read(chip, CMD);
+ if (! (data & ATI_REG_CMD_MODEM_RECEIVE_EN)) {
+ data |= ATI_REG_CMD_MODEM_RECEIVE_EN;
+ atiixp_write(chip, CMD, data);
+ }
+ } else
+ atiixp_update(chip, CMD, ATI_REG_CMD_MODEM_RECEIVE_EN, 0);
+}
+
+/* flush FIFO of analog IN DMA */
+static void atiixp_in_flush_dma(atiixp_t *chip)
+{
+ atiixp_write(chip, MODEM_FIFO_FLUSH, ATI_REG_MODEM_FIFO_IN_FLUSH);
+}
+
+/* set up slots and formats for analog OUT */
+static int snd_atiixp_playback_prepare(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int data;
+
+ spin_lock_irq(&chip->reg_lock);
+ /* set output threshold */
+ data = atiixp_read(chip, MODEM_OUT_FIFO);
+ data &= ~ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK;
+ data |= 0x04 << ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT;
+ atiixp_write(chip, MODEM_OUT_FIFO, data);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+/* set up slots and formats for analog IN */
+static int snd_atiixp_capture_prepare(snd_pcm_substream_t *substream)
+{
+ return 0;
+}
+
+/*
+ * hw_params - allocate the buffer and set up buffer descriptors
+ */
+static int snd_atiixp_pcm_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *hw_params)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;
+ int err;
+ int i;
+
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+ dma->buf_addr = substream->runtime->dma_addr;
+ dma->buf_bytes = params_buffer_bytes(hw_params);
+
+ err = atiixp_build_dma_packets(chip, dma, substream,
+ params_periods(hw_params),
+ params_period_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ /* set up modem rate */
+ for (i = 0; i < NUM_ATI_CODECS; i++) {
+ if (! chip->ac97[i])
+ continue;
+ snd_ac97_write(chip->ac97[i], AC97_LINE1_RATE, params_rate(hw_params));
+ snd_ac97_write(chip->ac97[i], AC97_LINE1_LEVEL, 0);
+ }
+
+ return err;
+}
+
+static int snd_atiixp_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;
+
+ atiixp_clear_dma_packets(chip, dma, substream);
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+
+/*
+ * pcm hardware definition, identical for all DMA types
+ */
+static snd_pcm_hardware_t snd_atiixp_pcm_hw =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 256 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = ATI_MAX_DESCRIPTORS,
+};
+
+static int snd_atiixp_pcm_open(snd_pcm_substream_t *substream, atiixp_dma_t *dma, int pcm_type)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+ static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+
+ snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+
+ if (dma->opened)
+ return -EBUSY;
+ dma->substream = substream;
+ runtime->hw = snd_atiixp_pcm_hw;
+ dma->ac97_pcm_type = pcm_type;
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ runtime->private_data = dma;
+
+ /* enable DMA bits */
+ spin_lock_irq(&chip->reg_lock);
+ dma->ops->enable_dma(chip, 1);
+ spin_unlock_irq(&chip->reg_lock);
+ dma->opened = 1;
+
+ return 0;
+}
+
+static int snd_atiixp_pcm_close(snd_pcm_substream_t *substream, atiixp_dma_t *dma)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ /* disable DMA bits */
+ snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+ spin_lock_irq(&chip->reg_lock);
+ dma->ops->enable_dma(chip, 0);
+ spin_unlock_irq(&chip->reg_lock);
+ dma->substream = NULL;
+ dma->opened = 0;
+ return 0;
+}
+
+/*
+ */
+static int snd_atiixp_playback_open(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ down(&chip->open_mutex);
+ err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
+ up(&chip->open_mutex);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int snd_atiixp_playback_close(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+ down(&chip->open_mutex);
+ err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
+ up(&chip->open_mutex);
+ return err;
+}
+
+static int snd_atiixp_capture_open(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1);
+}
+
+static int snd_atiixp_capture_close(snd_pcm_substream_t *substream)
+{
+ atiixp_t *chip = snd_pcm_substream_chip(substream);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]);
+}
+
+
+/* AC97 playback */
+static snd_pcm_ops_t snd_atiixp_playback_ops = {
+ .open = snd_atiixp_playback_open,
+ .close = snd_atiixp_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_atiixp_pcm_hw_params,
+ .hw_free = snd_atiixp_pcm_hw_free,
+ .prepare = snd_atiixp_playback_prepare,
+ .trigger = snd_atiixp_pcm_trigger,
+ .pointer = snd_atiixp_pcm_pointer,
+};
+
+/* AC97 capture */
+static snd_pcm_ops_t snd_atiixp_capture_ops = {
+ .open = snd_atiixp_capture_open,
+ .close = snd_atiixp_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_atiixp_pcm_hw_params,
+ .hw_free = snd_atiixp_pcm_hw_free,
+ .prepare = snd_atiixp_capture_prepare,
+ .trigger = snd_atiixp_pcm_trigger,
+ .pointer = snd_atiixp_pcm_pointer,
+};
+
+static atiixp_dma_ops_t snd_atiixp_playback_dma_ops = {
+ .type = ATI_DMA_PLAYBACK,
+ .llp_offset = ATI_REG_MODEM_OUT_DMA1_LINKPTR,
+ .dt_cur = ATI_REG_MODEM_OUT_DMA1_DT_CUR,
+ .enable_dma = atiixp_out_enable_dma,
+ .enable_transfer = atiixp_out_enable_transfer,
+ .flush_dma = atiixp_out_flush_dma,
+};
+
+static atiixp_dma_ops_t snd_atiixp_capture_dma_ops = {
+ .type = ATI_DMA_CAPTURE,
+ .llp_offset = ATI_REG_MODEM_IN_DMA_LINKPTR,
+ .dt_cur = ATI_REG_MODEM_IN_DMA_DT_CUR,
+ .enable_dma = atiixp_in_enable_dma,
+ .enable_transfer = atiixp_in_enable_transfer,
+ .flush_dma = atiixp_in_flush_dma,
+};
+
+static int __devinit snd_atiixp_pcm_new(atiixp_t *chip)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ /* initialize constants */
+ chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops;
+ chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops;
+
+ /* PCM #0: analog I/O */
+ err = snd_pcm_new(chip->card, "ATI IXP MC97", ATI_PCMDEV_ANALOG, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, "ATI IXP MC97");
+ chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024);
+
+ return 0;
+}
+
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ atiixp_t *chip = dev_id;
+ unsigned int status;
+
+ status = atiixp_read(chip, ISR);
+
+ if (! status)
+ return IRQ_NONE;
+
+ /* process audio DMA */
+ if (status & ATI_REG_ISR_MODEM_OUT1_XRUN)
+ snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]);
+ else if (status & ATI_REG_ISR_MODEM_OUT1_STATUS)
+ snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]);
+ if (status & ATI_REG_ISR_MODEM_IN_XRUN)
+ snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]);
+ else if (status & ATI_REG_ISR_MODEM_IN_STATUS)
+ snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]);
+
+ /* for codec detection */
+ if (status & CODEC_CHECK_BITS) {
+ unsigned int detected;
+ detected = status & CODEC_CHECK_BITS;
+ spin_lock(&chip->reg_lock);
+ chip->codec_not_ready_bits |= detected;
+ atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
+ spin_unlock(&chip->reg_lock);
+ }
+
+ /* ack */
+ atiixp_write(chip, ISR, status);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * ac97 mixer section
+ */
+
+static int __devinit snd_atiixp_mixer_new(atiixp_t *chip, int clock)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int i, err;
+ int codec_count;
+ static ac97_bus_ops_t ops = {
+ .write = snd_atiixp_ac97_write,
+ .read = snd_atiixp_ac97_read,
+ };
+ static unsigned int codec_skip[NUM_ATI_CODECS] = {
+ ATI_REG_ISR_CODEC0_NOT_READY,
+ ATI_REG_ISR_CODEC1_NOT_READY,
+ ATI_REG_ISR_CODEC2_NOT_READY,
+ };
+
+ if (snd_atiixp_codec_detect(chip) < 0)
+ return -ENXIO;
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ return err;
+ pbus->clock = clock;
+ pbus->shared_type = AC97_SHARED_TYPE_ATIIXP; /* shared with audio driver */
+ chip->ac97_bus = pbus;
+
+ codec_count = 0;
+ for (i = 0; i < NUM_ATI_CODECS; i++) {
+ if (chip->codec_not_ready_bits & codec_skip[i])
+ continue;
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.pci = chip->pci;
+ ac97.num = i;
+ ac97.scaps = AC97_SCAP_SKIP_AUDIO;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ chip->ac97[i] = NULL; /* to be sure */
+ snd_printdd("atiixp: codec %d not available for modem\n", i);
+ continue;
+ }
+ codec_count++;
+ }
+
+ if (! codec_count) {
+ snd_printk(KERN_ERR "atiixp: no codec available\n");
+ return -ENODEV;
+ }
+
+ /* snd_ac97_tune_hardware(chip->ac97, ac97_quirks); */
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int snd_atiixp_suspend(snd_card_t *card, pm_message_t state)
+{
+ atiixp_t *chip = card->pm_private_data;
+ int i;
+
+ for (i = 0; i < NUM_ATI_PCMDEVS; i++)
+ if (chip->pcmdevs[i])
+ snd_pcm_suspend_all(chip->pcmdevs[i]);
+ for (i = 0; i < NUM_ATI_CODECS; i++)
+ if (chip->ac97[i])
+ snd_ac97_suspend(chip->ac97[i]);
+ snd_atiixp_aclink_down(chip);
+ snd_atiixp_chip_stop(chip);
+
+ pci_set_power_state(chip->pci, 3);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int snd_atiixp_resume(snd_card_t *card)
+{
+ atiixp_t *chip = card->pm_private_data;
+ int i;
+
+ pci_enable_device(chip->pci);
+ pci_set_power_state(chip->pci, 0);
+ pci_set_master(chip->pci);
+
+ snd_atiixp_aclink_reset(chip);
+ snd_atiixp_chip_start(chip);
+
+ for (i = 0; i < NUM_ATI_CODECS; i++)
+ if (chip->ac97[i])
+ snd_ac97_resume(chip->ac97[i]);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ * proc interface for register dump
+ */
+
+static void snd_atiixp_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ atiixp_t *chip = entry->private_data;
+ int i;
+
+ for (i = 0; i < 256; i += 4)
+ snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i));
+}
+
+static void __devinit snd_atiixp_proc_init(atiixp_t *chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(chip->card, "atiixp", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read);
+}
+
+
+
+/*
+ * destructor
+ */
+
+static int snd_atiixp_free(atiixp_t *chip)
+{
+ if (chip->irq < 0)
+ goto __hw_end;
+ snd_atiixp_chip_stop(chip);
+ synchronize_irq(chip->irq);
+ __hw_end:
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->remap_addr)
+ iounmap(chip->remap_addr);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_atiixp_dev_free(snd_device_t *device)
+{
+ atiixp_t *chip = device->device_data;
+ return snd_atiixp_free(chip);
+}
+
+/*
+ * constructor for chip instance
+ */
+static int __devinit snd_atiixp_create(snd_card_t *card,
+ struct pci_dev *pci,
+ atiixp_t **r_chip)
+{
+ static snd_device_ops_t ops = {
+ .dev_free = snd_atiixp_dev_free,
+ };
+ atiixp_t *chip;
+ int err;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ init_MUTEX(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ if ((err = pci_request_regions(pci, "ATI IXP MC97")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->addr = pci_resource_start(pci, 0);
+ chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci, 0));
+ if (chip->remap_addr == NULL) {
+ snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
+ snd_atiixp_free(chip);
+ return -EIO;
+ }
+
+ if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ snd_atiixp_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
+ synchronize_irq(chip->irq);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_atiixp_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_chip = chip;
+ return 0;
+}
+
+
+static int __devinit snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ atiixp_t *chip;
+ unsigned char revision;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+
+ strcpy(card->driver, "ATIIXP-MODEM");
+ strcpy(card->shortname, "ATI IXP Modem");
+ if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
+ goto __error;
+
+ if ((err = snd_atiixp_aclink_reset(chip)) < 0)
+ goto __error;
+
+ if ((err = snd_atiixp_mixer_new(chip, ac97_clock[dev])) < 0)
+ goto __error;
+
+ if ((err = snd_atiixp_pcm_new(chip)) < 0)
+ goto __error;
+
+ snd_atiixp_proc_init(chip);
+
+ snd_atiixp_chip_start(chip);
+
+ sprintf(card->longname, "%s rev %x at 0x%lx, irq %i",
+ card->shortname, revision, chip->addr, chip->irq);
+
+ snd_card_set_pm_callback(card, snd_atiixp_suspend, snd_atiixp_resume, chip);
+
+ if ((err = snd_card_register(card)) < 0)
+ goto __error;
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+
+ __error:
+ snd_card_free(card);
+ return err;
+}
+
+static void __devexit snd_atiixp_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ATI IXP MC97 controller",
+ .id_table = snd_atiixp_ids,
+ .probe = snd_atiixp_probe,
+ .remove = __devexit_p(snd_atiixp_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+
+static int __init alsa_card_atiixp_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_atiixp_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_atiixp_init)
+module_exit(alsa_card_atiixp_exit)
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
new file mode 100644
index 00000000000..d0a66bc5d4a
--- /dev/null
+++ b/sound/pci/au88x0/Makefile
@@ -0,0 +1,7 @@
+snd-au8810-objs := au8810.o
+snd-au8820-objs := au8820.o
+snd-au8830-objs := au8830.o
+
+obj-$(CONFIG_SND_AU8810) += snd-au8810.o
+obj-$(CONFIG_SND_AU8820) += snd-au8820.o
+obj-$(CONFIG_SND_AU8830) += snd-au8830.o
diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c
new file mode 100644
index 00000000000..fce22c7af0e
--- /dev/null
+++ b/sound/pci/au88x0/au8810.c
@@ -0,0 +1,17 @@
+#include "au8810.h"
+#include "au88x0.h"
+static struct pci_device_id snd_vortex_ids[] = {
+ {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1,},
+ {0,}
+};
+
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mixer.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_eq.c"
+#include "au88x0_a3d.c"
+#include "au88x0_xtalk.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h
new file mode 100644
index 00000000000..3837d2ba5e6
--- /dev/null
+++ b/sound/pci/au88x0/au8810.h
@@ -0,0 +1,229 @@
+/*
+ Aureal Advantage Soundcard driver.
+ */
+
+#define CHIP_AU8810
+
+#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME_SHORT "au8810"
+
+#define NR_ADB 0x10
+#define NR_WT 0x00
+#define NR_SRC 0x10
+#define NR_A3D 0x10
+#define NR_MIXIN 0x20
+#define NR_MIXOUT 0x10
+
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00 /* read only, subbuffer, DMA pos */
+#define POS_MASK 0x00000fff
+#define POS_SHIFT 0x0
+#define ADB_SUBBUF_MASK 0x00003000 /* ADB only. */
+#define ADB_SUBBUF_SHIFT 0xc /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27180 /* write only; format, flags, DMA pos */
+#define OFFSET_MASK 0x00000fff
+#define OFFSET_SHIFT 0x0
+#define IE_MASK 0x00001000 /* interrupt enable. */
+#define IE_SHIFT 0xc
+#define DIR_MASK 0x00002000 /* Direction */
+#define DIR_SHIFT 0xd
+#define FMT_MASK 0x0003c000
+#define FMT_SHIFT 0xe
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27100
+#define VORTEX_ADBDMA_BUFCFG1 0x27104
+#define VORTEX_ADBDMA_BUFBASE 0x27000
+#define VORTEX_ADBDMA_START 0x27c00 /* Which subbuffer starts */
+
+#define VORTEX_ADBDMA_STATUS 0x27A90 /* stored at AdbDma->this_10 / 2 DWORD in size. */
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27fd8 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27fe8 /* DMA subbuf, DMA pos */
+#define WT_SUBBUF_MASK 0x3
+#define WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27fc0
+#define VORTEX_WTDMA_BUFCFG0 0x27fd0
+#define VORTEX_WTDMA_BUFCFG1 0x27fd4
+#define VORTEX_WTDMA_START 0x27fe4 /* which subbuffer is first */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_COUNT 173
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_COUNT 24
+#define ROUTE_MASK 0xffff
+#define SOURCE_MASK 0xff00
+#define ADB_MASK 0xff
+#define ADB_SHIFT 0x8
+
+/* ADB address */
+#define OFFSET_ADBDMA 0x00
+#define OFFSET_SRCIN 0x40
+#define OFFSET_SRCOUT 0x20
+#define OFFSET_MIXIN 0x50
+#define OFFSET_MIXOUT 0x30
+#define OFFSET_CODECIN 0x70
+#define OFFSET_CODECOUT 0x88
+#define OFFSET_SPORTIN 0x78 /* ch 0x13 */
+#define OFFSET_SPORTOUT 0x90
+#define OFFSET_SPDIFOUT 0x92 /* ch 0x14 check this! */
+#define OFFSET_EQIN 0xa0
+#define OFFSET_EQOUT 0x7e /* 2 routes on ch 0x11 */
+#define OFFSET_XTALKOUT 0x66 /* crosstalk canceller (source) */
+#define OFFSET_XTALKIN 0x96 /* crosstalk canceller (sink) */
+#define OFFSET_A3DIN 0x70 /* ADB sink. */
+#define OFFSET_A3DOUT 0xA6 /* ADB source. 2 routes per slice = 8 */
+#define OFFSET_EFXIN 0x80 /* ADB sink. */
+#define OFFSET_EFXOUT 0x68 /* ADB source. */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFOUT(x) (x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT) /* 0x10 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+#define ADB_XTALKIN(x) (x + OFFSET_XTALKIN)
+#define ADB_XTALKOUT(x) (x + OFFSET_XTALKOUT)
+
+#define MIX_OUTL 0xe
+#define MIX_OUTR 0xf
+#define MIX_INL 0x1e
+#define MIX_INR 0x1f
+#define MIX_DEFIGAIN 0x08 /* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x08
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CLIP 0x21f80
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_ENIN 0x21a00 /* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x21c00 /* AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x21000 /* in? */
+#define VORTEX_MIX_INVOL_B 0x20000 /* out? */
+#define VORTEX_MIX_VOL_A 0x21800
+#define VORTEX_MIX_VOL_B 0x20800
+
+#define VOL_MIN 0x80 /* Input volume when muted. */
+#define VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRC_CHNBASE 0x26c40
+#define VORTEX_SRC_RTBASE 0x26c00
+#define VORTEX_SRCBLOCK_SR 0x26cc0
+#define VORTEX_SRC_SOURCE 0x26cc4
+#define VORTEX_SRC_SOURCESIZE 0x26cc8
+/* Params
+ 0x26e00 : 1 U0
+ 0x26e40 : 2 CR
+ 0x26e80 : 3 U3
+ 0x26ec0 : 4 DRIFT1
+ 0x26f00 : 5 U1
+ 0x26f40 : 6 DRIFT2
+ 0x26f80 : 7 U2 : Target rate, direction
+*/
+
+#define VORTEX_SRC_CONVRATIO 0x26e40
+#define VORTEX_SRC_DRIFT0 0x26e80
+#define VORTEX_SRC_DRIFT1 0x26ec0
+#define VORTEX_SRC_DRIFT2 0x26f40
+#define VORTEX_SRC_U0 0x26e00
+#define U0_SLOWLOCK 0x200
+#define VORTEX_SRC_U1 0x26f00
+#define VORTEX_SRC_U2 0x26f80
+#define VORTEX_SRC_DATA 0x26800 /* 0xc800 */
+#define VORTEX_SRC_DATA0 0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define FIFO_RDONLY 0x00000001
+#define FIFO_CTRL 0x00000002 /* Allow ctrl. ? */
+#define FIFO_VALID 0x00000010
+#define FIFO_EMPTY 0x00000020
+#define FIFO_U0 0x00001000 /* Unknown. */
+#define FIFO_U1 0x00010000
+#define FIFO_SIZE_BITS 5
+#define FIFO_SIZE (1<<FIFO_SIZE_BITS) // 0x20
+#define FIFO_MASK (FIFO_SIZE-1) //0x1f /* at shift left 0xc */
+//#define FIFO_MASK 0x1f /* at shift left 0xb */
+//#define FIFO_SIZE 0x20
+#define FIFO_BITS 0x03880000
+#define VORTEX_FIFO_ADBDATA 0x14000
+#define VORTEX_FIFO_WTDATA 0x10000
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x29184
+#define VORTEX_CODEC_EN 0x29190
+#define EN_CODEC0 0x00000300
+#define EN_AC98 0x00000c00 /* Modem AC98 slots. */
+#define EN_CODEC1 0x00003000
+#define EN_CODEC (EN_CODEC0 | EN_CODEC1)
+#define EN_SPORT 0x00030000
+#define EN_SPDIF 0x000c0000
+
+#define VORTEX_CODEC_CHN 0x29080
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000 /* 0x000f0000 */
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+#define VORTEX_CODEC_IO 0x29188
+
+/* SPDIF */
+#define VORTEX_SPDIF_FLAGS 0x2205c
+#define VORTEX_SPDIF_CFG0 0x291D0
+#define VORTEX_SPDIF_CFG1 0x291D4
+#define VORTEX_SPDIF_SMPRATE 0x29194
+
+/* Sample timer */
+#define VORTEX_SMP_TIME 0x29198
+
+#define VORTEX_MODEM_CTRL 0x291ac
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000 /* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x2a004 /* Interrupt source mask. */
+
+#define VORTEX_STAT 0x2a008 /* Status */
+
+#define VORTEX_CTRL 0x2a00c
+#define CTRL_MIDI_EN 0x00000001
+#define CTRL_MIDI_PORT 0x00000060
+#define CTRL_GAME_EN 0x00000008
+#define CTRL_GAME_PORT 0x00000e00
+//#define CTRL_IRQ_ENABLE 0x01004000
+#define CTRL_IRQ_ENABLE 0x00004000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x2919c
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL 0x27ae8
+#define ENGINE_INIT 0x1380000
+
+/* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x28800
+#define VORTEX_MIDI_CMD 0x28804 /* Write command / Read status */
+
+#define VORTEX_CTRL2 0x2880c
+#define CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_LEGACY 0x28808
+#define VORTEX_GAME_AXIS 0x28810
+#define AXIS_SIZE 4
+#define AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c
new file mode 100644
index 00000000000..d1fbcce0725
--- /dev/null
+++ b/sound/pci/au88x0/au8820.c
@@ -0,0 +1,15 @@
+#include "au8820.h"
+#include "au88x0.h"
+static struct pci_device_id snd_vortex_ids[] = {
+ {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+ {0,}
+};
+
+#include "au88x0_synth.c"
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_mixer.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h
new file mode 100644
index 00000000000..be8022e7871
--- /dev/null
+++ b/sound/pci/au88x0/au8820.h
@@ -0,0 +1,209 @@
+/*
+ Aureal Vortex Soundcard driver.
+
+ IO addr collected from asp4core.vxd:
+ function address
+ 0005D5A0 13004
+ 00080674 14004
+ 00080AFF 12818
+
+ */
+
+#define CHIP_AU8820
+
+#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME_SHORT "au8820"
+
+/* Number of ADB and WT channels */
+#define NR_ADB 0x10
+#define NR_WT 0x20
+#define NR_SRC 0x10
+#define NR_A3D 0x00
+#define NR_MIXIN 0x10
+#define NR_MIXOUT 0x10
+
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x105c0 /* read only, subbuffer, DMA pos */
+#define POS_MASK 0x00000fff
+#define POS_SHIFT 0x0
+#define ADB_SUBBUF_MASK 0x00003000 /* ADB only. */
+#define ADB_SUBBUF_SHIFT 0xc /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x10580 /* write only, format, flags, DMA pos */
+#define OFFSET_MASK 0x00000fff
+#define OFFSET_SHIFT 0x0
+#define IE_MASK 0x00001000 /* interrupt enable. */
+#define IE_SHIFT 0xc
+#define DIR_MASK 0x00002000 /* Direction. */
+#define DIR_SHIFT 0xd
+#define FMT_MASK 0x0003c000
+#define FMT_SHIFT 0xe
+// The masks and shift also work for the wtdma, if not specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x10400
+#define VORTEX_ADBDMA_BUFCFG1 0x10404
+#define VORTEX_ADBDMA_BUFBASE 0x10200
+#define VORTEX_ADBDMA_START 0x106c0 /* Which subbuffer starts */
+#define VORTEX_ADBDMA_STATUS 0x10600 /* stored at AdbDma->this_10 / 2 DWORD in size. */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x10a00 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x10800
+#define VORTEX_ADB_RTBASE_COUNT 103
+#define VORTEX_ADB_CHNBASE 0x1099c
+#define VORTEX_ADB_CHNBASE_COUNT 22
+#define ROUTE_MASK 0x3fff
+#define ADB_MASK 0x7f
+#define ADB_SHIFT 0x7
+//#define ADB_MIX_MASK 0xf
+/* ADB address */
+#define OFFSET_ADBDMA 0x00
+#define OFFSET_SRCOUT 0x10 /* on channel 0x11 */
+#define OFFSET_SRCIN 0x10 /* on channel < 0x11 */
+#define OFFSET_MIXOUT 0x20 /* source */
+#define OFFSET_MIXIN 0x30 /* sink */
+#define OFFSET_CODECIN 0x48 /* ADB source */
+#define OFFSET_CODECOUT 0x58 /* ADB sink/target */
+#define OFFSET_SPORTOUT 0x60 /* sink */
+#define OFFSET_SPORTIN 0x50 /* source */
+#define OFFSET_EFXOUT 0x50 /* sink */
+#define OFFSET_EFXIN 0x40 /* source */
+#define OFFSET_A3DOUT 0x00 /* This card has no HRTF :( */
+#define OFFSET_A3DIN 0x00
+#define OFFSET_WTOUT 0x58 /* */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x + OFFSET_ADBDMA)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN) /* */
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT) /* 8 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+#define ADB_WTOUT(x,y) (y + OFFSET_WTOUT)
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x10500 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x10500 /* DMA subbuf, DMA pos */
+#define WT_SUBBUF_MASK (0x3 << WT_SUBBUF_SHIFT)
+#define WT_SUBBUF_SHIFT 0x15
+#define VORTEX_WTDMA_BUFBASE 0x10000
+#define VORTEX_WTDMA_BUFCFG0 0x10300
+#define VORTEX_WTDMA_BUFCFG1 0x10304
+#define VORTEX_WTDMA_START 0x10640 /* which subbuffer is first */
+
+#define VORTEX_WT_BASE 0x9000
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x9f00
+#define VORTEX_MIXER_CLIP 0x9f80
+#define VORTEX_MIXER_CHNBASE 0x9e40
+#define VORTEX_MIXER_RTBASE 0x9e00
+#define MIXER_RTBASE_SIZE 0x26
+#define VORTEX_MIX_ENIN 0x9a00 /* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x9c00
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x9000 /* in? */
+#define VORTEX_MIX_INVOL_B 0x8000 /* out? */
+#define VORTEX_MIX_VOL_A 0x9800
+#define VORTEX_MIX_VOL_B 0x8800
+
+#define VOL_MIN 0x80 /* Input volume when muted. */
+#define VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+//#define MIX_OUTL 0xe
+//#define MIX_OUTR 0xf
+//#define MIX_INL 0xe
+//#define MIX_INR 0xf
+#define MIX_DEFIGAIN 0x08 /* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x08
+
+/* SRC */
+#define VORTEX_SRCBLOCK_SR 0xccc0
+#define VORTEX_SRC_CHNBASE 0xcc40
+#define VORTEX_SRC_RTBASE 0xcc00
+#define VORTEX_SRC_SOURCE 0xccc4
+#define VORTEX_SRC_SOURCESIZE 0xccc8
+#define VORTEX_SRC_U0 0xce00
+#define VORTEX_SRC_DRIFT0 0xce80
+#define VORTEX_SRC_DRIFT1 0xcec0
+#define VORTEX_SRC_U1 0xcf00
+#define VORTEX_SRC_DRIFT2 0xcf40
+#define VORTEX_SRC_U2 0xcf80
+#define VORTEX_SRC_DATA 0xc800
+#define VORTEX_SRC_DATA0 0xc000
+#define VORTEX_SRC_CONVRATIO 0xce40
+//#define SRC_RATIO(x) ((((x<<15)/48000) + 1)/2) /* Playback */
+//#define SRC_RATIO2(x) ((((48000<<15)/x) + 1)/2) /* Recording */
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0xf800 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0xf840
+#define FIFO_RDONLY 0x00000001
+#define FIFO_CTRL 0x00000002 /* Allow ctrl. ? */
+#define FIFO_VALID 0x00000010
+#define FIFO_EMPTY 0x00000020
+#define FIFO_U0 0x00001000 /* Unknown. */
+#define FIFO_U1 0x00010000
+#define FIFO_SIZE_BITS 5
+#define FIFO_SIZE (1<<FIFO_SIZE_BITS) // 0x20
+#define FIFO_MASK (FIFO_SIZE-1) //0x1f /* at shift left 0xc */
+#define VORTEX_FIFO_ADBDATA 0xe000
+#define VORTEX_FIFO_WTDATA 0xe800
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x11984
+#define VORTEX_CODEC_EN 0x11990
+#define EN_CODEC 0x00000300
+#define EN_SPORT 0x00030000
+#define EN_SPDIF 0x000c0000
+#define VORTEX_CODEC_CHN 0x11880
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000 /* 0x000f0000 */
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+#define VORTEX_CODEC_IO 0x11988
+
+#define VORTEX_SPDIF_FLAGS 0x1005c /* FIXME */
+#define VORTEX_SPDIF_CFG0 0x119D0
+#define VORTEX_SPDIF_CFG1 0x119D4
+#define VORTEX_SPDIF_SMPRATE 0x11994
+
+/* Sample timer */
+#define VORTEX_SMP_TIME 0x11998
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x12800 /* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x12804 /* Interrupt source mask. */
+
+#define VORTEX_STAT 0x12808 /* ?? */
+
+#define VORTEX_CTRL 0x1280c
+#define CTRL_MIDI_EN 0x00000001
+#define CTRL_MIDI_PORT 0x00000060
+#define CTRL_GAME_EN 0x00000008
+#define CTRL_GAME_PORT 0x00000e00
+#define CTRL_IRQ_ENABLE 0x4000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x1199c
+
+/* DMA */
+#define VORTEX_DMA_BUFFER 0x10200
+#define VORTEX_ENGINE_CTRL 0x1060c
+#define ENGINE_INIT 0x0L
+
+ /* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x11000
+#define VORTEX_MIDI_CMD 0x11004 /* Write command / Read status */
+#define VORTEX_GAME_LEGACY 0x11008
+#define VORTEX_CTRL2 0x1100c
+#define CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_AXIS 0x11010
+#define AXIS_SIZE 4
+#define AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c
new file mode 100644
index 00000000000..d4f2717c14f
--- /dev/null
+++ b/sound/pci/au88x0/au8830.c
@@ -0,0 +1,18 @@
+#include "au8830.h"
+#include "au88x0.h"
+static struct pci_device_id snd_vortex_ids[] = {
+ {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+ {0,}
+};
+
+#include "au88x0_synth.c"
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mixer.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_eq.c"
+#include "au88x0_a3d.c"
+#include "au88x0_xtalk.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h
new file mode 100644
index 00000000000..aa77826b5e5
--- /dev/null
+++ b/sound/pci/au88x0/au8830.h
@@ -0,0 +1,256 @@
+/*
+ Aureal Vortex Soundcard driver.
+
+ IO addr collected from asp4core.vxd:
+ function address
+ 0005D5A0 13004
+ 00080674 14004
+ 00080AFF 12818
+
+ */
+
+#define CHIP_AU8830
+
+#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME_SHORT "au8830"
+
+#define NR_ADB 0x20
+#define NR_SRC 0x10
+#define NR_A3D 0x10
+#define NR_MIXIN 0x20
+#define NR_MIXOUT 0x10
+#define NR_WT 0x40
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00 /* read only, subbuffer, DMA pos */
+#define POS_MASK 0x00000fff
+#define POS_SHIFT 0x0
+#define ADB_SUBBUF_MASK 0x00003000 /* ADB only. */
+#define ADB_SUBBUF_SHIFT 0xc /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27a00 /* write only; format, flags, DMA pos */
+#define OFFSET_MASK 0x00000fff
+#define OFFSET_SHIFT 0x0
+#define IE_MASK 0x00001000 /* interrupt enable. */
+#define IE_SHIFT 0xc
+#define DIR_MASK 0x00002000 /* Direction. */
+#define DIR_SHIFT 0xd
+#define FMT_MASK 0x0003c000
+#define FMT_SHIFT 0xe
+#define ADB_FIFO_EN_SHIFT 0x15
+#define ADB_FIFO_EN (1 << 0x15)
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27800
+#define VORTEX_ADBDMA_BUFCFG1 0x27804
+#define VORTEX_ADBDMA_BUFBASE 0x27400
+#define VORTEX_ADBDMA_START 0x27c00 /* Which subbuffer starts */
+
+#define VORTEX_ADBDMA_STATUS 0x27A90 /* stored at AdbDma->this_10 / 2 DWORD in size. */
+/* Starting at the MSB, each pair of bits seem to be the current DMA page. */
+/* This current page bits are consistent (same value) with VORTEX_ADBDMA_STAT) */
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL 0x27ae8
+#define ENGINE_INIT 0x1380000
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27900 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27d00 /* DMA subbuf, DMA pos */
+#define WT_SUBBUF_MASK 0x3
+#define WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27000
+#define VORTEX_WTDMA_BUFCFG0 0x27600
+#define VORTEX_WTDMA_BUFCFG1 0x27604
+#define VORTEX_WTDMA_START 0x27b00 /* which subbuffer is first */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_COUNT 173
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_COUNT 24
+#define ROUTE_MASK 0xffff
+#define SOURCE_MASK 0xff00
+#define ADB_MASK 0xff
+#define ADB_SHIFT 0x8
+/* ADB address */
+#define OFFSET_ADBDMA 0x00
+#define OFFSET_ADBDMAB 0x20
+#define OFFSET_SRCIN 0x40
+#define OFFSET_SRCOUT 0x20 /* ch 0x11 */
+#define OFFSET_MIXIN 0x50 /* ch 0x11 */
+#define OFFSET_MIXOUT 0x30 /* ch 0x11 */
+#define OFFSET_CODECIN 0x70 /* ch 0x11 */ /* adb source */
+#define OFFSET_CODECOUT 0x88 /* ch 0x11 */ /* adb target */
+#define OFFSET_SPORTIN 0x78 /* ch 0x13 ADB source. 2 routes. */
+#define OFFSET_SPORTOUT 0x90 /* ch 0x13 ADB sink. 2 routes. */
+#define OFFSET_SPDIFIN 0x7A /* ch 0x14 ADB source. */
+#define OFFSET_SPDIFOUT 0x92 /* ch 0x14 ADB sink. */
+#define OFFSET_AC98IN 0x7c /* ch 0x14 ADB source. */
+#define OFFSET_AC98OUT 0x94 /* ch 0x14 ADB sink. */
+#define OFFSET_EQIN 0xa0 /* ch 0x11 */
+#define OFFSET_EQOUT 0x7e /* ch 0x11 */ /* 2 routes on ch 0x11 */
+#define OFFSET_A3DIN 0x70 /* ADB sink. */
+#define OFFSET_A3DOUT 0xA6 /* ADB source. 2 routes per slice = 8 */
+#define OFFSET_WT0 0x40 /* WT bank 0 output. 0x40 - 0x65 */
+#define OFFSET_WT1 0x80 /* WT bank 1 output. 0x80 - 0xA5 */
+/* WT sources offset : 0x00-0x1f Direct stream. */
+/* WT sources offset : 0x20-0x25 Mixed Output. */
+#define OFFSET_XTALKOUT 0x66 /* crosstalk canceller (source) 2 routes */
+#define OFFSET_XTALKIN 0x96 /* crosstalk canceller (sink). 10 routes */
+#define OFFSET_EFXOUT 0x68 /* ADB source. 8 routes. */
+#define OFFSET_EFXIN 0x80 /* ADB sink. 8 routes. */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFIN(x) (x + OFFSET_SPDIFIN)
+#define ADB_SPDIFOUT(x) (x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT) /* 0x10 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+//#define ADB_WTOUT(x) ((x<x20)?(x + OFFSET_WT0):(x + OFFSET_WT1))
+#define ADB_WTOUT(x,y) (((x)==0)?((y) + OFFSET_WT0):((y) + OFFSET_WT1))
+#define ADB_XTALKIN(x) ((x) + OFFSET_XTALKIN)
+#define ADB_XTALKOUT(x) ((x) + OFFSET_XTALKOUT)
+
+#define MIX_DEFIGAIN 0x08
+#define MIX_DEFOGAIN 0x08 /* 0x8->6dB (6dB = x4) 16 to 18 bit conversion? */
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CLIP 0x21f80
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_ENIN 0x21a00 /* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x21c00 /* wave data buffers. AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_B 0x20000 /* Input volume current */
+#define VORTEX_MIX_VOL_B 0x20800 /* Output Volume current */
+#define VORTEX_MIX_INVOL_A 0x21000 /* Input Volume target */
+#define VORTEX_MIX_VOL_A 0x21800 /* Output Volume target */
+
+#define VOL_MIN 0x80 /* Input volume when muted. */
+#define VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRC_CHNBASE 0x26c40
+#define VORTEX_SRC_RTBASE 0x26c00
+#define VORTEX_SRCBLOCK_SR 0x26cc0
+#define VORTEX_SRC_SOURCE 0x26cc4
+#define VORTEX_SRC_SOURCESIZE 0x26cc8
+/* Params
+ 0x26e00 : 1 U0
+ 0x26e40 : 2 CR
+ 0x26e80 : 3 U3
+ 0x26ec0 : 4 DRIFT1
+ 0x26f00 : 5 U1
+ 0x26f40 : 6 DRIFT2
+ 0x26f80 : 7 U2 : Target rate, direction
+*/
+
+#define VORTEX_SRC_CONVRATIO 0x26e40
+#define VORTEX_SRC_DRIFT0 0x26e80
+#define VORTEX_SRC_DRIFT1 0x26ec0
+#define VORTEX_SRC_DRIFT2 0x26f40
+#define VORTEX_SRC_U0 0x26e00
+#define U0_SLOWLOCK 0x200
+#define VORTEX_SRC_U1 0x26f00
+#define VORTEX_SRC_U2 0x26f80
+#define VORTEX_SRC_DATA 0x26800 /* 0xc800 */
+#define VORTEX_SRC_DATA0 0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define FIFO_RDONLY 0x00000001
+#define FIFO_CTRL 0x00000002 /* Allow ctrl. ? */
+#define FIFO_VALID 0x00000010
+#define FIFO_EMPTY 0x00000020
+#define FIFO_U0 0x00002000 /* Unknown. */
+#define FIFO_U1 0x00040000
+#define FIFO_SIZE_BITS 6
+#define FIFO_SIZE (1<<(FIFO_SIZE_BITS)) // 0x40
+#define FIFO_MASK (FIFO_SIZE-1) //0x3f /* at shift left 0xc */
+#define FIFO_BITS 0x1c400000
+#define VORTEX_FIFO_ADBDATA 0x14000
+#define VORTEX_FIFO_WTDATA 0x10000
+
+#define VORTEX_FIFO_GIRT 0x17000 /* wt0, wt1, adb */
+#define GIRT_COUNT 3
+
+/* CODEC */
+
+#define VORTEX_CODEC_CHN 0x29080 /* The name "CHN" is wrong. */
+
+#define VORTEX_CODEC_CTRL 0x29184
+#define VORTEX_CODEC_IO 0x29188
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000 /* 0x000f0000 */
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+
+#define VORTEX_CODEC_SPORTCTRL 0x2918c
+
+#define VORTEX_CODEC_EN 0x29190
+#define EN_AUDIO0 0x00000300
+#define EN_MODEM 0x00000c00
+#define EN_AUDIO1 0x00003000
+#define EN_SPORT 0x00030000
+#define EN_SPDIF 0x000c0000
+#define EN_CODEC (EN_AUDIO1 | EN_AUDIO0)
+
+#define VORTEX_SPDIF_SMPRATE 0x29194
+
+#define VORTEX_SPDIF_FLAGS 0x2205c
+#define VORTEX_SPDIF_CFG0 0x291D0 /* status data */
+#define VORTEX_SPDIF_CFG1 0x291D4
+
+#define VORTEX_SMP_TIME 0x29198 /* Sample counter/timer */
+#define VORTEX_SMP_TIMER 0x2919c
+#define VORTEX_CODEC2_CTRL 0x291a0
+
+#define VORTEX_MODEM_CTRL 0x291ac
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000 /* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x2a004 /* Interrupt source mask. */
+
+//#define VORTEX_IRQ_U0 0x2a008 /* ?? */
+#define VORTEX_STAT 0x2a008 /* Some sort of status */
+#define STAT_IRQ 0x00000001 /* This bitis set if the IRQ is valid. */
+
+#define VORTEX_CTRL 0x2a00c
+#define CTRL_MIDI_EN 0x00000001
+#define CTRL_MIDI_PORT 0x00000060
+#define CTRL_GAME_EN 0x00000008
+#define CTRL_GAME_PORT 0x00000e00
+#define CTRL_IRQ_ENABLE 0x00004000
+#define CTRL_SPDIF 0x00000000 /* unknown. Please find this value */
+#define CTRL_SPORT 0x00200000
+#define CTRL_RST 0x00800000
+#define CTRL_UNKNOWN 0x01000000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x2919c
+
+ /* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x28800
+#define VORTEX_MIDI_CMD 0x28804 /* Write command / Read status */
+
+#define VORTEX_GAME_LEGACY 0x28808
+#define VORTEX_CTRL2 0x2880c
+#define CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_AXIS 0x28810 /* Axis base register. 4 axis's */
+#define AXIS_SIZE 4
+#define AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
new file mode 100644
index 00000000000..889b4a1a51a
--- /dev/null
+++ b/sound/pci/au88x0/au88x0.c
@@ -0,0 +1,388 @@
+/*
+ * ALSA driver for the Aureal Vortex family of soundprocessors.
+ * Author: Manuel Jander (mjander@embedded.cl)
+ *
+ * This driver is the result of the OpenVortex Project from Savannah
+ * (savannah.nongnu.org/projects/openvortex). I would like to thank
+ * the developers of OpenVortex, Jeff Muizelaar and Kester Maddock, from
+ * whom i got plenty of help, and their codebase was invaluable.
+ * Thanks to the ALSA developers, they helped a lot working out
+ * the ALSA part.
+ * Thanks also to Sourceforge for maintaining the old binary drivers,
+ * and the forum, where developers could comunicate.
+ *
+ * Now at least i can play Legacy DOOM with MIDI music :-)
+ */
+
+#include "au88x0.h"
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <sound/initval.h>
+
+// module parameters (see "Module Parameters")
+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 int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 };
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+module_param_array(pcifix, int, NULL, 0444);
+MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard.");
+
+MODULE_DESCRIPTION("Aureal vortex");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}");
+
+MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
+
+static void vortex_fix_latency(struct pci_dev *vortex)
+{
+ int rc;
+ if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) {
+ printk(KERN_INFO CARD_NAME
+ ": vortex latency is 0xff\n");
+ } else {
+ printk(KERN_WARNING CARD_NAME
+ ": could not set vortex latency: pci error 0x%x\n", rc);
+ }
+}
+
+static void vortex_fix_agp_bridge(struct pci_dev *via)
+{
+ int rc;
+ u8 value;
+
+ /*
+ * only set the bit (Extend PCI#2 Internal Master for
+ * Efficient Handling of Dummy Requests) if the can
+ * read the config and it is not already set
+ */
+
+ if (!(rc = pci_read_config_byte(via, 0x42, &value))
+ && ((value & 0x10)
+ || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) {
+ printk(KERN_INFO CARD_NAME
+ ": bridge config is 0x%x\n", value | 0x10);
+ } else {
+ printk(KERN_WARNING CARD_NAME
+ ": could not set vortex latency: pci error 0x%x\n", rc);
+ }
+}
+
+static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
+{
+ struct pci_dev *via;
+
+ /* autodetect if workarounds are required */
+ if (fix == 255) {
+ /* VIA KT133 */
+ via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8365_1, NULL);
+ /* VIA Apollo */
+ if (via == NULL) {
+ via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C598_1, NULL);
+ }
+ /* AMD Irongate */
+ if (via == NULL) {
+ via = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
+ }
+ if (via) {
+ printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n");
+ vortex_fix_latency(vortex);
+ vortex_fix_agp_bridge(via);
+ }
+ } else {
+ if (fix & 0x1)
+ vortex_fix_latency(vortex);
+ if ((fix & 0x2) && (via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8365_1, NULL)))
+ vortex_fix_agp_bridge(via);
+ if ((fix & 0x4) && (via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C598_1, NULL)))
+ vortex_fix_agp_bridge(via);
+ if ((fix & 0x8) && (via = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL)))
+ vortex_fix_agp_bridge(via);
+ }
+}
+
+// component-destructor
+// (see "Management of Cards and Components")
+static int snd_vortex_dev_free(snd_device_t * device)
+{
+ vortex_t *vortex = device->device_data;
+
+ vortex_gameport_unregister(vortex);
+ vortex_core_shutdown(vortex);
+ // Take down PCI interface.
+ synchronize_irq(vortex->irq);
+ free_irq(vortex->irq, vortex);
+ pci_release_regions(vortex->pci_dev);
+ pci_disable_device(vortex->pci_dev);
+ kfree(vortex);
+
+ return 0;
+}
+
+// chip-specific constructor
+// (see "Management of Cards and Components")
+static int __devinit
+snd_vortex_create(snd_card_t * card, struct pci_dev *pci, vortex_t ** rchip)
+{
+ vortex_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_vortex_dev_free,
+ };
+
+ *rchip = NULL;
+
+ // check PCI availability (DMA).
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ if (!pci_dma_supported(pci, VORTEX_DMA_MASK)) {
+ printk(KERN_ERR "error to set DMA mask\n");
+ return -ENXIO;
+ }
+ pci_set_dma_mask(pci, VORTEX_DMA_MASK);
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->card = card;
+
+ // initialize the stuff
+ chip->pci_dev = pci;
+ chip->io = pci_resource_start(pci, 0);
+ chip->vendor = pci->vendor;
+ chip->device = pci->device;
+ chip->card = card;
+ chip->irq = -1;
+
+ // (1) PCI resource allocation
+ // Get MMIO area
+ //
+ if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0)
+ goto regions_out;
+
+ chip->mmio = ioremap_nocache(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ if (!chip->mmio) {
+ printk(KERN_ERR "MMIO area remap failed.\n");
+ err = -ENOMEM;
+ goto ioremap_out;
+ }
+
+ /* Init audio core.
+ * This must be done before we do request_irq otherwise we can get spurious
+ * interupts that we do not handle properly and make a mess of things */
+ if ((err = vortex_core_init(chip)) != 0) {
+ printk(KERN_ERR "hw core init failed\n");
+ goto core_out;
+ }
+
+ if ((err = request_irq(pci->irq, vortex_interrupt,
+ SA_INTERRUPT | SA_SHIRQ, CARD_NAME_SHORT,
+ chip)) != 0) {
+ printk(KERN_ERR "cannot grab irq\n");
+ goto irq_out;
+ }
+ chip->irq = pci->irq;
+
+ pci_set_master(pci);
+ // End of PCI setup.
+
+ // Register alsa root device.
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ goto alloc_out;
+ }
+
+ *rchip = chip;
+
+ return 0;
+
+ alloc_out:
+ synchronize_irq(chip->irq);
+ free_irq(chip->irq, chip);
+ irq_out:
+ vortex_core_shutdown(chip);
+ core_out:
+ iounmap(chip->mmio);
+ ioremap_out:
+ pci_release_regions(chip->pci_dev);
+ regions_out:
+ pci_disable_device(chip->pci_dev);
+ //FIXME: this not the right place to unregister the gameport
+ vortex_gameport_unregister(chip);
+ return err;
+}
+
+// constructor -- see "Constructor" sub-section
+static int __devinit
+snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ vortex_t *chip;
+ int err;
+
+ // (1)
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+ // (2)
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ // (3)
+ if ((err = snd_vortex_create(card, pci, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_vortex_workaround(pci, pcifix[dev]);
+ // (4) Alloc components.
+ // ADB pcm.
+ if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#ifndef CHIP_AU8820
+ // ADB SPDIF
+ if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ // A3D
+ if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#endif
+ /*
+ // ADB I2S
+ if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ */
+#ifndef CHIP_AU8810
+ // WT pcm.
+ if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#endif
+ // snd_ac97_mixer and Vortex mixer.
+ if ((err = snd_vortex_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_vortex_midi(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ vortex_gameport_register(chip);
+
+#if 0
+ if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
+ sizeof(snd_vortex_synth_arg_t), &wave) < 0
+ || wave == NULL) {
+ snd_printk("Can't initialize Aureal wavetable synth\n");
+ } else {
+ snd_vortex_synth_arg_t *arg;
+
+ arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+ strcpy(wave->name, "Aureal Synth");
+ arg->hwptr = vortex;
+ arg->index = 1;
+ arg->seq_ports = seq_ports[dev];
+ arg->max_voices = max_synth_voices[dev];
+ }
+#endif
+
+ // (5)
+ strcpy(card->driver, CARD_NAME_SHORT);
+ strcpy(card->shortname, CARD_NAME_SHORT);
+ sprintf(card->longname, "%s at 0x%lx irq %i",
+ card->shortname, chip->io, chip->irq);
+
+ if ((err = pci_read_config_word(pci, PCI_DEVICE_ID,
+ &(chip->device))) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = pci_read_config_word(pci, PCI_VENDOR_ID,
+ &(chip->vendor))) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = pci_read_config_byte(pci, PCI_REVISION_ID,
+ &(chip->rev))) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#ifdef CHIP_AU8830
+ if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
+ printk(KERN_ALERT
+ "vortex: The revision (%x) of your card has not been seen before.\n",
+ chip->rev);
+ printk(KERN_ALERT
+ "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+ snd_card_free(card);
+ err = -ENODEV;
+ return err;
+ }
+#endif
+
+ // (6)
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ // (7)
+ pci_set_drvdata(pci, card);
+ dev++;
+ vortex_connect_default(chip, 1);
+ vortex_enable_int(chip);
+ return 0;
+}
+
+// destructor -- see "Destructor" sub-section
+static void __devexit snd_vortex_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+// pci_driver definition
+static struct pci_driver driver = {
+ .name = CARD_NAME_SHORT,
+ .id_table = snd_vortex_ids,
+ .probe = snd_vortex_probe,
+ .remove = __devexit_p(snd_vortex_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_vortex_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+// clean up the module
+static void __exit alsa_card_vortex_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_vortex_init)
+module_exit(alsa_card_vortex_exit)
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
new file mode 100644
index 00000000000..ee1ede1979f
--- /dev/null
+++ b/sound/pci/au88x0/au88x0.h
@@ -0,0 +1,284 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SOUND_AU88X0_H
+#define __SOUND_AU88X0_H
+
+#ifdef __KERNEL__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/hwdep.h>
+#include <sound/ac97_codec.h>
+
+#endif
+
+#ifndef CHIP_AU8820
+#include "au88x0_eq.h"
+#include "au88x0_a3d.h"
+#endif
+#ifndef CHIP_AU8810
+#include "au88x0_wt.h"
+#endif
+
+#define VORTEX_DMA_MASK 0xffffffff
+
+#define hwread(x,y) readl((x)+((y)>>2))
+#define hwwrite(x,y,z) writel((z),(x)+((y)>>2))
+
+/* Vortex MPU401 defines. */
+#define MIDI_CLOCK_DIV 0x61
+/* Standart MPU401 defines. */
+#define MPU401_RESET 0xff
+#define MPU401_ENTER_UART 0x3f
+#define MPU401_ACK 0xfe
+
+// Get src register value to convert from x to y.
+#define SRC_RATIO(x,y) ((((x<<15)/y) + 1)/2)
+
+/* FIFO software state constants. */
+#define FIFO_STOP 0
+#define FIFO_START 1
+#define FIFO_PAUSE 2
+
+/* IRQ flags */
+#define IRQ_ERR_MASK 0x00ff
+#define IRQ_FATAL 0x0001
+#define IRQ_PARITY 0x0002
+#define IRQ_REG 0x0004
+#define IRQ_FIFO 0x0008
+#define IRQ_DMA 0x0010
+#define IRQ_PCMOUT 0x0020 /* PCM OUT page crossing */
+#define IRQ_TIMER 0x1000
+#define IRQ_MIDI 0x2000
+#define IRQ_MODEM 0x4000
+
+/* ADB Resource */
+#define VORTEX_RESOURCE_DMA 0x00000000
+#define VORTEX_RESOURCE_SRC 0x00000001
+#define VORTEX_RESOURCE_MIXIN 0x00000002
+#define VORTEX_RESOURCE_MIXOUT 0x00000003
+#define VORTEX_RESOURCE_A3D 0x00000004
+#define VORTEX_RESOURCE_LAST 0x00000005
+
+/* Check for SDAC bit in "Extended audio ID" AC97 register */
+//#define VORTEX_IS_QUAD(x) (((x)->codec == NULL) ? 0 : ((x)->codec->ext_id&0x80))
+#define VORTEX_IS_QUAD(x) ((x)->isquad)
+/* Check if chip has bug. */
+#define IS_BAD_CHIP(x) (\
+ (x->rev == 0xfe && x->device == PCI_DEVICE_ID_AUREAL_VORTEX_2) || \
+ (x->rev == 0xfe && x->device == PCI_DEVICE_ID_AUREAL_ADVANTAGE))
+
+
+/* PCM devices */
+#define VORTEX_PCM_ADB 0
+#define VORTEX_PCM_SPDIF 1
+#define VORTEX_PCM_A3D 2
+#define VORTEX_PCM_WT 3
+#define VORTEX_PCM_I2S 4
+#define VORTEX_PCM_LAST 5
+
+#define MIX_CAPT(x) (vortex->mixcapt[x])
+#define MIX_PLAYB(x) (vortex->mixplayb[x])
+#define MIX_SPDIF(x) (vortex->mixspdif[x])
+
+#define NR_WTPB 0x20 /* WT channels per eahc bank. */
+
+/* Structs */
+typedef struct {
+ //int this_08; /* Still unknown */
+ int fifo_enabled; /* this_24 */
+ int fifo_status; /* this_1c */
+ int dma_ctrl; /* this_78 (ADB), this_7c (WT) */
+ int dma_unknown; /* this_74 (ADB), this_78 (WT). WDM: +8 */
+ int cfg0;
+ int cfg1;
+
+ int nr_ch; /* Nr of PCM channels in use */
+ int type; /* Output type (ac97, a3d, spdif, i2s, dsp) */
+ int dma; /* Hardware DMA index. */
+ int dir; /* Stream Direction. */
+ u32 resources[5];
+
+ /* Virtual page extender stuff */
+ int nr_periods;
+ int period_bytes;
+ snd_pcm_sgbuf_t *sgbuf; /* DMA Scatter Gather struct */
+ int period_real;
+ int period_virt;
+
+ snd_pcm_substream_t *substream;
+} stream_t;
+
+typedef struct snd_vortex vortex_t;
+struct snd_vortex {
+ /* ALSA structs. */
+ snd_card_t *card;
+ snd_pcm_t *pcm[VORTEX_PCM_LAST];
+
+ snd_rawmidi_t *rmidi; /* Legacy Midi interface. */
+ ac97_t *codec;
+
+ /* Stream structs. */
+ stream_t dma_adb[NR_ADB];
+ int spdif_sr;
+#ifndef CHIP_AU8810
+ stream_t dma_wt[NR_WT];
+ wt_voice_t wt_voice[NR_WT]; /* WT register cache. */
+ char mixwt[(NR_WT / NR_WTPB) * 6]; /* WT mixin objects */
+#endif
+
+ /* Global resources */
+ s8 mixcapt[2];
+ s8 mixplayb[4];
+#ifndef CHIP_AU8820
+ s8 mixspdif[2];
+ s8 mixa3d[2]; /* mixers which collect all a3d streams. */
+ s8 mixxtlk[2]; /* crosstalk canceler mixer inputs. */
+#endif
+ u32 fixed_res[5];
+
+#ifndef CHIP_AU8820
+ /* Hardware equalizer structs */
+ eqlzr_t eq;
+ /* A3D structs */
+ a3dsrc_t a3d[NR_A3D];
+ /* Xtalk canceler */
+ int xt_mode; /* 1: speakers, 0:headphones. */
+#endif
+
+ int isquad; /* cache of extended ID codec flag. */
+
+ /* Gameport stuff. */
+ struct gameport *gameport;
+
+ /* PCI hardware resources */
+ unsigned long io;
+ unsigned long __iomem *mmio;
+ unsigned int irq;
+ spinlock_t lock;
+
+ /* PCI device */
+ struct pci_dev *pci_dev;
+ u16 vendor;
+ u16 device;
+ u8 rev;
+};
+
+/* Functions. */
+
+/* SRC */
+static void vortex_adb_setsrc(vortex_t * vortex, int adbdma,
+ unsigned int cvrt, int dir);
+
+/* DMA Engines. */
+static void vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
+ snd_pcm_sgbuf_t * sgbuf, int size,
+ int count);
+static void vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie,
+ int dir, int fmt, int d,
+ unsigned long offset);
+static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb);
+#ifndef CHIP_AU8810
+static void vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
+ snd_pcm_sgbuf_t * sgbuf, int size,
+ int count);
+static void vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d, /*int e, */
+ unsigned long offset);
+static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb);
+#endif
+
+static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma);
+//static void vortex_adbdma_stopfifo(vortex_t *vortex, int adbdma);
+static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma);
+static void vortex_adbdma_resumefifo(vortex_t * vortex, int adbdma);
+static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma);
+static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma);
+
+#ifndef CHIP_AU8810
+static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma);
+static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma);
+#endif
+
+/* global stuff. */
+static void vortex_codec_init(vortex_t * vortex);
+static void vortex_codec_write(ac97_t * codec, unsigned short addr,
+ unsigned short data);
+static unsigned short vortex_codec_read(ac97_t * codec, unsigned short addr);
+static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode);
+
+static int vortex_core_init(vortex_t * card);
+static int vortex_core_shutdown(vortex_t * card);
+static void vortex_enable_int(vortex_t * card);
+static irqreturn_t vortex_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs);
+static int vortex_alsafmt_aspfmt(int alsafmt);
+
+/* Connection stuff. */
+static void vortex_connect_default(vortex_t * vortex, int en);
+static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
+ int dir, int type);
+static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
+ int restype);
+#ifndef CHIP_AU8810
+static int vortex_wt_allocroute(vortex_t * vortex, int dma, int nr_ch);
+static void vortex_wt_connect(vortex_t * vortex, int en);
+static void vortex_wt_init(vortex_t * vortex);
+#endif
+
+static void vortex_route(vortex_t * vortex, int en, unsigned char channel,
+ unsigned char source, unsigned char dest);
+#if 0
+static void vortex_routes(vortex_t * vortex, int en, unsigned char channel,
+ unsigned char source, unsigned char dest0,
+ unsigned char dest1);
+#endif
+static void vortex_connection_mixin_mix(vortex_t * vortex, int en,
+ unsigned char mixin,
+ unsigned char mix, int a);
+static void vortex_mix_setinputvolumebyte(vortex_t * vortex,
+ unsigned char mix, int mixin,
+ unsigned char vol);
+static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
+ unsigned char vol);
+
+/* A3D functions. */
+#ifndef CHIP_AU8820
+static void vortex_Vort3D(vortex_t * v, int en);
+static void vortex_Vort3D_connect(vortex_t * vortex, int en);
+static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en);
+#endif
+
+/* Driver stuff. */
+static int __devinit vortex_gameport_register(vortex_t * card);
+static void vortex_gameport_unregister(vortex_t * card);
+#ifndef CHIP_AU8820
+static int __devinit vortex_eq_init(vortex_t * vortex);
+static int __devexit vortex_eq_free(vortex_t * vortex);
+#endif
+/* ALSA stuff. */
+static int __devinit snd_vortex_new_pcm(vortex_t * vortex, int idx, int nr);
+static int __devinit snd_vortex_mixer(vortex_t * vortex);
+static int __devinit snd_vortex_midi(vortex_t * vortex);
+#endif
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
new file mode 100644
index 00000000000..9ea2ba7bc3c
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -0,0 +1,914 @@
+/***************************************************************************
+ * au88x0_a3d.c
+ *
+ * Fri Jul 18 14:16:22 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.net
+ *
+ * A3D. You may think i'm crazy, but this may work someday. Who knows...
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "au88x0_a3d.h"
+#include "au88x0_a3ddata.c"
+#include "au88x0_xtalk.h"
+#include "au88x0.h"
+
+static void
+a3dsrc_SetTimeConsts(a3dsrc_t * a, short HrtfTrack, short ItdTrack,
+ short GTrack, short CTrack)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_HrtfTrackTC), HrtfTrack);
+ hwwrite(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_ITDTrackTC), ItdTrack);
+ hwwrite(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_GainTrackTC), GTrack);
+ hwwrite(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_CoeffTrackTC), CTrack);
+}
+
+#if 0
+static void
+a3dsrc_GetTimeConsts(a3dsrc_t * a, short *HrtfTrack, short *ItdTrack,
+ short *GTrack, short *CTrack)
+{
+ // stub!
+}
+
+#endif
+/* Atmospheric absorbtion. */
+
+static void
+a3dsrc_SetAtmosTarget(a3dsrc_t * a, short aa, short b, short c, short d,
+ short e)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_A21Target),
+ (e << 0x10) | d);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_B10Target),
+ (b << 0x10) | aa);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_B2Target), c);
+}
+
+static void
+a3dsrc_SetAtmosCurrent(a3dsrc_t * a, short aa, short b, short c, short d,
+ short e)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_A12Current),
+ (e << 0x10) | d);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_B01Current),
+ (b << 0x10) | aa);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_B2Current), c);
+}
+
+static void
+a3dsrc_SetAtmosState(a3dsrc_t * a, short x1, short x2, short y1, short y2)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_x1), x1);
+ hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_x2), x2);
+ hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_y1), y1);
+ hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_y2), y2);
+}
+
+#if 0
+static void
+a3dsrc_GetAtmosTarget(a3dsrc_t * a, short *aa, short *b, short *c,
+ short *d, short *e)
+{
+}
+static void
+a3dsrc_GetAtmosCurrent(a3dsrc_t * a, short *bb01, short *ab01, short *b2,
+ short *aa12, short *ba12)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *aa12 =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_A12Current));
+ *ba12 =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_A12Current));
+ *ab01 =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_B01Current));
+ *bb01 =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_B01Current));
+ *b2 =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_B2Current));
+}
+
+static void
+a3dsrc_GetAtmosState(a3dsrc_t * a, short *x1, short *x2, short *y1, short *y2)
+{
+
+}
+
+#endif
+/* HRTF */
+
+static void
+a3dsrc_SetHrtfTarget(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+
+ for (i = 0; i < HRTF_SZ; i++)
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source,
+ A3D_B_HrtfTarget) + (i << 2),
+ (b[i] << 0x10) | aa[i]);
+}
+
+static void
+a3dsrc_SetHrtfCurrent(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+
+ for (i = 0; i < HRTF_SZ; i++)
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source,
+ A3D_B_HrtfCurrent) + (i << 2),
+ (b[i] << 0x10) | aa[i]);
+}
+
+static void
+a3dsrc_SetHrtfState(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+
+ for (i = 0; i < HRTF_SZ; i++)
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source,
+ A3D_B_HrtfDelayLine) + (i << 2),
+ (b[i] << 0x10) | aa[i]);
+}
+
+static void a3dsrc_SetHrtfOutput(a3dsrc_t * a, short left, short right)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_HrtfOutL), left);
+ hwwrite(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_HrtfOutR), right);
+}
+
+#if 0
+static void a3dsrc_GetHrtfTarget(a3dsrc_t * a, a3d_Hrtf_t aa, a3d_Hrtf_t b)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+
+ for (i = 0; i < HRTF_SZ; i++)
+ aa[i] =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source,
+ A3D_A_HrtfTarget + (i << 2)));
+ for (i = 0; i < HRTF_SZ; i++)
+ b[i] =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source,
+ A3D_B_HrtfTarget + (i << 2)));
+}
+
+static void a3dsrc_GetHrtfCurrent(a3dsrc_t * a, a3d_Hrtf_t aa, a3d_Hrtf_t b)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+
+ for (i = 0; i < HRTF_SZ; i++)
+ aa[i] =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source,
+ A3D_A_HrtfCurrent + (i << 2)));
+ for (i = 0; i < HRTF_SZ; i++)
+ b[i] =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source,
+ A3D_B_HrtfCurrent + (i << 2)));
+}
+
+static void a3dsrc_GetHrtfState(a3dsrc_t * a, a3d_Hrtf_t aa, a3d_Hrtf_t b)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+ // FIXME: verify this!
+ for (i = 0; i < HRTF_SZ; i++)
+ aa[i] =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source,
+ A3D_A_HrtfDelayLine + (i << 2)));
+ for (i = 0; i < HRTF_SZ; i++)
+ b[i] =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source,
+ A3D_B_HrtfDelayLine + (i << 2)));
+}
+
+static void a3dsrc_GetHrtfOutput(a3dsrc_t * a, short *left, short *right)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *left =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_HrtfOutL));
+ *right =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_HrtfOutR));
+}
+
+#endif
+
+/* Interaural Time Difference.
+ * "The other main clue that humans use to locate sounds, is called
+ * Interaural Time Difference (ITD). The differences in distance from
+ * the sound source to a listeners ears means that the sound will
+ * reach one ear slightly before the other....", found somewhere with google.*/
+static void a3dsrc_SetItdTarget(a3dsrc_t * a, short litd, short ritd)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+
+ if (litd < 0)
+ litd = 0;
+ if (litd > 0x57FF)
+ litd = 0x57FF;
+ if (ritd < 0)
+ ritd = 0;
+ if (ritd > 0x57FF)
+ ritd = 0x57FF;
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_ITDTarget),
+ (ritd << 0x10) | litd);
+ //hwwrite(vortex->mmio, addr(0x191DF+5, this04, this08), (ritd<<0x10)|litd);
+}
+
+static void a3dsrc_SetItdCurrent(a3dsrc_t * a, short litd, short ritd)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+
+ if (litd < 0)
+ litd = 0;
+ if (litd > 0x57FF)
+ litd = 0x57FF;
+ if (ritd < 0)
+ ritd = 0;
+ if (ritd > 0x57FF)
+ ritd = 0x57FF;
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_ITDCurrent),
+ (ritd << 0x10) | litd);
+ //hwwrite(vortex->mmio, addr(0x191DF+1, this04, this08), (ritd<<0x10)|litd);
+}
+
+static void a3dsrc_SetItdDline(a3dsrc_t * a, a3d_ItdDline_t const dline)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+ /* 45 != 40 -> Check this ! */
+ for (i = 0; i < DLINE_SZ; i++)
+ hwwrite(vortex->mmio,
+ a3d_addrA(a->slice, a->source,
+ A3D_A_ITDDelayLine) + (i << 2), dline[i]);
+}
+
+#if 0
+static void a3dsrc_GetItdTarget(a3dsrc_t * a, short *litd, short *ritd)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *ritd =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_ITDTarget));
+ *litd =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_ITDTarget));
+}
+
+static void a3dsrc_GetItdCurrent(a3dsrc_t * a, short *litd, short *ritd)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+
+ *ritd =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_ITDCurrent));
+ *litd =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_ITDCurrent));
+}
+
+static void a3dsrc_GetItdDline(a3dsrc_t * a, a3d_ItdDline_t dline)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+
+ for (i = 0; i < DLINE_SZ; i++)
+ dline[i] =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source,
+ A3D_A_ITDDelayLine + (i << 2)));
+}
+
+#endif
+/* This is may be used for ILD Interaural Level Difference. */
+
+static void a3dsrc_SetGainTarget(a3dsrc_t * a, short left, short right)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_GainTarget),
+ (right << 0x10) | left);
+}
+
+static void a3dsrc_SetGainCurrent(a3dsrc_t * a, short left, short right)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_GainCurrent),
+ (right << 0x10) | left);
+}
+
+#if 0
+static void a3dsrc_GetGainTarget(a3dsrc_t * a, short *left, short *right)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *right =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_GainTarget));
+ *left =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_GainTarget));
+}
+
+static void a3dsrc_GetGainCurrent(a3dsrc_t * a, short *left, short *right)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *right =
+ hwread(vortex->mmio,
+ a3d_addrA(a->slice, a->source, A3D_A_GainCurrent));
+ *left =
+ hwread(vortex->mmio,
+ a3d_addrB(a->slice, a->source, A3D_B_GainCurrent));
+}
+
+/* CA3dIO this func seems to be inlined all over this place. */
+static void CA3dIO_WriteReg(a3dsrc_t * a, unsigned long addr, short aa, short b)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio, addr, (aa << 0x10) | b);
+}
+
+#endif
+/* Generic A3D stuff */
+
+static void a3dsrc_SetA3DSampleRate(a3dsrc_t * a, int sr)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int esp0 = 0;
+
+ esp0 = (((esp0 & 0x7fffffff) | 0xB8000000) & 0x7) | ((sr & 0x1f) << 3);
+ hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd), esp0);
+ //hwwrite(vortex->mmio, 0x19C38 + (this08<<0xd), esp0);
+}
+
+static void a3dsrc_EnableA3D(a3dsrc_t * a)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd),
+ 0xF0000001);
+ //hwwrite(vortex->mmio, 0x19C38 + (this08<<0xd), 0xF0000001);
+}
+
+static void a3dsrc_DisableA3D(a3dsrc_t * a)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd),
+ 0xF0000000);
+}
+
+static void a3dsrc_SetA3DControlReg(a3dsrc_t * a, unsigned long ctrl)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd), ctrl);
+}
+
+static void a3dsrc_SetA3DPointerReg(a3dsrc_t * a, unsigned long ptr)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ hwwrite(vortex->mmio, A3D_SLICE_Pointers + ((a->slice) << 0xd), ptr);
+}
+
+#if 0
+static void a3dsrc_GetA3DSampleRate(a3dsrc_t * a, int *sr)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *sr = ((hwread(vortex->mmio, A3D_SLICE_Control + (a->slice << 0xd))
+ >> 3) & 0x1f);
+ //*sr = ((hwread(vortex->mmio, 0x19C38 + (this08<<0xd))>>3)&0x1f);
+}
+
+static void a3dsrc_GetA3DControlReg(a3dsrc_t * a, unsigned long *ctrl)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *ctrl = hwread(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd));
+}
+
+static void a3dsrc_GetA3DPointerReg(a3dsrc_t * a, unsigned long *ptr)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ *ptr = hwread(vortex->mmio, A3D_SLICE_Pointers + ((a->slice) << 0xd));
+}
+
+#endif
+static void a3dsrc_ZeroSliceIO(a3dsrc_t * a)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+ int i;
+
+ for (i = 0; i < 8; i++)
+ hwwrite(vortex->mmio,
+ A3D_SLICE_VDBDest +
+ ((((a->slice) << 0xb) + i) << 2), 0);
+ for (i = 0; i < 4; i++)
+ hwwrite(vortex->mmio,
+ A3D_SLICE_VDBSource +
+ ((((a->slice) << 0xb) + i) << 2), 0);
+}
+
+/* Reset Single A3D source. */
+static void a3dsrc_ZeroState(a3dsrc_t * a)
+{
+
+ //printk("vortex: ZeroState slice: %d, source %d\n", a->slice, a->source);
+
+ a3dsrc_SetAtmosState(a, 0, 0, 0, 0);
+ a3dsrc_SetHrtfState(a, A3dHrirZeros, A3dHrirZeros);
+ a3dsrc_SetItdDline(a, A3dItdDlineZeros);
+ a3dsrc_SetHrtfOutput(a, 0, 0);
+ a3dsrc_SetTimeConsts(a, 0, 0, 0, 0);
+
+ a3dsrc_SetAtmosCurrent(a, 0, 0, 0, 0, 0);
+ a3dsrc_SetAtmosTarget(a, 0, 0, 0, 0, 0);
+ a3dsrc_SetItdCurrent(a, 0, 0);
+ a3dsrc_SetItdTarget(a, 0, 0);
+ a3dsrc_SetGainCurrent(a, 0, 0);
+ a3dsrc_SetGainTarget(a, 0, 0);
+
+ a3dsrc_SetHrtfCurrent(a, A3dHrirZeros, A3dHrirZeros);
+ a3dsrc_SetHrtfTarget(a, A3dHrirZeros, A3dHrirZeros);
+}
+
+/* Reset entire A3D engine */
+static void a3dsrc_ZeroStateA3D(a3dsrc_t * a)
+{
+ int i, var, var2;
+
+ if ((a->vortex) == NULL) {
+ printk("vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
+ return;
+ }
+
+ a3dsrc_SetA3DControlReg(a, 0);
+ a3dsrc_SetA3DPointerReg(a, 0);
+
+ var = a->slice;
+ var2 = a->source;
+ for (i = 0; i < 4; i++) {
+ a->slice = i;
+ a3dsrc_ZeroSliceIO(a);
+ //a3dsrc_ZeroState(a);
+ }
+ a->source = var2;
+ a->slice = var;
+}
+
+/* Program A3D block as pass through */
+static void a3dsrc_ProgramPipe(a3dsrc_t * a)
+{
+ a3dsrc_SetTimeConsts(a, 0, 0, 0, 0);
+ a3dsrc_SetAtmosCurrent(a, 0, 0x4000, 0, 0, 0);
+ a3dsrc_SetAtmosTarget(a, 0x4000, 0, 0, 0, 0);
+ a3dsrc_SetItdCurrent(a, 0, 0);
+ a3dsrc_SetItdTarget(a, 0, 0);
+ a3dsrc_SetGainCurrent(a, 0x7fff, 0x7fff);
+ a3dsrc_SetGainTarget(a, 0x7fff, 0x7fff);
+
+ /* SET HRTF HERE */
+
+ /* Single spike leads to identity transfer function. */
+ a3dsrc_SetHrtfCurrent(a, A3dHrirImpulse, A3dHrirImpulse);
+ a3dsrc_SetHrtfTarget(a, A3dHrirImpulse, A3dHrirImpulse);
+
+ /* Test: Sounds saturated. */
+ //a3dsrc_SetHrtfCurrent(a, A3dHrirSatTest, A3dHrirSatTest);
+ //a3dsrc_SetHrtfTarget(a, A3dHrirSatTest, A3dHrirSatTest);
+}
+
+/* VDB = Vortex audio Dataflow Bus */
+#if 0
+static void a3dsrc_ClearVDBData(a3dsrc_t * a, unsigned long aa)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+
+ // ((aa >> 2) << 8) - (aa >> 2)
+ hwwrite(vortex->mmio,
+ a3d_addrS(a->slice, A3D_SLICE_VDBDest) + (a->source << 2), 0);
+ hwwrite(vortex->mmio,
+ a3d_addrS(a->slice,
+ A3D_SLICE_VDBDest + 4) + (a->source << 2), 0);
+ /*
+ hwwrite(vortex->mmio, 0x19c00 + (((aa>>2)*255*4)+aa)*8, 0);
+ hwwrite(vortex->mmio, 0x19c04 + (((aa>>2)*255*4)+aa)*8, 0);
+ */
+}
+#endif
+
+/* A3D HwSource stuff. */
+
+static void vortex_A3dSourceHw_Initialize(vortex_t * v, int source, int slice)
+{
+ a3dsrc_t *a3dsrc = &(v->a3d[source + (slice * 4)]);
+ //a3dsrc_t *a3dsrc = &(v->a3d[source + (slice*4)]);
+
+ a3dsrc->vortex = (void *)v;
+ a3dsrc->source = source; /* source */
+ a3dsrc->slice = slice; /* slice */
+ a3dsrc_ZeroState(a3dsrc);
+ /* Added by me. */
+ a3dsrc_SetA3DSampleRate(a3dsrc, 0x11);
+}
+
+static int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
+{
+ v->xt_mode = mode; /* this_14 */
+
+ vortex_XtalkHw_init(v);
+ vortex_XtalkHw_SetGainsAllChan(v);
+ switch (v->xt_mode) {
+ case XT_SPEAKER0:
+ vortex_XtalkHw_ProgramXtalkNarrow(v);
+ break;
+ case XT_SPEAKER1:
+ vortex_XtalkHw_ProgramXtalkWide(v);
+ break;
+ default:
+ case XT_HEADPHONE:
+ vortex_XtalkHw_ProgramPipe(v);
+ break;
+ case XT_DIAMOND:
+ vortex_XtalkHw_ProgramDiamondXtalk(v);
+ break;
+ }
+ vortex_XtalkHw_SetSampleRate(v, 0x11);
+ vortex_XtalkHw_Enable(v);
+ return 0;
+}
+
+/* 3D Sound entry points. */
+
+static int vortex_a3d_register_controls(vortex_t * vortex);
+static void vortex_a3d_unregister_controls(vortex_t * vortex);
+/* A3D base support init/shudown */
+static void vortex_Vort3D(vortex_t * v, int en)
+{
+ int i;
+ if (en) {
+ Vort3DRend_Initialize(v, XT_HEADPHONE);
+ for (i = 0; i < NR_A3D; i++) {
+ vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2);
+ a3dsrc_ZeroStateA3D(&(v->a3d[0]));
+ }
+ } else {
+ vortex_XtalkHw_Disable(v);
+ }
+ /* Register ALSA controls */
+ if (en) {
+ vortex_a3d_register_controls(v);
+ } else {
+ vortex_a3d_unregister_controls(v);
+ }
+}
+
+/* Make A3D subsystem connections. */
+static void vortex_Vort3D_connect(vortex_t * v, int en)
+{
+ int i;
+
+// Disable AU8810 routes, since they seem to be wrong (in au8810.h).
+#ifdef CHIP_AU8810
+ return;
+#endif
+
+#if 1
+ /* Alloc Xtalk mixin resources */
+ v->mixxtlk[0] =
+ vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+ if (v->mixxtlk[0] < 0) {
+ printk
+ ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ return;
+ }
+ v->mixxtlk[1] =
+ vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+ if (v->mixxtlk[1] < 0) {
+ printk
+ ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ return;
+ }
+#endif
+
+ /* Connect A3D -> XTALK */
+ for (i = 0; i < 4; i++) {
+ // 2 outputs per each A3D slice.
+ vortex_route(v, en, 0x11, ADB_A3DOUT(i * 2), ADB_XTALKIN(i));
+ vortex_route(v, en, 0x11, ADB_A3DOUT(i * 2) + 1, ADB_XTALKIN(5 + i));
+ }
+#if 0
+ vortex_route(v, en, 0x11, ADB_XTALKOUT(0), ADB_EQIN(2));
+ vortex_route(v, en, 0x11, ADB_XTALKOUT(1), ADB_EQIN(3));
+#else
+ /* Connect XTalk -> mixer */
+ vortex_route(v, en, 0x11, ADB_XTALKOUT(0), ADB_MIXIN(v->mixxtlk[0]));
+ vortex_route(v, en, 0x11, ADB_XTALKOUT(1), ADB_MIXIN(v->mixxtlk[1]));
+ vortex_connection_mixin_mix(v, en, v->mixxtlk[0], v->mixplayb[0], 0);
+ vortex_connection_mixin_mix(v, en, v->mixxtlk[1], v->mixplayb[1], 0);
+ vortex_mix_setinputvolumebyte(v, v->mixplayb[0], v->mixxtlk[0],
+ en ? MIX_DEFIGAIN : VOL_MIN);
+ vortex_mix_setinputvolumebyte(v, v->mixplayb[1], v->mixxtlk[1],
+ en ? MIX_DEFIGAIN : VOL_MIN);
+ if (VORTEX_IS_QUAD(v)) {
+ vortex_connection_mixin_mix(v, en, v->mixxtlk[0],
+ v->mixplayb[2], 0);
+ vortex_connection_mixin_mix(v, en, v->mixxtlk[1],
+ v->mixplayb[3], 0);
+ vortex_mix_setinputvolumebyte(v, v->mixplayb[2],
+ v->mixxtlk[0],
+ en ? MIX_DEFIGAIN : VOL_MIN);
+ vortex_mix_setinputvolumebyte(v, v->mixplayb[3],
+ v->mixxtlk[1],
+ en ? MIX_DEFIGAIN : VOL_MIN);
+ }
+#endif
+}
+
+/* Initialize one single A3D source. */
+static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en)
+{
+ if (a->vortex == NULL) {
+ printk
+ ("vortex: Vort3D_InitializeSource: A3D source not initialized\n");
+ return;
+ }
+ if (en) {
+ a3dsrc_ProgramPipe(a);
+ a3dsrc_SetA3DSampleRate(a, 0x11);
+ a3dsrc_SetTimeConsts(a, HrtfTCDefault,
+ ItdTCDefault, GainTCDefault,
+ CoefTCDefault);
+ /* Remark: zero gain is muted. */
+ //a3dsrc_SetGainTarget(a,0,0);
+ //a3dsrc_SetGainCurrent(a,0,0);
+ a3dsrc_EnableA3D(a);
+ } else {
+ a3dsrc_DisableA3D(a);
+ a3dsrc_ZeroState(a);
+ }
+}
+
+/* Conversion of coordinates into 3D parameters. */
+
+static void vortex_a3d_coord2hrtf(a3d_Hrtf_t hrtf, int *coord)
+{
+ /* FIXME: implement this. */
+
+}
+static void vortex_a3d_coord2itd(a3d_Itd_t itd, int *coord)
+{
+ /* FIXME: implement this. */
+
+}
+static void vortex_a3d_coord2ild(a3d_LRGains_t ild, int left, int right)
+{
+ /* FIXME: implement this. */
+
+}
+static void vortex_a3d_translate_filter(a3d_atmos_t filter, int *params)
+{
+ /* FIXME: implement this. */
+
+}
+
+/* ALSA control interface. */
+
+static int
+snd_vortex_a3d_hrtf_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 6;
+ uinfo->value.integer.min = 0x00000000;
+ uinfo->value.integer.max = 0xffffffff;
+ return 0;
+}
+static int
+snd_vortex_a3d_itd_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0x00000000;
+ uinfo->value.integer.max = 0xffffffff;
+ return 0;
+}
+static int
+snd_vortex_a3d_ild_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0x00000000;
+ uinfo->value.integer.max = 0xffffffff;
+ return 0;
+}
+static int
+snd_vortex_a3d_filter_info(snd_kcontrol_t *
+ kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 4;
+ uinfo->value.integer.min = 0x00000000;
+ uinfo->value.integer.max = 0xffffffff;
+ return 0;
+}
+
+static int
+snd_vortex_a3d_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ //a3dsrc_t *a = kcontrol->private_data;
+ /* No read yet. Would this be really useable/needed ? */
+
+ return 0;
+}
+
+static int
+snd_vortex_a3d_hrtf_put(snd_kcontrol_t *
+ kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ int changed = 1, i;
+ int coord[6];
+ for (i = 0; i < 6; i++)
+ coord[i] = ucontrol->value.integer.value[i];
+ /* Translate orientation coordinates to a3d params. */
+ vortex_a3d_coord2hrtf(a->hrtf[0], coord);
+ vortex_a3d_coord2hrtf(a->hrtf[1], coord);
+ a3dsrc_SetHrtfTarget(a, a->hrtf[0], a->hrtf[1]);
+ a3dsrc_SetHrtfCurrent(a, a->hrtf[0], a->hrtf[1]);
+ return changed;
+}
+
+static int
+snd_vortex_a3d_itd_put(snd_kcontrol_t *
+ kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ int coord[6];
+ int i, changed = 1;
+ for (i = 0; i < 6; i++)
+ coord[i] = ucontrol->value.integer.value[i];
+ /* Translate orientation coordinates to a3d params. */
+ vortex_a3d_coord2itd(a->hrtf[0], coord);
+ vortex_a3d_coord2itd(a->hrtf[1], coord);
+ /* Inter aural time difference. */
+ a3dsrc_SetItdTarget(a, a->itd[0], a->itd[1]);
+ a3dsrc_SetItdCurrent(a, a->itd[0], a->itd[1]);
+ a3dsrc_SetItdDline(a, a->dline);
+ return changed;
+}
+
+static int
+snd_vortex_a3d_ild_put(snd_kcontrol_t *
+ kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ int changed = 1;
+ int l, r;
+ /* There may be some scale tranlation needed here. */
+ l = ucontrol->value.integer.value[0];
+ r = ucontrol->value.integer.value[1];
+ vortex_a3d_coord2ild(a->ild, l, r);
+ /* Left Right panning. */
+ a3dsrc_SetGainTarget(a, l, r);
+ a3dsrc_SetGainCurrent(a, l, r);
+ return changed;
+}
+
+static int
+snd_vortex_a3d_filter_put(snd_kcontrol_t
+ * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ int i, changed = 1;
+ int params[6];
+ for (i = 0; i < 6; i++)
+ params[i] = ucontrol->value.integer.value[i];
+ /* Translate generic filter params to a3d filter params. */
+ vortex_a3d_translate_filter(a->filter, params);
+ /* Atmospheric absorbtion and filtering. */
+ a3dsrc_SetAtmosTarget(a, a->filter[0],
+ a->filter[1], a->filter[2],
+ a->filter[3], a->filter[4]);
+ a3dsrc_SetAtmosCurrent(a, a->filter[0],
+ a->filter[1], a->filter[2],
+ a->filter[3], a->filter[4]);
+ return changed;
+}
+
+static snd_kcontrol_new_t vortex_a3d_kcontrol __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Playback PCM advanced processing",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_vortex_a3d_hrtf_info,
+ .get = snd_vortex_a3d_get,
+ .put = snd_vortex_a3d_hrtf_put,
+};
+
+/* Control (un)registration. */
+static int vortex_a3d_register_controls(vortex_t * vortex)
+{
+ snd_kcontrol_t *kcontrol;
+ int err, i;
+ /* HRTF controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ if ((kcontrol =
+ snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_HRTF;
+ kcontrol->info = snd_vortex_a3d_hrtf_info;
+ kcontrol->put = snd_vortex_a3d_hrtf_put;
+ if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ return err;
+ }
+ /* ITD controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ if ((kcontrol =
+ snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_ITD;
+ kcontrol->info = snd_vortex_a3d_itd_info;
+ kcontrol->put = snd_vortex_a3d_itd_put;
+ if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ return err;
+ }
+ /* ILD (gains) controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ if ((kcontrol =
+ snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_GAINS;
+ kcontrol->info = snd_vortex_a3d_ild_info;
+ kcontrol->put = snd_vortex_a3d_ild_put;
+ if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ return err;
+ }
+ /* Filter controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ if ((kcontrol =
+ snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_FILTER;
+ kcontrol->info = snd_vortex_a3d_filter_info;
+ kcontrol->put = snd_vortex_a3d_filter_put;
+ if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ return err;
+ }
+ return 0;
+}
+
+static void vortex_a3d_unregister_controls(vortex_t * vortex)
+{
+
+}
+
+/* End of File*/
diff --git a/sound/pci/au88x0/au88x0_a3d.h b/sound/pci/au88x0/au88x0_a3d.h
new file mode 100644
index 00000000000..0584c65bcab
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3d.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * au88x0_a3d.h
+ *
+ * Fri Jul 18 14:16:03 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.net
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _AU88X0_A3D_H
+#define _AU88X0_A3D_H
+
+//#include <openal.h>
+
+#define HRTF_SZ 0x38
+#define DLINE_SZ 0x28
+
+#define CTRLID_HRTF 1
+#define CTRLID_ITD 2
+#define CTRLID_ILD 4
+#define CTRLID_FILTER 8
+#define CTRLID_GAINS 16
+
+/* 3D parameter structs */
+typedef unsigned short int a3d_Hrtf_t[HRTF_SZ];
+typedef unsigned short int a3d_ItdDline_t[DLINE_SZ];
+typedef unsigned short int a3d_atmos_t[5];
+typedef unsigned short int a3d_LRGains_t[2];
+typedef unsigned short int a3d_Itd_t[2];
+typedef unsigned short int a3d_Ild_t[2];
+
+typedef struct {
+ void *vortex; // Formerly CAsp4HwIO*, now vortex_t*.
+ unsigned int source; /* this_04 */
+ unsigned int slice; /* this_08 */
+ a3d_Hrtf_t hrtf[2];
+ a3d_Itd_t itd;
+ a3d_Ild_t ild;
+ a3d_ItdDline_t dline;
+ a3d_atmos_t filter;
+} a3dsrc_t;
+
+/* First Register bank */
+
+#define A3D_A_HrtfCurrent 0x18000 /* 56 ULONG */
+#define A3D_A_GainCurrent 0x180E0
+#define A3D_A_GainTarget 0x180E4
+#define A3D_A_A12Current 0x180E8 /* Atmospheric current. */
+#define A3D_A_A21Target 0x180EC /* Atmospheric target */
+#define A3D_A_B01Current 0x180F0 /* Atmospheric current */
+#define A3D_A_B10Target 0x180F4 /* Atmospheric target */
+#define A3D_A_B2Current 0x180F8 /* Atmospheric current */
+#define A3D_A_B2Target 0x180FC /* Atmospheric target */
+#define A3D_A_HrtfTarget 0x18100 /* 56 ULONG */
+#define A3D_A_ITDCurrent 0x181E0
+#define A3D_A_ITDTarget 0x181E4
+#define A3D_A_HrtfDelayLine 0x181E8 /* 56 ULONG */
+#define A3D_A_ITDDelayLine 0x182C8 /* 40/45 ULONG */
+#define A3D_A_HrtfTrackTC 0x1837C /* Time Constants */
+#define A3D_A_GainTrackTC 0x18380
+#define A3D_A_CoeffTrackTC 0x18384
+#define A3D_A_ITDTrackTC 0x18388
+#define A3D_A_x1 0x1838C
+#define A3D_A_x2 0x18390
+#define A3D_A_y1 0x18394
+#define A3D_A_y2 0x18398
+#define A3D_A_HrtfOutL 0x1839C
+#define A3D_A_HrtfOutR 0x183A0
+#define A3D_A_TAIL 0x183A4
+
+/* Second register bank */
+#define A3D_B_HrtfCurrent 0x19000 /* 56 ULONG */
+#define A3D_B_GainCurrent 0x190E0
+#define A3D_B_GainTarget 0x190E4
+#define A3D_B_A12Current 0x190E8
+#define A3D_B_A21Target 0x190EC
+#define A3D_B_B01Current 0x190F0
+#define A3D_B_B10Target 0x190F4
+#define A3D_B_B2Current 0x190F8
+#define A3D_B_B2Target 0x190FC
+#define A3D_B_HrtfTarget 0x19100 /* 56 ULONG */
+#define A3D_B_ITDCurrent 0x191E0
+#define A3D_B_ITDTarget 0x191E4
+#define A3D_B_HrtfDelayLine 0x191E8 /* 56 ULONG */
+#define A3D_B_TAIL 0x192C8
+
+/* There are 4 slices, 4 a3d each = 16 a3d sources. */
+#define A3D_SLICE_BANK_A 0x18000 /* 4 sources */
+#define A3D_SLICE_BANK_B 0x19000 /* 4 sources */
+#define A3D_SLICE_VDBDest 0x19C00 /* 8 ULONG */
+#define A3D_SLICE_VDBSource 0x19C20 /* 4 ULONG */
+#define A3D_SLICE_ABReg 0x19C30
+#define A3D_SLICE_CReg 0x19C34
+#define A3D_SLICE_Control 0x19C38
+#define A3D_SLICE_DebugReserved 0x19C3c /* Dangerous! */
+#define A3D_SLICE_Pointers 0x19C40
+#define A3D_SLICE_TAIL 0x1A000
+
+// Slice size: 0x2000
+// Source size: 0x3A4, 0x2C8
+
+/* Address generator macro. */
+#define a3d_addrA(slice,source,reg) (((slice)<<0xd)+((source)*0x3A4)+(reg))
+#define a3d_addrB(slice,source,reg) (((slice)<<0xd)+((source)*0x2C8)+(reg))
+#define a3d_addrS(slice,reg) (((slice)<<0xd)+(reg))
+//#define a3d_addr(slice,source,reg) (((reg)>=0x19000) ? a3d_addr2((slice),(source),(reg)) : a3d_addr1((slice),(source),(reg)))
+
+#endif /* _AU88X0_A3D_H */
diff --git a/sound/pci/au88x0/au88x0_a3ddata.c b/sound/pci/au88x0/au88x0_a3ddata.c
new file mode 100644
index 00000000000..6fab4bba5a0
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3ddata.c
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * au88x0_a3ddata.c
+ *
+ * Wed Nov 19 21:11:32 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Constant initializer values. */
+
+static const a3d_Hrtf_t A3dHrirZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0
+};
+
+static const a3d_Hrtf_t A3dHrirImpulse = {
+ 0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0
+};
+
+static const a3d_Hrtf_t A3dHrirOnes = {
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff,
+ 0x7fff,
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff,
+ 0x7fff,
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff,
+ 0x7fff,
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff,
+ 0x7fff,
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff
+};
+
+static const a3d_Hrtf_t A3dHrirSatTest = {
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff,
+ 0x7fff,
+ 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001,
+ 0x8001,
+ 0x8001,
+ 0x7fff, 0x0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const a3d_Hrtf_t A3dHrirDImpulse = {
+ 0, 0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0
+};
+
+static const a3d_ItdDline_t A3dItdDlineZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static short const GainTCDefault = 0x300;
+static short const ItdTCDefault = 0x0C8;
+static short const HrtfTCDefault = 0x147;
+static short const CoefTCDefault = 0x300;
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
new file mode 100644
index 00000000000..f0eda4bbbb3
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -0,0 +1,2837 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ Vortex core low level functions.
+
+ Author: Manuel Jander (mjander@users.sourceforge.cl)
+ These functions are mainly the result of translations made
+ from the original disassembly of the au88x0 binary drivers,
+ written by Aureal before they went down.
+ Many thanks to the Jeff Muizelaar, Kester Maddock, and whoever
+ contributed to the OpenVortex project.
+ The author of this file, put the few available pieces together
+ and translated the rest of the riddle (Mix, Src and connection stuff).
+ Some things are still to be discovered, and their meanings are unclear.
+
+ Some of these functions aren't intended to be really used, rather
+ to help to understand how does the AU88X0 chips work. Keep them in, because
+ they could be used somewhere in the future.
+
+ This code hasn't been tested or proof read thoroughly. If you wanna help,
+ take a look at the AU88X0 assembly and check if this matches.
+ Functions tested ok so far are (they show the desired effect
+ at least):
+ vortex_routes(); (1 bug fixed).
+ vortex_adb_addroute();
+ vortex_adb_addroutes();
+ vortex_connect_codecplay();
+ vortex_src_flushbuffers();
+ vortex_adbdma_setmode(); note: still some unknown arguments!
+ vortex_adbdma_startfifo();
+ vortex_adbdma_stopfifo();
+ vortex_fifo_setadbctrl(); note: still some unknown arguments!
+ vortex_mix_setinputvolumebyte();
+ vortex_mix_enableinput();
+ vortex_mixer_addWTD(); (fixed)
+ vortex_connection_adbdma_src_src();
+ vortex_connection_adbdma_src();
+ vortex_src_change_convratio();
+ vortex_src_addWTD(); (fixed)
+
+ History:
+
+ 01-03-2003 First revision.
+ 01-21-2003 Some bug fixes.
+ 17-02-2003 many bugfixes after a big versioning mess.
+ 18-02-2003 JAAAAAHHHUUUUUU!!!! The mixer works !! I'm just so happy !
+ (2 hours later...) I cant believe it! Im really lucky today.
+ Now the SRC is working too! Yeah! XMMS works !
+ 20-02-2003 First steps into the ALSA world.
+ 28-02-2003 As my birthday present, i discovered how the DMA buffer pages really
+ work :-). It was all wrong.
+ 12-03-2003 ALSA driver starts working (2 channels).
+ 16-03-2003 More srcblock_setupchannel discoveries.
+ 12-04-2003 AU8830 playback support. Recording in the works.
+ 17-04-2003 vortex_route() and vortex_routes() bug fixes. AU8830 recording
+ works now, but chipn' dale effect is still there.
+ 16-05-2003 SrcSetupChannel cleanup. Moved the Src setup stuff entirely
+ into au88x0_pcm.c .
+ 06-06-2003 Buffer shifter bugfix. Mixer volume fix.
+ 07-12-2003 A3D routing finally fixed. Believed to be OK.
+ 25-03-2004 Many thanks to Claudia, for such valuable bug reports.
+
+*/
+
+#include "au88x0.h"
+#include "au88x0_a3d.h"
+#include <linux/delay.h>
+
+/* MIXER (CAsp4Mix.s and CAsp4Mixer.s) */
+
+// FIXME: get rid of this.
+static int mchannels[NR_MIXIN];
+static int rampchs[NR_MIXIN];
+
+static void vortex_mixer_en_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_MIXER_SR,
+ hwread(vortex->mmio, VORTEX_MIXER_SR) | (0x1 << channel));
+}
+static void vortex_mixer_dis_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_MIXER_SR,
+ hwread(vortex->mmio, VORTEX_MIXER_SR) & ~(0x1 << channel));
+}
+
+#if 0
+static void
+vortex_mix_muteinputgain(vortex_t * vortex, unsigned char mix,
+ unsigned char channel)
+{
+ hwwrite(vortex->mmio, VORTEX_MIX_INVOL_A + ((mix << 5) + channel),
+ 0x80);
+ hwwrite(vortex->mmio, VORTEX_MIX_INVOL_B + ((mix << 5) + channel),
+ 0x80);
+}
+
+static int vortex_mix_getvolume(vortex_t * vortex, unsigned char mix)
+{
+ int a;
+ a = hwread(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2)) & 0xff;
+ //FP2LinearFrac(a);
+ return (a);
+}
+
+static int
+vortex_mix_getinputvolume(vortex_t * vortex, unsigned char mix,
+ int channel, int *vol)
+{
+ int a;
+ if (!(mchannels[mix] & (1 << channel)))
+ return 0;
+ a = hwread(vortex->mmio,
+ VORTEX_MIX_INVOL_A + (((mix << 5) + channel) << 2));
+ /*
+ if (rampchs[mix] == 0)
+ a = FP2LinearFrac(a);
+ else
+ a = FP2LinearFracWT(a);
+ */
+ *vol = a;
+ return (0);
+}
+
+static unsigned int vortex_mix_boost6db(unsigned char vol)
+{
+ return (vol + 8); /* WOW! what a complex function! */
+}
+
+static void vortex_mix_rampvolume(vortex_t * vortex, int mix)
+{
+ int ch;
+ char a;
+ // This function is intended for ramping down only (see vortex_disableinput()).
+ for (ch = 0; ch < 0x20; ch++) {
+ if (((1 << ch) & rampchs[mix]) == 0)
+ continue;
+ a = hwread(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + ch) << 2));
+ if (a > -126) {
+ a -= 2;
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_A +
+ (((mix << 5) + ch) << 2), a);
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_B +
+ (((mix << 5) + ch) << 2), a);
+ } else
+ vortex_mix_killinput(vortex, mix, ch);
+ }
+}
+
+static int
+vortex_mix_getenablebit(vortex_t * vortex, unsigned char mix, int mixin)
+{
+ int addr, temp;
+ if (mixin >= 0)
+ addr = mixin;
+ else
+ addr = mixin + 3;
+ addr = ((mix << 3) + (addr >> 2)) << 2;
+ temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+ return ((temp >> (mixin & 3)) & 1);
+}
+#endif
+static void
+vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
+ unsigned char vol)
+{
+ int temp;
+ hwwrite(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2), vol);
+ if (1) { /*if (this_10) */
+ temp = hwread(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2));
+ if ((temp != 0x80) || (vol == 0x80))
+ return;
+ }
+ hwwrite(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2), vol);
+}
+
+static void
+vortex_mix_setinputvolumebyte(vortex_t * vortex, unsigned char mix,
+ int mixin, unsigned char vol)
+{
+ int temp;
+
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_A + (((mix << 5) + mixin) << 2), vol);
+ if (1) { /* this_10, initialized to 1. */
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2));
+ if ((temp != 0x80) || (vol == 0x80))
+ return;
+ }
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), vol);
+}
+
+static void
+vortex_mix_setenablebit(vortex_t * vortex, unsigned char mix, int mixin, int en)
+{
+ int temp, addr;
+
+ if (mixin < 0)
+ addr = (mixin + 3);
+ else
+ addr = mixin;
+ addr = ((mix << 3) + (addr >> 2)) << 2;
+ temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+ if (en)
+ temp |= (1 << (mixin & 3));
+ else
+ temp &= ~(1 << (mixin & 3));
+ /* Mute input. Astatic void crackling? */
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), 0x80);
+ /* Looks like clear buffer. */
+ hwwrite(vortex->mmio, VORTEX_MIX_SMP + (mixin << 2), 0x0);
+ hwwrite(vortex->mmio, VORTEX_MIX_SMP + 4 + (mixin << 2), 0x0);
+ /* Write enable bit. */
+ hwwrite(vortex->mmio, VORTEX_MIX_ENIN + addr, temp);
+}
+
+static void
+vortex_mix_killinput(vortex_t * vortex, unsigned char mix, int mixin)
+{
+ rampchs[mix] &= ~(1 << mixin);
+ vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80);
+ mchannels[mix] &= ~(1 << mixin);
+ vortex_mix_setenablebit(vortex, mix, mixin, 0);
+}
+
+static void
+vortex_mix_enableinput(vortex_t * vortex, unsigned char mix, int mixin)
+{
+ vortex_mix_killinput(vortex, mix, mixin);
+ if ((mchannels[mix] & (1 << mixin)) == 0) {
+ vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80); /*0x80 : mute */
+ mchannels[mix] |= (1 << mixin);
+ }
+ vortex_mix_setenablebit(vortex, mix, mixin, 1);
+}
+
+static void
+vortex_mix_disableinput(vortex_t * vortex, unsigned char mix, int channel,
+ int ramp)
+{
+ if (ramp) {
+ rampchs[mix] |= (1 << channel);
+ // Register callback.
+ //vortex_mix_startrampvolume(vortex);
+ vortex_mix_killinput(vortex, mix, channel);
+ } else
+ vortex_mix_killinput(vortex, mix, channel);
+}
+
+static int
+vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
+{
+ int temp, lifeboat = 0, prev;
+
+ temp = hwread(vortex->mmio, VORTEX_MIXER_SR);
+ if ((temp & (1 << ch)) == 0) {
+ hwwrite(vortex->mmio, VORTEX_MIXER_CHNBASE + (ch << 2), mix);
+ vortex_mixer_en_sr(vortex, ch);
+ return 1;
+ }
+ prev = VORTEX_MIXER_CHNBASE + (ch << 2);
+ temp = hwread(vortex->mmio, prev);
+ while (temp & 0x10) {
+ prev = VORTEX_MIXER_RTBASE + ((temp & 0xf) << 2);
+ temp = hwread(vortex->mmio, prev);
+ //printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
+ if ((++lifeboat) > 0xf) {
+ printk(KERN_ERR
+ "vortex_mixer_addWTD: lifeboat overflow\n");
+ return 0;
+ }
+ }
+ hwwrite(vortex->mmio, VORTEX_MIXER_RTBASE + ((temp & 0xf) << 2), mix);
+ hwwrite(vortex->mmio, prev, (temp & 0xf) | 0x10);
+ return 1;
+}
+
+static int
+vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
+{
+ int esp14 = -1, esp18, eax, ebx, edx, ebp, esi = 0;
+ //int esp1f=edi(while)=src, esp10=ch;
+
+ eax = hwread(vortex->mmio, VORTEX_MIXER_SR);
+ if (((1 << ch) & eax) == 0) {
+ printk(KERN_ERR "mix ALARM %x\n", eax);
+ return 0;
+ }
+ ebp = VORTEX_MIXER_CHNBASE + (ch << 2);
+ esp18 = hwread(vortex->mmio, ebp);
+ if (esp18 & 0x10) {
+ ebx = (esp18 & 0xf);
+ if (mix == ebx) {
+ ebx = VORTEX_MIXER_RTBASE + (mix << 2);
+ edx = hwread(vortex->mmio, ebx);
+ //7b60
+ hwwrite(vortex->mmio, ebp, edx);
+ hwwrite(vortex->mmio, ebx, 0);
+ } else {
+ //7ad3
+ edx =
+ hwread(vortex->mmio,
+ VORTEX_MIXER_RTBASE + (ebx << 2));
+ //printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
+ while ((edx & 0xf) != mix) {
+ if ((esi) > 0xf) {
+ printk(KERN_ERR
+ "vortex: mixdelWTD: error lifeboat overflow\n");
+ return 0;
+ }
+ esp14 = ebx;
+ ebx = edx & 0xf;
+ ebp = ebx << 2;
+ edx =
+ hwread(vortex->mmio,
+ VORTEX_MIXER_RTBASE + ebp);
+ //printk(KERN_INFO "vortex: mixdelWTD: while addr=%x, val=%x\n", ebp, edx);
+ esi++;
+ }
+ //7b30
+ ebp = ebx << 2;
+ if (edx & 0x10) { /* Delete entry in between others */
+ ebx = VORTEX_MIXER_RTBASE + ((edx & 0xf) << 2);
+ edx = hwread(vortex->mmio, ebx);
+ //7b60
+ hwwrite(vortex->mmio,
+ VORTEX_MIXER_RTBASE + ebp, edx);
+ hwwrite(vortex->mmio, ebx, 0);
+ //printk(KERN_INFO "vortex mixdelWTD between addr= 0x%x, val= 0x%x\n", ebp, edx);
+ } else { /* Delete last entry */
+ //7b83
+ if (esp14 == -1)
+ hwwrite(vortex->mmio,
+ VORTEX_MIXER_CHNBASE +
+ (ch << 2), esp18 & 0xef);
+ else {
+ ebx = (0xffffffe0 & edx) | (0xf & ebx);
+ hwwrite(vortex->mmio,
+ VORTEX_MIXER_RTBASE +
+ (esp14 << 2), ebx);
+ //printk(KERN_INFO "vortex mixdelWTD last addr= 0x%x, val= 0x%x\n", esp14, ebx);
+ }
+ hwwrite(vortex->mmio,
+ VORTEX_MIXER_RTBASE + ebp, 0);
+ return 1;
+ }
+ }
+ } else {
+ //printk(KERN_INFO "removed last mix\n");
+ //7be0
+ vortex_mixer_dis_sr(vortex, ch);
+ hwwrite(vortex->mmio, ebp, 0);
+ }
+ return 1;
+}
+
+static void vortex_mixer_init(vortex_t * vortex)
+{
+ unsigned long addr;
+ int x;
+
+ // FIXME: get rid of this crap.
+ memset(mchannels, 0, NR_MIXOUT * sizeof(int));
+ memset(rampchs, 0, NR_MIXOUT * sizeof(int));
+
+ addr = VORTEX_MIX_SMP + 0x17c;
+ for (x = 0x5f; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0);
+ addr -= 4;
+ }
+ addr = VORTEX_MIX_ENIN + 0x1fc;
+ for (x = 0x7f; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0);
+ addr -= 4;
+ }
+ addr = VORTEX_MIX_SMP + 0x17c;
+ for (x = 0x5f; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0);
+ addr -= 4;
+ }
+ addr = VORTEX_MIX_INVOL_A + 0x7fc;
+ for (x = 0x1ff; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0x80);
+ addr -= 4;
+ }
+ addr = VORTEX_MIX_VOL_A + 0x3c;
+ for (x = 0xf; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0x80);
+ addr -= 4;
+ }
+ addr = VORTEX_MIX_INVOL_B + 0x7fc;
+ for (x = 0x1ff; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0x80);
+ addr -= 4;
+ }
+ addr = VORTEX_MIX_VOL_B + 0x3c;
+ for (x = 0xf; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0x80);
+ addr -= 4;
+ }
+ addr = VORTEX_MIXER_RTBASE + (MIXER_RTBASE_SIZE - 1) * 4;
+ for (x = (MIXER_RTBASE_SIZE - 1); x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0x0);
+ addr -= 4;
+ }
+ hwwrite(vortex->mmio, VORTEX_MIXER_SR, 0);
+
+ /* Set clipping ceiling (this may be all wrong). */
+ /*
+ for (x = 0; x > 0x80; x++) {
+ hwwrite(vortex->mmio, VORTEX_MIXER_CLIP + (x << 2), 0x3ffff);
+ }
+ */
+ /*
+ call CAsp4Mix__Initialize_CAsp4HwIO____CAsp4Mixer____
+ Register ISR callback for volume smooth fade out.
+ Maybe this avoids clicks when press "stop" ?
+ */
+}
+
+/* SRC (CAsp4Src.s and CAsp4SrcBlock) */
+
+static void vortex_src_en_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_SRCBLOCK_SR,
+ hwread(vortex->mmio, VORTEX_SRCBLOCK_SR) | (0x1 << channel));
+}
+
+static void vortex_src_dis_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_SRCBLOCK_SR,
+ hwread(vortex->mmio, VORTEX_SRCBLOCK_SR) & ~(0x1 << channel));
+}
+
+static void vortex_src_flushbuffers(vortex_t * vortex, unsigned char src)
+{
+ int i;
+
+ for (i = 0x1f; i >= 0; i--)
+ hwwrite(vortex->mmio,
+ VORTEX_SRC_DATA0 + (src << 7) + (i << 2), 0);
+ hwwrite(vortex->mmio, VORTEX_SRC_DATA + (src << 3), 0);
+ hwwrite(vortex->mmio, VORTEX_SRC_DATA + (src << 3) + 4, 0);
+}
+
+static void vortex_src_cleardrift(vortex_t * vortex, unsigned char src)
+{
+ hwwrite(vortex->mmio, VORTEX_SRC_DRIFT0 + (src << 2), 0);
+ hwwrite(vortex->mmio, VORTEX_SRC_DRIFT1 + (src << 2), 0);
+ hwwrite(vortex->mmio, VORTEX_SRC_DRIFT2 + (src << 2), 1);
+}
+
+static void
+vortex_src_set_throttlesource(vortex_t * vortex, unsigned char src, int en)
+{
+ int temp;
+
+ temp = hwread(vortex->mmio, VORTEX_SRC_SOURCE);
+ if (en)
+ temp |= 1 << src;
+ else
+ temp &= ~(1 << src);
+ hwwrite(vortex->mmio, VORTEX_SRC_SOURCE, temp);
+}
+
+static int
+vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio)
+{
+ int temp, lifeboat = 0;
+
+ do {
+ hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio);
+ temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
+ if ((++lifeboat) > 0x9) {
+ printk(KERN_ERR "Vortex: Src cvr fail\n");
+ break;
+ }
+ }
+ while (temp != ratio);
+ return temp;
+}
+
+#if 0
+static void vortex_src_slowlock(vortex_t * vortex, unsigned char src)
+{
+ int temp;
+
+ hwwrite(vortex->mmio, VORTEX_SRC_DRIFT2 + (src << 2), 1);
+ hwwrite(vortex->mmio, VORTEX_SRC_DRIFT0 + (src << 2), 0);
+ temp = hwread(vortex->mmio, VORTEX_SRC_U0 + (src << 2));
+ if (temp & 0x200)
+ hwwrite(vortex->mmio, VORTEX_SRC_U0 + (src << 2),
+ temp & ~0x200L);
+}
+
+static void
+vortex_src_change_convratio(vortex_t * vortex, unsigned char src, int ratio)
+{
+ int temp, a;
+
+ if ((ratio & 0x10000) && (ratio != 0x10000)) {
+ if (ratio & 0x3fff)
+ a = (0x11 - ((ratio >> 0xe) & 0x3)) - 1;
+ else
+ a = (0x11 - ((ratio >> 0xe) & 0x3)) - 2;
+ } else
+ a = 0xc;
+ temp = hwread(vortex->mmio, VORTEX_SRC_U0 + (src << 2));
+ if (((temp >> 4) & 0xf) != a)
+ hwwrite(vortex->mmio, VORTEX_SRC_U0 + (src << 2),
+ (temp & 0xf) | ((a & 0xf) << 4));
+
+ vortex_src_persist_convratio(vortex, src, ratio);
+}
+
+static int
+vortex_src_checkratio(vortex_t * vortex, unsigned char src,
+ unsigned int desired_ratio)
+{
+ int hw_ratio, lifeboat = 0;
+
+ hw_ratio = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
+
+ while (hw_ratio != desired_ratio) {
+ hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), desired_ratio);
+
+ if ((lifeboat++) > 15) {
+ printk(KERN_ERR "Vortex: could not set src-%d from %d to %d\n",
+ src, hw_ratio, desired_ratio);
+ break;
+ }
+ }
+
+ return hw_ratio;
+}
+
+#endif
+/*
+ Objective: Set samplerate for given SRC module.
+ Arguments:
+ card: pointer to vortex_t strcut.
+ src: Integer index of the SRC module.
+ cr: Current sample rate conversion factor.
+ b: unknown 16 bit value.
+ sweep: Enable Samplerate fade from cr toward tr flag.
+ dirplay: 1: playback, 0: recording.
+ sl: Slow Lock flag.
+ tr: Target samplerate conversion.
+ thsource: Throttle source flag (no idea what that means).
+*/
+static void vortex_src_setupchannel(vortex_t * card, unsigned char src,
+ unsigned int cr, unsigned int b, int sweep, int d,
+ int dirplay, int sl, unsigned int tr, int thsource)
+{
+ // noplayback: d=2,4,7,0xa,0xb when using first 2 src's.
+ // c: enables pitch sweep.
+ // looks like g is c related. Maybe g is a sweep parameter ?
+ // g = cvr
+ // dirplay: 0 = recording, 1 = playback
+ // d = src hw index.
+
+ int esi, ebp = 0, esp10;
+
+ vortex_src_flushbuffers(card, src);
+
+ if (sweep) {
+ if ((tr & 0x10000) && (tr != 0x10000)) {
+ tr = 0;
+ esi = 0x7;
+ } else {
+ if ((((short)tr) < 0) && (tr != 0x8000)) {
+ tr = 0;
+ esi = 0x8;
+ } else {
+ tr = 1;
+ esi = 0xc;
+ }
+ }
+ } else {
+ if ((cr & 0x10000) && (cr != 0x10000)) {
+ tr = 0; /*ebx = 0 */
+ esi = 0x11 - ((cr >> 0xe) & 7);
+ if (cr & 0x3fff)
+ esi -= 1;
+ else
+ esi -= 2;
+ } else {
+ tr = 1;
+ esi = 0xc;
+ }
+ }
+ vortex_src_cleardrift(card, src);
+ vortex_src_set_throttlesource(card, src, thsource);
+
+ if ((dirplay == 0) && (sweep == 0)) {
+ if (tr)
+ esp10 = 0xf;
+ else
+ esp10 = 0xc;
+ ebp = 0;
+ } else {
+ if (tr)
+ ebp = 0xf;
+ else
+ ebp = 0xc;
+ esp10 = 0;
+ }
+ hwwrite(card->mmio, VORTEX_SRC_U0 + (src << 2),
+ (sl << 0x9) | (sweep << 0x8) | ((esi & 0xf) << 4) | d);
+ /* 0xc0 esi=0xc c=f=0 d=0 */
+ vortex_src_persist_convratio(card, src, cr);
+ hwwrite(card->mmio, VORTEX_SRC_U1 + (src << 2), b & 0xffff);
+ /* 0 b=0 */
+ hwwrite(card->mmio, VORTEX_SRC_U2 + (src << 2),
+ (tr << 0x11) | (dirplay << 0x10) | (ebp << 0x8) | esp10);
+ /* 0x30f00 e=g=1 esp10=0 ebp=f */
+ //printk(KERN_INFO "vortex: SRC %d, d=0x%x, esi=0x%x, esp10=0x%x, ebp=0x%x\n", src, d, esi, esp10, ebp);
+}
+
+static void vortex_srcblock_init(vortex_t * vortex)
+{
+ unsigned long addr;
+ int x;
+ hwwrite(vortex->mmio, VORTEX_SRC_SOURCESIZE, 0x1ff);
+ /*
+ for (x=0; x<0x10; x++) {
+ vortex_src_init(&vortex_src[x], x);
+ }
+ */
+ //addr = 0xcc3c;
+ //addr = 0x26c3c;
+ addr = VORTEX_SRC_RTBASE + 0x3c;
+ for (x = 0xf; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0);
+ addr -= 4;
+ }
+ //addr = 0xcc94;
+ //addr = 0x26c94;
+ addr = VORTEX_SRC_CHNBASE + 0x54;
+ for (x = 0x15; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, 0);
+ addr -= 4;
+ }
+}
+
+static int
+vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
+{
+ int temp, lifeboat = 0, prev;
+ // esp13 = src
+
+ temp = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
+ if ((temp & (1 << ch)) == 0) {
+ hwwrite(vortex->mmio, VORTEX_SRC_CHNBASE + (ch << 2), src);
+ vortex_src_en_sr(vortex, ch);
+ return 1;
+ }
+ prev = VORTEX_SRC_CHNBASE + (ch << 2); /*ebp */
+ temp = hwread(vortex->mmio, prev);
+ //while (temp & NR_SRC) {
+ while (temp & 0x10) {
+ prev = VORTEX_SRC_RTBASE + ((temp & 0xf) << 2); /*esp12 */
+ //prev = VORTEX_SRC_RTBASE + ((temp & (NR_SRC-1)) << 2); /*esp12*/
+ temp = hwread(vortex->mmio, prev);
+ //printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp);
+ if ((++lifeboat) > 0xf) {
+ printk(KERN_ERR
+ "vortex_src_addWTD: lifeboat overflow\n");
+ return 0;
+ }
+ }
+ hwwrite(vortex->mmio, VORTEX_SRC_RTBASE + ((temp & 0xf) << 2), src);
+ //hwwrite(vortex->mmio, prev, (temp & (NR_SRC-1)) | NR_SRC);
+ hwwrite(vortex->mmio, prev, (temp & 0xf) | 0x10);
+ return 1;
+}
+
+static int
+vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
+{
+ int esp14 = -1, esp18, eax, ebx, edx, ebp, esi = 0;
+ //int esp1f=edi(while)=src, esp10=ch;
+
+ eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
+ if (((1 << ch) & eax) == 0) {
+ printk(KERN_ERR "src alarm\n");
+ return 0;
+ }
+ ebp = VORTEX_SRC_CHNBASE + (ch << 2);
+ esp18 = hwread(vortex->mmio, ebp);
+ if (esp18 & 0x10) {
+ ebx = (esp18 & 0xf);
+ if (src == ebx) {
+ ebx = VORTEX_SRC_RTBASE + (src << 2);
+ edx = hwread(vortex->mmio, ebx);
+ //7b60
+ hwwrite(vortex->mmio, ebp, edx);
+ hwwrite(vortex->mmio, ebx, 0);
+ } else {
+ //7ad3
+ edx =
+ hwread(vortex->mmio,
+ VORTEX_SRC_RTBASE + (ebx << 2));
+ //printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
+ while ((edx & 0xf) != src) {
+ if ((esi) > 0xf) {
+ printk
+ ("vortex: srcdelWTD: error, lifeboat overflow\n");
+ return 0;
+ }
+ esp14 = ebx;
+ ebx = edx & 0xf;
+ ebp = ebx << 2;
+ edx =
+ hwread(vortex->mmio,
+ VORTEX_SRC_RTBASE + ebp);
+ //printk(KERN_INFO "vortex: srcdelWTD: while addr=%x, val=%x\n", ebp, edx);
+ esi++;
+ }
+ //7b30
+ ebp = ebx << 2;
+ if (edx & 0x10) { /* Delete entry in between others */
+ ebx = VORTEX_SRC_RTBASE + ((edx & 0xf) << 2);
+ edx = hwread(vortex->mmio, ebx);
+ //7b60
+ hwwrite(vortex->mmio,
+ VORTEX_SRC_RTBASE + ebp, edx);
+ hwwrite(vortex->mmio, ebx, 0);
+ //printk(KERN_INFO "vortex srcdelWTD between addr= 0x%x, val= 0x%x\n", ebp, edx);
+ } else { /* Delete last entry */
+ //7b83
+ if (esp14 == -1)
+ hwwrite(vortex->mmio,
+ VORTEX_SRC_CHNBASE +
+ (ch << 2), esp18 & 0xef);
+ else {
+ ebx = (0xffffffe0 & edx) | (0xf & ebx);
+ hwwrite(vortex->mmio,
+ VORTEX_SRC_RTBASE +
+ (esp14 << 2), ebx);
+ //printk(KERN_INFO"vortex srcdelWTD last addr= 0x%x, val= 0x%x\n", esp14, ebx);
+ }
+ hwwrite(vortex->mmio,
+ VORTEX_SRC_RTBASE + ebp, 0);
+ return 1;
+ }
+ }
+ } else {
+ //7be0
+ vortex_src_dis_sr(vortex, ch);
+ hwwrite(vortex->mmio, ebp, 0);
+ }
+ return 1;
+}
+
+ /*FIFO*/
+
+static void
+vortex_fifo_clearadbdata(vortex_t * vortex, int fifo, int x)
+{
+ for (x--; x >= 0; x--)
+ hwwrite(vortex->mmio,
+ VORTEX_FIFO_ADBDATA +
+ (((fifo << FIFO_SIZE_BITS) + x) << 2), 0);
+}
+
+#if 0
+static void vortex_fifo_adbinitialize(vortex_t * vortex, int fifo, int j)
+{
+ vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE);
+#ifdef CHIP_AU8820
+ hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2),
+ (FIFO_U1 | ((j & FIFO_MASK) << 0xb)));
+#else
+ hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2),
+ (FIFO_U1 | ((j & FIFO_MASK) << 0xc)));
+#endif
+}
+#endif
+static void vortex_fifo_setadbvalid(vortex_t * vortex, int fifo, int en)
+{
+ hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2),
+ (hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2)) &
+ 0xffffffef) | ((1 & en) << 4) | FIFO_U1);
+}
+
+static void
+vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
+ int empty, int valid, int f)
+{
+ int temp, lifeboat = 0;
+ //int this_8[NR_ADB] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* position */
+ int this_4 = 0x2;
+ /* f seems priority related.
+ * CAsp4AdbDma::SetPriority is the only place that calls SetAdbCtrl with f set to 1
+ * every where else it is set to 0. It seems, however, that CAsp4AdbDma::SetPriority
+ * is never called, thus the f related bits remain a mystery for now.
+ */
+ do {
+ temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
+ if (lifeboat++ > 0xbb8) {
+ printk(KERN_ERR
+ "Vortex: vortex_fifo_setadbctrl fail\n");
+ break;
+ }
+ }
+ while (temp & FIFO_RDONLY);
+
+ // AU8830 semes to take some special care about fifo content (data).
+ // But i'm just to lazy to translate that :)
+ if (valid) {
+ if ((temp & FIFO_VALID) == 0) {
+ //this_8[fifo] = 0;
+ vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE); // this_4
+#ifdef CHIP_AU8820
+ temp = (this_4 & 0x1f) << 0xb;
+#else
+ temp = (this_4 & 0x3f) << 0xc;
+#endif
+ temp = (temp & 0xfffffffd) | ((b & 1) << 1);
+ temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+ temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+ temp |= FIFO_U1;
+ temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+#ifdef CHIP_AU8820
+ temp = (temp & 0xfffbffff) | ((f & 1) << 0x12);
+#endif
+#ifdef CHIP_AU8830
+ temp = (temp & 0xf7ffffff) | ((f & 1) << 0x1b);
+ temp = (temp & 0xefffffff) | ((f & 1) << 0x1c);
+#endif
+#ifdef CHIP_AU8810
+ temp = (temp & 0xfeffffff) | ((f & 1) << 0x18);
+ temp = (temp & 0xfdffffff) | ((f & 1) << 0x19);
+#endif
+ }
+ } else {
+ if (temp & FIFO_VALID) {
+#ifdef CHIP_AU8820
+ temp = ((f & 1) << 0x12) | (temp & 0xfffbffef);
+#endif
+#ifdef CHIP_AU8830
+ temp =
+ ((f & 1) << 0x1b) | (temp & 0xe7ffffef) | FIFO_BITS;
+#endif
+#ifdef CHIP_AU8810
+ temp =
+ ((f & 1) << 0x18) | (temp & 0xfcffffef) | FIFO_BITS;
+#endif
+ } else
+ /*if (this_8[fifo]) */
+ vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE);
+ }
+ hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2), temp);
+ hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
+}
+
+#ifndef CHIP_AU8810
+static void vortex_fifo_clearwtdata(vortex_t * vortex, int fifo, int x)
+{
+ if (x < 1)
+ return;
+ for (x--; x >= 0; x--)
+ hwwrite(vortex->mmio,
+ VORTEX_FIFO_WTDATA +
+ (((fifo << FIFO_SIZE_BITS) + x) << 2), 0);
+}
+
+static void vortex_fifo_wtinitialize(vortex_t * vortex, int fifo, int j)
+{
+ vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+#ifdef CHIP_AU8820
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2),
+ (FIFO_U1 | ((j & FIFO_MASK) << 0xb)));
+#else
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2),
+ (FIFO_U1 | ((j & FIFO_MASK) << 0xc)));
+#endif
+}
+
+static void vortex_fifo_setwtvalid(vortex_t * vortex, int fifo, int en)
+{
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2),
+ (hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2)) &
+ 0xffffffef) | ((en & 1) << 4) | FIFO_U1);
+}
+
+static void
+vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
+ int empty, int valid, int f)
+{
+ int temp = 0, lifeboat = 0;
+ int this_4 = 2;
+
+ do {
+ temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+ if (lifeboat++ > 0xbb8) {
+ printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail\n");
+ break;
+ }
+ }
+ while (temp & FIFO_RDONLY);
+
+ if (valid) {
+ if ((temp & FIFO_VALID) == 0) {
+ vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE); // this_4
+#ifdef CHIP_AU8820
+ temp = (this_4 & 0x1f) << 0xb;
+#else
+ temp = (this_4 & 0x3f) << 0xc;
+#endif
+ temp = (temp & 0xfffffffd) | ((ctrl & 1) << 1);
+ temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+ temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+ temp |= FIFO_U1;
+ temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+#ifdef CHIP_AU8820
+ temp = (temp & 0xfffbffff) | ((f & 1) << 0x12);
+#endif
+#ifdef CHIP_AU8830
+ temp = (temp & 0xf7ffffff) | ((f & 1) << 0x1b);
+ temp = (temp & 0xefffffff) | ((f & 1) << 0x1c);
+#endif
+#ifdef CHIP_AU8810
+ temp = (temp & 0xfeffffff) | ((f & 1) << 0x18);
+ temp = (temp & 0xfdffffff) | ((f & 1) << 0x19);
+#endif
+ }
+ } else {
+ if (temp & FIFO_VALID) {
+#ifdef CHIP_AU8820
+ temp = ((f & 1) << 0x12) | (temp & 0xfffbffef);
+#endif
+#ifdef CHIP_AU8830
+ temp =
+ ((f & 1) << 0x1b) | (temp & 0xe7ffffef) | FIFO_BITS;
+#endif
+#ifdef CHIP_AU8810
+ temp =
+ ((f & 1) << 0x18) | (temp & 0xfcffffef) | FIFO_BITS;
+#endif
+ } else
+ /*if (this_8[fifo]) */
+ vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+ }
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+ hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+
+/*
+ do {
+ temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+ if (lifeboat++ > 0xbb8) {
+ printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
+ break;
+ }
+ } while ((temp & FIFO_RDONLY)&&(temp & FIFO_VALID)&&(temp != 0xFFFFFFFF));
+
+
+ if (valid) {
+ if (temp & FIFO_VALID) {
+ temp = 0x40000;
+ //temp |= 0x08000000;
+ //temp |= 0x10000000;
+ //temp |= 0x04000000;
+ //temp |= 0x00400000;
+ temp |= 0x1c400000;
+ temp &= 0xFFFFFFF3;
+ temp &= 0xFFFFFFEF;
+ temp |= (valid & 1) << 4;
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+ return;
+ } else {
+ vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+ return;
+ }
+ } else {
+ temp &= 0xffffffef;
+ temp |= 0x08000000;
+ temp |= 0x10000000;
+ temp |= 0x04000000;
+ temp |= 0x00400000;
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+ temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+ //((temp >> 6) & 0x3f)
+
+ priority = 0;
+ if (((temp & 0x0fc0) ^ ((temp >> 6) & 0x0fc0)) & 0FFFFFFC0)
+ vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+ valid = 0xfb;
+ temp = (temp & 0xfffffffd) | ((ctrl & 1) << 1);
+ temp = (temp & 0xfffdffff) | ((f & 1) << 0x11);
+ temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+ temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+ temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+ }
+
+ */
+
+ /*
+ temp = (temp & 0xfffffffd) | ((ctrl & 1) << 1);
+ temp = (temp & 0xfffdffff) | ((f & 1) << 0x11);
+ temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+ temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+ temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+ #ifdef FIFO_BITS
+ temp = temp | FIFO_BITS | 40000;
+ #endif
+ // 0x1c440010, 0x1c400000
+ hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+ */
+}
+
+#endif
+static void vortex_fifo_init(vortex_t * vortex)
+{
+ int x;
+ unsigned long addr;
+
+ /* ADB DMA channels fifos. */
+ addr = VORTEX_FIFO_ADBCTRL + ((NR_ADB - 1) * 4);
+ for (x = NR_ADB - 1; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
+ if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
+ printk(KERN_ERR "bad adb fifo reset!");
+ vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
+ addr -= 4;
+ }
+
+#ifndef CHIP_AU8810
+ /* WT DMA channels fifos. */
+ addr = VORTEX_FIFO_WTCTRL + ((NR_WT - 1) * 4);
+ for (x = NR_WT - 1; x >= 0; x--) {
+ hwwrite(vortex->mmio, addr, FIFO_U0);
+ if (hwread(vortex->mmio, addr) != FIFO_U0)
+ printk(KERN_ERR
+ "bad wt fifo reset (0x%08lx, 0x%08x)!\n",
+ addr, hwread(vortex->mmio, addr));
+ vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE);
+ addr -= 4;
+ }
+#endif
+ /* trigger... */
+#ifdef CHIP_AU8820
+ hwwrite(vortex->mmio, 0xf8c0, 0xd03); //0x0843 0xd6b
+#else
+#ifdef CHIP_AU8830
+ hwwrite(vortex->mmio, 0x17000, 0x61); /* wt a */
+ hwwrite(vortex->mmio, 0x17004, 0x61); /* wt b */
+#endif
+ hwwrite(vortex->mmio, 0x17008, 0x61); /* adb */
+#endif
+}
+
+/* ADBDMA */
+
+static void vortex_adbdma_init(vortex_t * vortex)
+{
+}
+
+static void vortex_adbdma_setfirstbuffer(vortex_t * vortex, int adbdma)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+ dma->dma_ctrl);
+}
+
+static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+ //hwwrite(vortex->mmio, VORTEX_ADBDMA_START + (adbdma << 2), sb << (((NR_ADB-1)-((adbdma&0xf)*2))));
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_START + (adbdma << 2),
+ sb << ((0xf - (adbdma & 0xf)) * 2));
+ dma->period_real = dma->period_virt = sb;
+}
+
+static void
+vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
+ snd_pcm_sgbuf_t * sgbuf, int psize, int count)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ if (sgbuf == NULL) {
+ printk(KERN_INFO "vortex: FATAL: sgbuf is NULL!\n");
+ return;
+ }
+ //printk(KERN_INFO "vortex: page count = %d, tblcount = %d\n", count, sgbuf->tblsize);
+
+ dma->period_bytes = psize;
+ dma->nr_periods = count;
+ dma->sgbuf = sgbuf;
+
+ dma->cfg0 = 0;
+ dma->cfg1 = 0;
+ switch (count) {
+ /* Four or more pages */
+ default:
+ case 4:
+ dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize - 1);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc,
+ snd_sgbuf_get_addr(sgbuf, psize * 3));
+ /* 3 pages */
+ case 3:
+ dma->cfg0 |= 0x12000000;
+ dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8,
+ snd_sgbuf_get_addr(sgbuf, psize * 2));
+ /* 2 pages */
+ case 2:
+ dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4,
+ snd_sgbuf_get_addr(sgbuf, psize));
+ /* 1 page */
+ case 1:
+ dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (adbdma << 4),
+ snd_sgbuf_get_addr(sgbuf, 0));
+ break;
+ }
+ //printk("vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", dma->cfg0, dma->cfg1);
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0);
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG1 + (adbdma << 3), dma->cfg1);
+
+ vortex_adbdma_setfirstbuffer(vortex, adbdma);
+ vortex_adbdma_setstartbuffer(vortex, adbdma, 0);
+}
+
+static void
+vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie, int dir,
+ int fmt, int d, unsigned long offset)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ dma->dma_unknown = d;
+ dma->dma_ctrl =
+ ((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
+ /* Enable PCMOUT interrupts. */
+ dma->dma_ctrl =
+ (dma->dma_ctrl & ~IE_MASK) | ((ie << IE_SHIFT) & IE_MASK);
+
+ dma->dma_ctrl =
+ (dma->dma_ctrl & ~DIR_MASK) | ((dir << DIR_SHIFT) & DIR_MASK);
+ dma->dma_ctrl =
+ (dma->dma_ctrl & ~FMT_MASK) | ((fmt << FMT_SHIFT) & FMT_MASK);
+
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+ dma->dma_ctrl);
+ hwread(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2));
+}
+
+static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+ int page, p, pp, delta, i;
+
+ page =
+ (hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2)) &
+ ADB_SUBBUF_MASK) >> ADB_SUBBUF_SHIFT;
+ if (dma->nr_periods >= 4)
+ delta = (page - dma->period_real) & 3;
+ else {
+ delta = (page - dma->period_real);
+ if (delta < 0)
+ delta += dma->nr_periods;
+ }
+ if (delta == 0)
+ return 0;
+
+ /* refresh hw page table */
+ if (dma->nr_periods > 4) {
+ for (i = 0; i < delta; i++) {
+ /* p: audio buffer page index */
+ p = dma->period_virt + i + 4;
+ if (p >= dma->nr_periods)
+ p -= dma->nr_periods;
+ /* pp: hardware DMA page index. */
+ pp = dma->period_real + i;
+ if (pp >= 4)
+ pp -= 4;
+ //hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), dma->table[p].addr);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
+ snd_sgbuf_get_addr(dma->sgbuf,
+ dma->period_bytes * p));
+ /* Force write thru cache. */
+ hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE +
+ (((adbdma << 2) + pp) << 2));
+ }
+ }
+ dma->period_virt += delta;
+ dma->period_real = page;
+ if (dma->period_virt >= dma->nr_periods)
+ dma->period_virt -= dma->nr_periods;
+ if (delta != 1)
+ printk(KERN_INFO "vortex: %d virt=%d, real=%d, delta=%d\n",
+ adbdma, dma->period_virt, dma->period_real, delta);
+
+ return delta;
+}
+
+
+static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
+ stream_t *dma = &vortex->dma_adb[adbdma];
+ int p, pp, i;
+
+ /* refresh hw page table */
+ for (i=0 ; i < 4 && i < dma->nr_periods; i++) {
+ /* p: audio buffer page index */
+ p = dma->period_virt + i;
+ if (p >= dma->nr_periods)
+ p -= dma->nr_periods;
+ /* pp: hardware DMA page index. */
+ pp = dma->period_real + i;
+ if (dma->nr_periods < 4) {
+ if (pp >= dma->nr_periods)
+ pp -= dma->nr_periods;
+ }
+ else {
+ if (pp >= 4)
+ pp -= 4;
+ }
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+ /* Force write thru cache. */
+ hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
+ }
+}
+
+static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+ int temp;
+
+ temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
+ temp = (dma->period_virt * dma->period_bytes) + (temp & POS_MASK);
+ return (temp);
+}
+
+static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma)
+{
+ int this_8 = 0 /*empty */ , this_4 = 0 /*priority */ ;
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ switch (dma->fifo_status) {
+ case FIFO_START:
+ vortex_fifo_setadbvalid(vortex, adbdma,
+ dma->fifo_enabled ? 1 : 0);
+ break;
+ case FIFO_STOP:
+ this_8 = 1;
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ case FIFO_PAUSE:
+ vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_START;
+}
+
+static void vortex_adbdma_resumefifo(vortex_t * vortex, int adbdma)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ int this_8 = 1, this_4 = 0;
+ switch (dma->fifo_status) {
+ case FIFO_STOP:
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ case FIFO_PAUSE:
+ vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_START;
+}
+
+static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ int this_8 = 0, this_4 = 0;
+ switch (dma->fifo_status) {
+ case FIFO_START:
+ vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ break;
+ case FIFO_STOP:
+ hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_PAUSE;
+}
+
+#if 0 // Using pause instead
+static void vortex_adbdma_stopfifo(vortex_t * vortex, int adbdma)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ int this_4 = 0, this_8 = 0;
+ if (dma->fifo_status == FIFO_START)
+ vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ else if (dma->fifo_status == FIFO_STOP)
+ return;
+ dma->fifo_status = FIFO_STOP;
+ dma->fifo_enabled = 0;
+}
+
+#endif
+/* WTDMA */
+
+#ifndef CHIP_AU8810
+static void vortex_wtdma_setfirstbuffer(vortex_t * vortex, int wtdma)
+{
+ //int this_7c=dma_ctrl;
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+}
+
+static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+ //hwwrite(vortex->mmio, VORTEX_WTDMA_START + (wtdma << 2), sb << ((0x1f-(wtdma&0xf)*2)));
+ hwwrite(vortex->mmio, VORTEX_WTDMA_START + (wtdma << 2),
+ sb << ((0xf - (wtdma & 0xf)) * 2));
+ dma->period_real = dma->period_virt = sb;
+}
+
+static void
+vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
+ snd_pcm_sgbuf_t * sgbuf, int psize, int count)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ dma->period_bytes = psize;
+ dma->nr_periods = count;
+ dma->sgbuf = sgbuf;
+
+ dma->cfg0 = 0;
+ dma->cfg1 = 0;
+ switch (count) {
+ /* Four or more pages */
+ default:
+ case 4:
+ dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc,
+ snd_sgbuf_get_addr(sgbuf, psize * 3));
+ /* 3 pages */
+ case 3:
+ dma->cfg0 |= 0x12000000;
+ dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x8,
+ snd_sgbuf_get_addr(sgbuf, psize * 2));
+ /* 2 pages */
+ case 2:
+ dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4,
+ snd_sgbuf_get_addr(sgbuf, psize));
+ /* 1 page */
+ case 1:
+ dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
+ snd_sgbuf_get_addr(sgbuf, 0));
+ break;
+ }
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG0 + (wtdma << 3), dma->cfg0);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG1 + (wtdma << 3), dma->cfg1);
+
+ vortex_wtdma_setfirstbuffer(vortex, wtdma);
+ vortex_wtdma_setstartbuffer(vortex, wtdma, 0);
+}
+
+static void
+vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d,
+ /*int e, */ unsigned long offset)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ //dma->this_08 = e;
+ dma->dma_unknown = d;
+ dma->dma_ctrl = 0;
+ dma->dma_ctrl =
+ ((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
+ /* PCMOUT interrupt */
+ dma->dma_ctrl =
+ (dma->dma_ctrl & ~IE_MASK) | ((ie << IE_SHIFT) & IE_MASK);
+ /* Always playback. */
+ dma->dma_ctrl |= (1 << DIR_SHIFT);
+ /* Audio Format */
+ dma->dma_ctrl =
+ (dma->dma_ctrl & FMT_MASK) | ((fmt << FMT_SHIFT) & FMT_MASK);
+ /* Write into hardware */
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+}
+
+static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+ int page, p, pp, delta, i;
+
+ page =
+ (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) &
+ WT_SUBBUF_MASK)
+ >> WT_SUBBUF_SHIFT;
+ if (dma->nr_periods >= 4)
+ delta = (page - dma->period_real) & 3;
+ else {
+ delta = (page - dma->period_real);
+ if (delta < 0)
+ delta += dma->nr_periods;
+ }
+ if (delta == 0)
+ return 0;
+
+ /* refresh hw page table */
+ if (dma->nr_periods > 4) {
+ for (i = 0; i < delta; i++) {
+ /* p: audio buffer page index */
+ p = dma->period_virt + i + 4;
+ if (p >= dma->nr_periods)
+ p -= dma->nr_periods;
+ /* pp: hardware DMA page index. */
+ pp = dma->period_real + i;
+ if (pp >= 4)
+ pp -= 4;
+ hwwrite(vortex->mmio,
+ VORTEX_WTDMA_BUFBASE +
+ (((wtdma << 2) + pp) << 2),
+ snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+ /* Force write thru cache. */
+ hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +
+ (((wtdma << 2) + pp) << 2));
+ }
+ }
+ dma->period_virt += delta;
+ if (dma->period_virt >= dma->nr_periods)
+ dma->period_virt -= dma->nr_periods;
+ dma->period_real = page;
+
+ if (delta != 1)
+ printk(KERN_WARNING "vortex: wt virt = %d, delta = %d\n",
+ dma->period_virt, delta);
+
+ return delta;
+}
+
+#if 0
+static void
+vortex_wtdma_getposition(vortex_t * vortex, int wtdma, int *subbuf, int *pos)
+{
+ int temp;
+ temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
+ *subbuf = (temp >> WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK;
+ *pos = temp & POS_MASK;
+}
+
+static int vortex_wtdma_getcursubuffer(vortex_t * vortex, int wtdma)
+{
+ return ((hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) >>
+ POS_SHIFT) & POS_MASK);
+}
+#endif
+static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+ int temp;
+
+ temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
+ //temp = (temp & POS_MASK) + (((temp>>WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK)*(dma->cfg0&POS_MASK));
+ temp = (temp & POS_MASK) + ((dma->period_virt) * (dma->period_bytes));
+ return temp;
+}
+
+static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+ int this_8 = 0, this_4 = 0;
+
+ switch (dma->fifo_status) {
+ case FIFO_START:
+ vortex_fifo_setwtvalid(vortex, wtdma,
+ dma->fifo_enabled ? 1 : 0);
+ break;
+ case FIFO_STOP:
+ this_8 = 1;
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ case FIFO_PAUSE:
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_START;
+}
+
+static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ int this_8 = 0, this_4 = 0;
+ switch (dma->fifo_status) {
+ case FIFO_STOP:
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ case FIFO_PAUSE:
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_START;
+}
+
+static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ int this_8 = 0, this_4 = 0;
+ switch (dma->fifo_status) {
+ case FIFO_START:
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ break;
+ case FIFO_STOP:
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_PAUSE;
+}
+
+static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ int this_4 = 0, this_8 = 0;
+ if (dma->fifo_status == FIFO_START)
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ else if (dma->fifo_status == FIFO_STOP)
+ return;
+ dma->fifo_status = FIFO_STOP;
+ dma->fifo_enabled = 0;
+}
+
+#endif
+/* ADB Routes */
+
+typedef int ADBRamLink;
+static void vortex_adb_init(vortex_t * vortex)
+{
+ int i;
+ /* it looks like we are writing more than we need to...
+ * if we write what we are supposed to it breaks things... */
+ hwwrite(vortex->mmio, VORTEX_ADB_SR, 0);
+ for (i = 0; i < VORTEX_ADB_RTBASE_COUNT; i++)
+ hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (i << 2),
+ hwread(vortex->mmio,
+ VORTEX_ADB_RTBASE + (i << 2)) | ROUTE_MASK);
+ for (i = 0; i < VORTEX_ADB_CHNBASE_COUNT; i++) {
+ hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (i << 2),
+ hwread(vortex->mmio,
+ VORTEX_ADB_CHNBASE + (i << 2)) | ROUTE_MASK);
+ }
+}
+
+static void vortex_adb_en_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_ADB_SR,
+ hwread(vortex->mmio, VORTEX_ADB_SR) | (0x1 << channel));
+}
+
+static void vortex_adb_dis_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_ADB_SR,
+ hwread(vortex->mmio, VORTEX_ADB_SR) & ~(0x1 << channel));
+}
+
+static void
+vortex_adb_addroutes(vortex_t * vortex, unsigned char channel,
+ ADBRamLink * route, int rnum)
+{
+ int temp, prev, lifeboat = 0;
+
+ if ((rnum <= 0) || (route == NULL))
+ return;
+ /* Write last routes. */
+ rnum--;
+ hwwrite(vortex->mmio,
+ VORTEX_ADB_RTBASE + ((route[rnum] & ADB_MASK) << 2),
+ ROUTE_MASK);
+ while (rnum > 0) {
+ hwwrite(vortex->mmio,
+ VORTEX_ADB_RTBASE +
+ ((route[rnum - 1] & ADB_MASK) << 2), route[rnum]);
+ rnum--;
+ }
+ /* Write first route. */
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_ADB_CHNBASE + (channel << 2)) & ADB_MASK;
+ if (temp == ADB_MASK) {
+ /* First entry on this channel. */
+ hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2),
+ route[0]);
+ vortex_adb_en_sr(vortex, channel);
+ return;
+ }
+ /* Not first entry on this channel. Need to link. */
+ do {
+ prev = temp;
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
+ if ((lifeboat++) > ADB_MASK) {
+ printk(KERN_ERR
+ "vortex_adb_addroutes: unending route! 0x%x\n",
+ *route);
+ return;
+ }
+ }
+ while (temp != ADB_MASK);
+ hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2), route[0]);
+}
+
+static void
+vortex_adb_delroutes(vortex_t * vortex, unsigned char channel,
+ ADBRamLink route0, ADBRamLink route1)
+{
+ int temp, lifeboat = 0, prev;
+
+ /* Find route. */
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_ADB_CHNBASE + (channel << 2)) & ADB_MASK;
+ if (temp == (route0 & ADB_MASK)) {
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_ADB_RTBASE + ((route1 & ADB_MASK) << 2));
+ if ((temp & ADB_MASK) == ADB_MASK)
+ vortex_adb_dis_sr(vortex, channel);
+ hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2),
+ temp);
+ return;
+ }
+ do {
+ prev = temp;
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK;
+ if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) {
+ printk(KERN_ERR
+ "vortex_adb_delroutes: route not found! 0x%x\n",
+ route0);
+ return;
+ }
+ }
+ while (temp != (route0 & ADB_MASK));
+ temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+ if ((temp & ADB_MASK) == route1)
+ temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+ /* Make bridge over deleted route. */
+ hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2), temp);
+}
+
+static void
+vortex_route(vortex_t * vortex, int en, unsigned char channel,
+ unsigned char source, unsigned char dest)
+{
+ ADBRamLink route;
+
+ route = ((source & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+ if (en) {
+ vortex_adb_addroutes(vortex, channel, &route, 1);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= OFFSET_SRCOUT))
+ vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= OFFSET_MIXOUT))
+ vortex_mixer_addWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ } else {
+ vortex_adb_delroutes(vortex, channel, route, route);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= OFFSET_SRCOUT))
+ vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= OFFSET_MIXOUT))
+ vortex_mixer_delWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ }
+}
+
+#if 0
+static void
+vortex_routes(vortex_t * vortex, int en, unsigned char channel,
+ unsigned char source, unsigned char dest0, unsigned char dest1)
+{
+ ADBRamLink route[2];
+
+ route[0] = ((source & ADB_MASK) << ADB_SHIFT) | (dest0 & ADB_MASK);
+ route[1] = ((source & ADB_MASK) << ADB_SHIFT) | (dest1 & ADB_MASK);
+
+ if (en) {
+ vortex_adb_addroutes(vortex, channel, route, 2);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= (OFFSET_SRCOUT)))
+ vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= (OFFSET_MIXOUT)))
+ vortex_mixer_addWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ } else {
+ vortex_adb_delroutes(vortex, channel, route[0], route[1]);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= (OFFSET_SRCOUT)))
+ vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= (OFFSET_MIXOUT)))
+ vortex_mixer_delWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ }
+}
+
+#endif
+/* Route two sources to same target. Sources must be of same class !!! */
+static void
+vortex_routeLRT(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char source0, unsigned char source1,
+ unsigned char dest)
+{
+ ADBRamLink route[2];
+
+ route[0] = ((source0 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+ route[1] = ((source1 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+
+ if (dest < 0x10)
+ route[1] = (route[1] & ~ADB_MASK) | (dest + 0x20); /* fifo A */
+
+ if (en) {
+ vortex_adb_addroutes(vortex, ch, route, 2);
+ if ((source0 < (OFFSET_SRCOUT + NR_SRC))
+ && (source0 >= OFFSET_SRCOUT)) {
+ vortex_src_addWTD(vortex,
+ (source0 - OFFSET_SRCOUT), ch);
+ vortex_src_addWTD(vortex,
+ (source1 - OFFSET_SRCOUT), ch);
+ } else if ((source0 < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source0 >= OFFSET_MIXOUT)) {
+ vortex_mixer_addWTD(vortex,
+ (source0 - OFFSET_MIXOUT), ch);
+ vortex_mixer_addWTD(vortex,
+ (source1 - OFFSET_MIXOUT), ch);
+ }
+ } else {
+ vortex_adb_delroutes(vortex, ch, route[0], route[1]);
+ if ((source0 < (OFFSET_SRCOUT + NR_SRC))
+ && (source0 >= OFFSET_SRCOUT)) {
+ vortex_src_delWTD(vortex,
+ (source0 - OFFSET_SRCOUT), ch);
+ vortex_src_delWTD(vortex,
+ (source1 - OFFSET_SRCOUT), ch);
+ } else if ((source0 < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source0 >= OFFSET_MIXOUT)) {
+ vortex_mixer_delWTD(vortex,
+ (source0 - OFFSET_MIXOUT), ch);
+ vortex_mixer_delWTD(vortex,
+ (source1 - OFFSET_MIXOUT), ch);
+ }
+ }
+}
+
+/* Connection stuff */
+
+// Connect adbdma to src('s).
+static void
+vortex_connection_adbdma_src(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char adbdma, unsigned char src)
+{
+ vortex_route(vortex, en, ch, ADB_DMA(adbdma), ADB_SRCIN(src));
+}
+
+// Connect SRC to mixin.
+static void
+vortex_connection_src_mixin(vortex_t * vortex, int en,
+ unsigned char channel, unsigned char src,
+ unsigned char mixin)
+{
+ vortex_route(vortex, en, channel, ADB_SRCOUT(src), ADB_MIXIN(mixin));
+}
+
+// Connect mixin with mix output.
+static void
+vortex_connection_mixin_mix(vortex_t * vortex, int en, unsigned char mixin,
+ unsigned char mix, int a)
+{
+ if (en) {
+ vortex_mix_enableinput(vortex, mix, mixin);
+ vortex_mix_setinputvolumebyte(vortex, mix, mixin, MIX_DEFIGAIN); // added to original code.
+ } else
+ vortex_mix_disableinput(vortex, mix, mixin, a);
+}
+
+// Connect absolut address to mixin.
+static void
+vortex_connection_adb_mixin(vortex_t * vortex, int en,
+ unsigned char channel, unsigned char source,
+ unsigned char mixin)
+{
+ vortex_route(vortex, en, channel, source, ADB_MIXIN(mixin));
+}
+
+static void
+vortex_connection_src_adbdma(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char src, unsigned char adbdma)
+{
+ vortex_route(vortex, en, ch, ADB_SRCOUT(src), ADB_DMA(adbdma));
+}
+
+static void
+vortex_connection_src_src_adbdma(vortex_t * vortex, int en,
+ unsigned char ch, unsigned char src0,
+ unsigned char src1, unsigned char adbdma)
+{
+
+ vortex_routeLRT(vortex, en, ch, ADB_SRCOUT(src0), ADB_SRCOUT(src1),
+ ADB_DMA(adbdma));
+}
+
+// mix to absolut address.
+static void
+vortex_connection_mix_adb(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char mix, unsigned char dest)
+{
+ vortex_route(vortex, en, ch, ADB_MIXOUT(mix), dest);
+ vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN); // added to original code.
+}
+
+// mixer to src.
+static void
+vortex_connection_mix_src(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char mix, unsigned char src)
+{
+ vortex_route(vortex, en, ch, ADB_MIXOUT(mix), ADB_SRCIN(src));
+ vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN); // added to original code.
+}
+
+#if 0
+static void
+vortex_connection_adbdma_src_src(vortex_t * vortex, int en,
+ unsigned char channel,
+ unsigned char adbdma, unsigned char src0,
+ unsigned char src1)
+{
+ vortex_routes(vortex, en, channel, ADB_DMA(adbdma),
+ ADB_SRCIN(src0), ADB_SRCIN(src1));
+}
+
+// Connect two mix to AdbDma.
+static void
+vortex_connection_mix_mix_adbdma(vortex_t * vortex, int en,
+ unsigned char ch, unsigned char mix0,
+ unsigned char mix1, unsigned char adbdma)
+{
+
+ ADBRamLink routes[2];
+ routes[0] =
+ (((mix0 +
+ OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | (adbdma & ADB_MASK);
+ routes[1] =
+ (((mix1 + OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | ((adbdma +
+ 0x20) &
+ ADB_MASK);
+ if (en) {
+ vortex_adb_addroutes(vortex, ch, routes, 0x2);
+ vortex_mixer_addWTD(vortex, mix0, ch);
+ vortex_mixer_addWTD(vortex, mix1, ch);
+ } else {
+ vortex_adb_delroutes(vortex, ch, routes[0], routes[1]);
+ vortex_mixer_delWTD(vortex, mix0, ch);
+ vortex_mixer_delWTD(vortex, mix1, ch);
+ }
+}
+#endif
+
+/* CODEC connect. */
+
+static void
+vortex_connect_codecplay(vortex_t * vortex, int en, unsigned char mixers[])
+{
+#ifdef CHIP_AU8820
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_CODECOUT(0));
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_CODECOUT(1));
+#else
+#if 1
+ // Connect front channels through EQ.
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_EQIN(0));
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_EQIN(1));
+ /* Lower volume, since EQ has some gain. */
+ vortex_mix_setvolumebyte(vortex, mixers[0], 0);
+ vortex_mix_setvolumebyte(vortex, mixers[1], 0);
+ vortex_route(vortex, en, 0x11, ADB_EQOUT(0), ADB_CODECOUT(0));
+ vortex_route(vortex, en, 0x11, ADB_EQOUT(1), ADB_CODECOUT(1));
+
+ /* Check if reg 0x28 has SDAC bit set. */
+ if (VORTEX_IS_QUAD(vortex)) {
+ /* Rear channel. Note: ADB_CODECOUT(0+2) and (1+2) is for AC97 modem */
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[2],
+ ADB_CODECOUT(0 + 4));
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[3],
+ ADB_CODECOUT(1 + 4));
+ //printk("SDAC detected ");
+ }
+#else
+ // Use plain direct output to codec.
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_CODECOUT(0));
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_CODECOUT(1));
+#endif
+#endif
+}
+
+static void
+vortex_connect_codecrec(vortex_t * vortex, int en, unsigned char mixin0,
+ unsigned char mixin1)
+{
+ /*
+ Enable: 0x1, 0x1
+ Channel: 0x11, 0x11
+ ADB Source address: 0x48, 0x49
+ Destination Asp4Topology_0x9c,0x98
+ */
+ vortex_connection_adb_mixin(vortex, en, 0x11, ADB_CODECIN(0), mixin0);
+ vortex_connection_adb_mixin(vortex, en, 0x11, ADB_CODECIN(1), mixin1);
+}
+
+// Higher level ADB audio path (de)allocator.
+
+/* Resource manager */
+static int resnum[VORTEX_RESOURCE_LAST] =
+ { NR_ADB, NR_SRC, NR_MIXIN, NR_MIXOUT, NR_A3D };
+/*
+ Checkout/Checkin resource of given type.
+ resmap: resource map to be used. If NULL means that we want to allocate
+ a DMA resource (root of all other resources of a dma channel).
+ out: Mean checkout if != 0. Else mean Checkin resource.
+ restype: Indicates type of resource to be checked in or out.
+*/
+static char
+vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
+{
+ int i, qty = resnum[restype], resinuse = 0;
+
+ if (out) {
+ /* Gather used resources by all streams. */
+ for (i = 0; i < NR_ADB; i++) {
+ resinuse |= vortex->dma_adb[i].resources[restype];
+ }
+ resinuse |= vortex->fixed_res[restype];
+ /* Find and take free resource. */
+ for (i = 0; i < qty; i++) {
+ if ((resinuse & (1 << i)) == 0) {
+ if (resmap != NULL)
+ resmap[restype] |= (1 << i);
+ else
+ vortex->dma_adb[i].resources[restype] |= (1 << i);
+ //printk("vortex: ResManager: type %d out %d\n", restype, i);
+ return i;
+ }
+ }
+ } else {
+ if (resmap == NULL)
+ return -EINVAL;
+ /* Checkin first resource of type restype. */
+ for (i = 0; i < qty; i++) {
+ if (resmap[restype] & (1 << i)) {
+ resmap[restype] &= ~(1 << i);
+ //printk("vortex: ResManager: type %d in %d\n",restype, i);
+ return i;
+ }
+ }
+ }
+ printk("vortex: FATAL: ResManager: resource type %d exhausted.\n", restype);
+ return -ENOMEM;
+}
+
+/* Default Connections */
+static int
+vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);
+
+static void vortex_connect_default(vortex_t * vortex, int en)
+{
+ // Connect AC97 codec.
+ vortex->mixplayb[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex->mixplayb[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ if (VORTEX_IS_QUAD(vortex)) {
+ vortex->mixplayb[2] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex->mixplayb[3] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ }
+ vortex_connect_codecplay(vortex, en, vortex->mixplayb);
+
+ vortex->mixcapt[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXIN);
+ vortex->mixcapt[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXIN);
+ vortex_connect_codecrec(vortex, en, MIX_CAPT(0), MIX_CAPT(1));
+
+ // Connect SPDIF
+#ifndef CHIP_AU8820
+ vortex->mixspdif[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex->mixspdif[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[0],
+ ADB_SPDIFOUT(0));
+ vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[1],
+ ADB_SPDIFOUT(1));
+#endif
+ // Connect WT
+#ifndef CHIP_AU8810
+ vortex_wt_connect(vortex, en);
+#endif
+ // A3D (crosstalk canceler and A3D slices). AU8810 disabled for now.
+#ifndef CHIP_AU8820
+ vortex_Vort3D_connect(vortex, en);
+#endif
+ // Connect I2S
+
+ // Connect DSP interface for SQ3500 turbo (not here i think...)
+
+ // Connect AC98 modem codec
+
+}
+
+/*
+ Allocate nr_ch pcm audio routes if dma < 0. If dma >= 0, existing routes
+ are deallocated.
+ dma: DMA engine routes to be deallocated when dma >= 0.
+ nr_ch: Number of channels to be de/allocated.
+ dir: direction of stream. Uses same values as substream->stream.
+ type: Type of audio output/source (codec, spdif, i2s, dsp, etc)
+ Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
+*/
+static int
+vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
+{
+ stream_t *stream;
+ int i, en;
+
+ if ((nr_ch == 3)
+ || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
+ return -EBUSY;
+
+ if (dma >= 0) {
+ en = 0;
+ vortex_adb_checkinout(vortex,
+ vortex->dma_adb[dma].resources, en,
+ VORTEX_RESOURCE_DMA);
+ } else {
+ en = 1;
+ if ((dma =
+ vortex_adb_checkinout(vortex, NULL, en,
+ VORTEX_RESOURCE_DMA)) < 0)
+ return -EBUSY;
+ }
+
+ stream = &vortex->dma_adb[dma];
+ stream->dma = dma;
+ stream->dir = dir;
+ stream->type = type;
+
+ /* PLAYBACK ROUTES. */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ int src[4], mix[4], ch_top;
+#ifndef CHIP_AU8820
+ int a3d = 0;
+#endif
+ /* Get SRC and MIXER hardware resources. */
+ if (stream->type != VORTEX_PCM_SPDIF) {
+ for (i = 0; i < nr_ch; i++) {
+ if ((src[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC)) < 0) {
+ memset(stream->resources, 0,
+ sizeof(unsigned char) *
+ VORTEX_RESOURCE_LAST);
+ return -EBUSY;
+ }
+ if (stream->type != VORTEX_PCM_A3D) {
+ if ((mix[i] = vortex_adb_checkinout(vortex,
+ stream->resources,
+ en,
+ VORTEX_RESOURCE_MIXIN)) < 0) {
+ memset(stream->resources,
+ 0,
+ sizeof(unsigned char) * VORTEX_RESOURCE_LAST);
+ return -EBUSY;
+ }
+ }
+ }
+ }
+#ifndef CHIP_AU8820
+ if (stream->type == VORTEX_PCM_A3D) {
+ if ((a3d =
+ vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_A3D)) < 0) {
+ memset(stream->resources, 0,
+ sizeof(unsigned char) *
+ VORTEX_RESOURCE_LAST);
+ printk("vortex: out of A3D sources. Sorry\n");
+ return -EBUSY;
+ }
+ /* (De)Initialize A3D hardware source. */
+ vortex_Vort3D_InitializeSource(&(vortex->a3d[a3d]), en);
+ }
+ /* Make SPDIF out exclusive to "spdif" device when in use. */
+ if ((stream->type == VORTEX_PCM_SPDIF) && (en)) {
+ vortex_route(vortex, 0, 0x14,
+ ADB_MIXOUT(vortex->mixspdif[0]),
+ ADB_SPDIFOUT(0));
+ vortex_route(vortex, 0, 0x14,
+ ADB_MIXOUT(vortex->mixspdif[1]),
+ ADB_SPDIFOUT(1));
+ }
+#endif
+ /* Make playback routes. */
+ for (i = 0; i < nr_ch; i++) {
+ if (stream->type == VORTEX_PCM_ADB) {
+ vortex_connection_adbdma_src(vortex, en,
+ src[nr_ch - 1],
+ dma,
+ src[i]);
+ vortex_connection_src_mixin(vortex, en,
+ 0x11, src[i],
+ mix[i]);
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i],
+ MIX_PLAYB(i), 0);
+#ifndef CHIP_AU8820
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i],
+ MIX_SPDIF(i % 2), 0);
+ vortex_mix_setinputvolumebyte(vortex,
+ MIX_SPDIF(i % 2),
+ mix[i],
+ MIX_DEFIGAIN);
+#endif
+ }
+#ifndef CHIP_AU8820
+ if (stream->type == VORTEX_PCM_A3D) {
+ vortex_connection_adbdma_src(vortex, en,
+ src[nr_ch - 1],
+ dma,
+ src[i]);
+ vortex_route(vortex, en, 0x11, ADB_SRCOUT(src[i]), ADB_A3DIN(a3d));
+ /* XTalk test. */
+ //vortex_route(vortex, en, 0x11, dma, ADB_XTALKIN(i?9:4));
+ //vortex_route(vortex, en, 0x11, ADB_SRCOUT(src[i]), ADB_XTALKIN(i?4:9));
+ }
+ if (stream->type == VORTEX_PCM_SPDIF)
+ vortex_route(vortex, en, 0x14,
+ ADB_DMA(stream->dma),
+ ADB_SPDIFOUT(i));
+#endif
+ }
+ if (stream->type != VORTEX_PCM_SPDIF && stream->type != VORTEX_PCM_A3D) {
+ ch_top = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = nr_ch; i < ch_top; i++) {
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i % nr_ch],
+ MIX_PLAYB(i), 0);
+#ifndef CHIP_AU8820
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i % nr_ch],
+ MIX_SPDIF(i % 2),
+ 0);
+ vortex_mix_setinputvolumebyte(vortex,
+ MIX_SPDIF(i % 2),
+ mix[i % nr_ch],
+ MIX_DEFIGAIN);
+#endif
+ }
+ }
+#ifndef CHIP_AU8820
+ else {
+ if (nr_ch == 1 && stream->type == VORTEX_PCM_SPDIF)
+ vortex_route(vortex, en, 0x14,
+ ADB_DMA(stream->dma),
+ ADB_SPDIFOUT(1));
+ }
+ /* Reconnect SPDIF out when "spdif" device is down. */
+ if ((stream->type == VORTEX_PCM_SPDIF) && (!en)) {
+ vortex_route(vortex, 1, 0x14,
+ ADB_MIXOUT(vortex->mixspdif[0]),
+ ADB_SPDIFOUT(0));
+ vortex_route(vortex, 1, 0x14,
+ ADB_MIXOUT(vortex->mixspdif[1]),
+ ADB_SPDIFOUT(1));
+ }
+#endif
+ /* CAPTURE ROUTES. */
+ } else {
+ int src[2], mix[2];
+
+ /* Get SRC and MIXER hardware resources. */
+ for (i = 0; i < nr_ch; i++) {
+ if ((mix[i] =
+ vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_MIXOUT))
+ < 0) {
+ memset(stream->resources, 0,
+ sizeof(unsigned char) *
+ VORTEX_RESOURCE_LAST);
+ return -EBUSY;
+ }
+ if ((src[i] =
+ vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC)) < 0) {
+ memset(stream->resources, 0,
+ sizeof(unsigned char) *
+ VORTEX_RESOURCE_LAST);
+ return -EBUSY;
+ }
+ }
+
+ /* Make capture routes. */
+ vortex_connection_mixin_mix(vortex, en, MIX_CAPT(0), mix[0], 0);
+ vortex_connection_mix_src(vortex, en, 0x11, mix[0], src[0]);
+ if (nr_ch == 1) {
+ vortex_connection_mixin_mix(vortex, en,
+ MIX_CAPT(1), mix[0], 0);
+ vortex_connection_src_adbdma(vortex, en,
+ src[0],
+ src[0], dma);
+ } else {
+ vortex_connection_mixin_mix(vortex, en,
+ MIX_CAPT(1), mix[1], 0);
+ vortex_connection_mix_src(vortex, en, 0x11, mix[1],
+ src[1]);
+ vortex_connection_src_src_adbdma(vortex, en,
+ src[1], src[0],
+ src[1], dma);
+ }
+ }
+ vortex->dma_adb[dma].nr_ch = nr_ch;
+
+#if 0
+ /* AC97 Codec channel setup. FIXME: this has no effect on some cards !! */
+ if (nr_ch < 4) {
+ /* Copy stereo to rear channel (surround) */
+ snd_ac97_write_cache(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT,
+ snd_ac97_read(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT)
+ | 4);
+ } else {
+ /* Allow separate front and rear channels. */
+ snd_ac97_write_cache(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT,
+ snd_ac97_read(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT)
+ & ~((u32)
+ 4));
+ }
+#endif
+ return dma;
+}
+
+/*
+ Set the SampleRate of the SRC's attached to the given DMA engine.
+ */
+static void
+vortex_adb_setsrc(vortex_t * vortex, int adbdma, unsigned int rate, int dir)
+{
+ stream_t *stream = &(vortex->dma_adb[adbdma]);
+ int i, cvrt;
+
+ /* dir=1:play ; dir=0:rec */
+ if (dir)
+ cvrt = SRC_RATIO(rate, 48000);
+ else
+ cvrt = SRC_RATIO(48000, rate);
+
+ /* Setup SRC's */
+ for (i = 0; i < NR_SRC; i++) {
+ if (stream->resources[VORTEX_RESOURCE_SRC] & (1 << i))
+ vortex_src_setupchannel(vortex, i, cvrt, 0, 0, i, dir, 1, cvrt, dir);
+ }
+}
+
+// Timer and ISR functions.
+
+static void vortex_settimer(vortex_t * vortex, int period)
+{
+ //set the timer period to <period> 48000ths of a second.
+ hwwrite(vortex->mmio, VORTEX_IRQ_STAT, period);
+}
+
+#if 0
+static void vortex_enable_timer_int(vortex_t * card)
+{
+ hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+ hwread(card->mmio, VORTEX_IRQ_CTRL) | IRQ_TIMER | 0x60);
+}
+
+static void vortex_disable_timer_int(vortex_t * card)
+{
+ hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+ hwread(card->mmio, VORTEX_IRQ_CTRL) & ~IRQ_TIMER);
+}
+
+#endif
+static void vortex_enable_int(vortex_t * card)
+{
+ // CAsp4ISR__EnableVortexInt_void_
+ hwwrite(card->mmio, VORTEX_CTRL,
+ hwread(card->mmio, VORTEX_CTRL) | CTRL_IRQ_ENABLE);
+ hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+ (hwread(card->mmio, VORTEX_IRQ_CTRL) & 0xffffefc0) | 0x24);
+}
+
+static void vortex_disable_int(vortex_t * card)
+{
+ hwwrite(card->mmio, VORTEX_CTRL,
+ hwread(card->mmio, VORTEX_CTRL) & ~CTRL_IRQ_ENABLE);
+}
+
+static irqreturn_t vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ vortex_t *vortex = dev_id;
+ int i, handled;
+ u32 source;
+
+ //check if the interrupt is ours.
+ if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1))
+ return IRQ_NONE;
+
+ // This is the Interrrupt Enable flag we set before (consistency check).
+ if (!(hwread(vortex->mmio, VORTEX_CTRL) & CTRL_IRQ_ENABLE))
+ return IRQ_NONE;
+
+ source = hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
+ // Reset IRQ flags.
+ hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, source);
+ hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
+ // Is at least one IRQ flag set?
+ if (source == 0) {
+ printk(KERN_ERR "vortex: missing irq source\n");
+ return IRQ_NONE;
+ }
+
+ handled = 0;
+ // Attend every interrupt source.
+ if (unlikely(source & IRQ_ERR_MASK)) {
+ if (source & IRQ_FATAL) {
+ printk(KERN_ERR "vortex: IRQ fatal error\n");
+ }
+ if (source & IRQ_PARITY) {
+ printk(KERN_ERR "vortex: IRQ parity error\n");
+ }
+ if (source & IRQ_REG) {
+ printk(KERN_ERR "vortex: IRQ reg error\n");
+ }
+ if (source & IRQ_FIFO) {
+ printk(KERN_ERR "vortex: IRQ fifo error\n");
+ }
+ if (source & IRQ_DMA) {
+ printk(KERN_ERR "vortex: IRQ dma error\n");
+ }
+ handled = 1;
+ }
+ if (source & IRQ_PCMOUT) {
+ /* ALSA period acknowledge. */
+ spin_lock(&vortex->lock);
+ for (i = 0; i < NR_ADB; i++) {
+ if (vortex->dma_adb[i].fifo_status == FIFO_START) {
+ if (vortex_adbdma_bufshift(vortex, i)) ;
+ spin_unlock(&vortex->lock);
+ snd_pcm_period_elapsed(vortex->dma_adb[i].
+ substream);
+ spin_lock(&vortex->lock);
+ }
+ }
+#ifndef CHIP_AU8810
+ for (i = 0; i < NR_WT; i++) {
+ if (vortex->dma_wt[i].fifo_status == FIFO_START) {
+ if (vortex_wtdma_bufshift(vortex, i)) ;
+ spin_unlock(&vortex->lock);
+ snd_pcm_period_elapsed(vortex->dma_wt[i].
+ substream);
+ spin_lock(&vortex->lock);
+ }
+ }
+#endif
+ spin_unlock(&vortex->lock);
+ handled = 1;
+ }
+ //Acknowledge the Timer interrupt
+ if (source & IRQ_TIMER) {
+ hwread(vortex->mmio, VORTEX_IRQ_STAT);
+ handled = 1;
+ }
+ if (source & IRQ_MIDI) {
+ snd_mpu401_uart_interrupt(vortex->irq,
+ vortex->rmidi->private_data, regs);
+ handled = 1;
+ }
+
+ if (!handled) {
+ printk(KERN_ERR "vortex: unknown irq source %x\n", source);
+ }
+ return IRQ_RETVAL(handled);
+}
+
+/* Codec */
+
+#define POLL_COUNT 1000
+static void vortex_codec_init(vortex_t * vortex)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ /* the windows driver writes -i, so we write -i */
+ hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), -i);
+ msleep(2);
+ }
+ if (0) {
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x8068);
+ msleep(1);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+ msleep(1);
+ } else {
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80e8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+ }
+ for (i = 0; i < 32; i++) {
+ hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), -i);
+ msleep(5);
+ }
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0xe8);
+ msleep(1);
+ /* Enable codec channels 0 and 1. */
+ hwwrite(vortex->mmio, VORTEX_CODEC_EN,
+ hwread(vortex->mmio, VORTEX_CODEC_EN) | EN_CODEC);
+}
+
+static void
+vortex_codec_write(ac97_t * codec, unsigned short addr, unsigned short data)
+{
+
+ vortex_t *card = (vortex_t *) codec->private_data;
+ unsigned int lifeboat = 0;
+
+ /* wait for transactions to clear */
+ while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+ udelay(100);
+ if (lifeboat++ > POLL_COUNT) {
+ printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+ return;
+ }
+ }
+ /* write register */
+ hwwrite(card->mmio, VORTEX_CODEC_IO,
+ ((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK) |
+ ((data << VORTEX_CODEC_DATSHIFT) & VORTEX_CODEC_DATMASK) |
+ VORTEX_CODEC_WRITE);
+
+ /* Flush Caches. */
+ hwread(card->mmio, VORTEX_CODEC_IO);
+}
+
+static unsigned short vortex_codec_read(ac97_t * codec, unsigned short addr)
+{
+
+ vortex_t *card = (vortex_t *) codec->private_data;
+ u32 read_addr, data;
+ unsigned lifeboat = 0;
+
+ /* wait for transactions to clear */
+ while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+ udelay(100);
+ if (lifeboat++ > POLL_COUNT) {
+ printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+ return 0xffff;
+ }
+ }
+ /* set up read address */
+ read_addr = ((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK);
+ hwwrite(card->mmio, VORTEX_CODEC_IO, read_addr);
+
+ /* wait for address */
+ do {
+ udelay(100);
+ data = hwread(card->mmio, VORTEX_CODEC_IO);
+ if (lifeboat++ > POLL_COUNT) {
+ printk(KERN_ERR "vortex: ac97 address never arrived\n");
+ return 0xffff;
+ }
+ } while ((data & VORTEX_CODEC_ADDMASK) !=
+ (addr << VORTEX_CODEC_ADDSHIFT));
+
+ /* return data. */
+ return (u16) (data & VORTEX_CODEC_DATMASK);
+}
+
+/* SPDIF support */
+
+static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
+{
+ int i, this_38 = 0, this_04 = 0, this_08 = 0, this_0c = 0;
+
+ /* CAsp4Spdif::InitializeSpdifHardware(void) */
+ hwwrite(vortex->mmio, VORTEX_SPDIF_FLAGS,
+ hwread(vortex->mmio, VORTEX_SPDIF_FLAGS) & 0xfff3fffd);
+ //for (i=0x291D4; i<0x29200; i+=4)
+ for (i = 0; i < 11; i++)
+ hwwrite(vortex->mmio, VORTEX_SPDIF_CFG1 + (i << 2), 0);
+ //hwwrite(vortex->mmio, 0x29190, hwread(vortex->mmio, 0x29190) | 0xc0000);
+ hwwrite(vortex->mmio, VORTEX_CODEC_EN,
+ hwread(vortex->mmio, VORTEX_CODEC_EN) | EN_SPDIF);
+
+ /* CAsp4Spdif::ProgramSRCInHardware(enum SPDIF_SR,enum SPDIFMODE) */
+ if (this_04 && this_08) {
+ int edi;
+
+ i = (((0x5DC00000 / spdif_sr) + 1) >> 1);
+ if (i > 0x800) {
+ if (i < 0x1ffff)
+ edi = (i >> 1);
+ else
+ edi = 0x1ffff;
+ } else {
+ i = edi = 0x800;
+ }
+ /* this_04 and this_08 are the CASp4Src's (samplerate converters) */
+ vortex_src_setupchannel(vortex, this_04, edi, 0, 1,
+ this_0c, 1, 0, edi, 1);
+ vortex_src_setupchannel(vortex, this_08, edi, 0, 1,
+ this_0c, 1, 0, edi, 1);
+ }
+
+ i = spdif_sr;
+ spdif_sr |= 0x8c;
+ switch (i) {
+ case 32000:
+ this_38 &= 0xFFFFFFFE;
+ this_38 &= 0xFFFFFFFD;
+ this_38 &= 0xF3FFFFFF;
+ this_38 |= 0x03000000; /* set 32khz samplerate */
+ this_38 &= 0xFFFFFF3F;
+ spdif_sr &= 0xFFFFFFFD;
+ spdif_sr |= 1;
+ break;
+ case 44100:
+ this_38 &= 0xFFFFFFFE;
+ this_38 &= 0xFFFFFFFD;
+ this_38 &= 0xF0FFFFFF;
+ this_38 |= 0x03000000;
+ this_38 &= 0xFFFFFF3F;
+ spdif_sr &= 0xFFFFFFFC;
+ break;
+ case 48000:
+ if (spdif_mode == 1) {
+ this_38 &= 0xFFFFFFFE;
+ this_38 &= 0xFFFFFFFD;
+ this_38 &= 0xF2FFFFFF;
+ this_38 |= 0x02000000; /* set 48khz samplerate */
+ this_38 &= 0xFFFFFF3F;
+ } else {
+ /* J. Gordon Wolfe: I think this stuff is for AC3 */
+ this_38 |= 0x00000003;
+ this_38 &= 0xFFFFFFBF;
+ this_38 |= 0x80;
+ }
+ spdif_sr |= 2;
+ spdif_sr &= 0xFFFFFFFE;
+ break;
+
+ }
+ /* looks like the next 2 lines transfer a 16-bit value into 2 8-bit
+ registers. seems to be for the standard IEC/SPDIF initialization
+ stuff */
+ hwwrite(vortex->mmio, VORTEX_SPDIF_CFG0, this_38 & 0xffff);
+ hwwrite(vortex->mmio, VORTEX_SPDIF_CFG1, this_38 >> 0x10);
+ hwwrite(vortex->mmio, VORTEX_SPDIF_SMPRATE, spdif_sr);
+}
+
+/* Initialization */
+
+static int vortex_core_init(vortex_t * vortex)
+{
+
+ printk(KERN_INFO "Vortex: init.... ");
+ /* Hardware Init. */
+ hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
+ msleep(5);
+ hwwrite(vortex->mmio, VORTEX_CTRL,
+ hwread(vortex->mmio, VORTEX_CTRL) & 0xffdfffff);
+ msleep(5);
+ /* Reset IRQ flags */
+ hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffffffff);
+ hwread(vortex->mmio, VORTEX_IRQ_STAT);
+
+ vortex_codec_init(vortex);
+
+#ifdef CHIP_AU8830
+ hwwrite(vortex->mmio, VORTEX_CTRL,
+ hwread(vortex->mmio, VORTEX_CTRL) | 0x1000000);
+#endif
+
+ /* Init audio engine. */
+ vortex_adbdma_init(vortex);
+ hwwrite(vortex->mmio, VORTEX_ENGINE_CTRL, 0x0); //, 0xc83c7e58, 0xc5f93e58
+ vortex_adb_init(vortex);
+ /* Init processing blocks. */
+ vortex_fifo_init(vortex);
+ vortex_mixer_init(vortex);
+ vortex_srcblock_init(vortex);
+#ifndef CHIP_AU8820
+ vortex_eq_init(vortex);
+ vortex_spdif_init(vortex, 48000, 1);
+ vortex_Vort3D(vortex, 1);
+#endif
+#ifndef CHIP_AU8810
+ vortex_wt_init(vortex);
+#endif
+ // Moved to au88x0.c
+ //vortex_connect_default(vortex, 1);
+
+ vortex_settimer(vortex, 0x90);
+ // Enable Interrupts.
+ // vortex_enable_int() must be first !!
+ // hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, 0);
+ // vortex_enable_int(vortex);
+ //vortex_enable_timer_int(vortex);
+ //vortex_disable_timer_int(vortex);
+
+ printk(KERN_INFO "done.\n");
+ spin_lock_init(&vortex->lock);
+
+ return 0;
+}
+
+static int vortex_core_shutdown(vortex_t * vortex)
+{
+
+ printk(KERN_INFO "Vortex: shutdown...");
+#ifndef CHIP_AU8820
+ vortex_eq_free(vortex);
+ vortex_Vort3D(vortex, 0);
+#endif
+ //vortex_disable_timer_int(vortex);
+ vortex_disable_int(vortex);
+ vortex_connect_default(vortex, 0);
+ /* Reset all DMA fifos. */
+ vortex_fifo_init(vortex);
+ /* Erase all audio routes. */
+ vortex_adb_init(vortex);
+
+ /* Disable MPU401 */
+ //hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, hwread(vortex->mmio, VORTEX_IRQ_CTRL) & ~IRQ_MIDI);
+ //hwwrite(vortex->mmio, VORTEX_CTRL, hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_EN);
+
+ hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, 0);
+ hwwrite(vortex->mmio, VORTEX_CTRL, 0);
+ msleep(5);
+ hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
+
+ printk(KERN_INFO "done.\n");
+ return 0;
+}
+
+/* Alsa support. */
+
+static int vortex_alsafmt_aspfmt(int alsafmt)
+{
+ int fmt;
+
+ switch (alsafmt) {
+ case SNDRV_PCM_FORMAT_U8:
+ fmt = 0x1;
+ break;
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ fmt = 0x2;
+ break;
+ case SNDRV_PCM_FORMAT_A_LAW:
+ fmt = 0x3;
+ break;
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ fmt = 0x4; /* guess. */
+ break;
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ fmt = 0x5; /* guess. */
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ fmt = 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ fmt = 0x9; /* check this... */
+ break;
+ default:
+ fmt = 0x8;
+ printk(KERN_ERR "vortex: format unsupported %d\n", alsafmt);
+ break;
+ }
+ return fmt;
+}
+
+/* Some not yet useful translations. */
+#if 0
+typedef enum {
+ ASPFMTLINEAR16 = 0, /* 0x8 */
+ ASPFMTLINEAR8, /* 0x1 */
+ ASPFMTULAW, /* 0x2 */
+ ASPFMTALAW, /* 0x3 */
+ ASPFMTSPORT, /* ? */
+ ASPFMTSPDIF, /* ? */
+} ASPENCODING;
+
+static int
+vortex_translateformat(vortex_t * vortex, char bits, char nch, int encod)
+{
+ int a, this_194;
+
+ if ((bits != 8) || (bits != 16))
+ return -1;
+
+ switch (encod) {
+ case 0:
+ if (bits == 0x10)
+ a = 8; // 16 bit
+ break;
+ case 1:
+ if (bits == 8)
+ a = 1; // 8 bit
+ break;
+ case 2:
+ a = 2; // U_LAW
+ break;
+ case 3:
+ a = 3; // A_LAW
+ break;
+ }
+ switch (nch) {
+ case 1:
+ this_194 = 0;
+ break;
+ case 2:
+ this_194 = 1;
+ break;
+ case 4:
+ this_194 = 1;
+ break;
+ case 6:
+ this_194 = 1;
+ break;
+ }
+ return (a);
+}
+
+static void vortex_cdmacore_setformat(vortex_t * vortex, int bits, int nch)
+{
+ short int d, this_148;
+
+ d = ((bits >> 3) * nch);
+ this_148 = 0xbb80 / d;
+}
+#endif
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
new file mode 100644
index 00000000000..53b47a42c7d
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eq.c
@@ -0,0 +1,937 @@
+/***************************************************************************
+ * au88x0_eq.c
+ * Aureal Vortex Hardware EQ control/access.
+ *
+ * Sun Jun 8 18:19:19 2003
+ * 2003 Manuel Jander (mjander@users.sourceforge.net)
+ *
+ * 02 July 2003: First time something works :)
+ * November 2003: A3D Bypass code completed but untested.
+ *
+ * TODO:
+ * - Debug (testing)
+ * - Test peak visualization support.
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ The Aureal Hardware EQ is found on AU8810 and AU8830 chips only.
+ it has 4 inputs (2 for general mix, 2 for A3D) and 2 outputs (supposed
+ to be routed to the codec).
+*/
+
+#include "au88x0.h"
+#include "au88x0_eq.h"
+#include "au88x0_eqdata.c"
+
+#define VORTEX_EQ_BASE 0x2b000
+#define VORTEX_EQ_DEST (VORTEX_EQ_BASE + 0x410)
+#define VORTEX_EQ_SOURCE (VORTEX_EQ_BASE + 0x430)
+#define VORTEX_EQ_CTRL (VORTEX_EQ_BASE + 0x440)
+
+#define VORTEX_BAND_COEFF_SIZE 0x30
+
+/* CEqHw.s */
+static void vortex_EqHw_SetTimeConsts(vortex_t * vortex, u16 gain, u16 level)
+{
+ hwwrite(vortex->mmio, 0x2b3c4, gain);
+ hwwrite(vortex->mmio, 0x2b3c8, level);
+}
+
+static inline u16 sign_invert(u16 a)
+{
+ /* -(-32768) -> -32768 so we do -(-32768) -> 32767 to make the result positive */
+ if (a == (u16)-32768)
+ return 32767;
+ else
+ return -a;
+}
+
+static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 coefs[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, n /*esp2c */;
+
+ for (n = 0; n < eqhw->this04; n++) {
+ hwwrite(vortex->mmio, 0x2b000 + n * 0x30, coefs[i + 0]);
+ hwwrite(vortex->mmio, 0x2b004 + n * 0x30, coefs[i + 1]);
+
+ if (eqhw->this08 == 0) {
+ hwwrite(vortex->mmio, 0x2b008 + n * 0x30, coefs[i + 2]);
+ hwwrite(vortex->mmio, 0x2b00c + n * 0x30, coefs[i + 3]);
+ hwwrite(vortex->mmio, 0x2b010 + n * 0x30, coefs[i + 4]);
+ } else {
+ hwwrite(vortex->mmio, 0x2b008 + n * 0x30, sign_invert(coefs[2 + i]));
+ hwwrite(vortex->mmio, 0x2b00c + n * 0x30, sign_invert(coefs[3 + i]));
+ hwwrite(vortex->mmio, 0x2b010 + n * 0x30, sign_invert(coefs[4 + i]));
+ }
+ i += 5;
+ }
+}
+
+static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 coefs[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, n /*esp2c */;
+
+ for (n = 0; n < eqhw->this04; n++) {
+ hwwrite(vortex->mmio, 0x2b1e0 + n * 0x30, coefs[0 + i]);
+ hwwrite(vortex->mmio, 0x2b1e4 + n * 0x30, coefs[1 + i]);
+
+ if (eqhw->this08 == 0) {
+ hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, coefs[2 + i]);
+ hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, coefs[3 + i]);
+ hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, coefs[4 + i]);
+ } else {
+ hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, sign_invert(coefs[2 + i]));
+ hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, sign_invert(coefs[3 + i]));
+ hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, sign_invert(coefs[4 + i]));
+ }
+ i += 5;
+ }
+
+}
+
+static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, ebx;
+
+ hwwrite(vortex->mmio, 0x2b3fc, a[0]);
+ hwwrite(vortex->mmio, 0x2b400, a[1]);
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b014 + (i * 0xc), b[i]);
+ hwwrite(vortex->mmio, 0x2b018 + (i * 0xc), b[1 + i]);
+ hwwrite(vortex->mmio, 0x2b01c + (i * 0xc), b[2 + i]);
+ hwwrite(vortex->mmio, 0x2b020 + (i * 0xc), b[3 + i]);
+ i += 4;
+ }
+}
+
+static void vortex_EqHw_SetRightStates(vortex_t * vortex, u16 a[], u16 b[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, ebx;
+
+ hwwrite(vortex->mmio, 0x2b404, a[0]);
+ hwwrite(vortex->mmio, 0x2b408, a[1]);
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b1f4 + (i * 0xc), b[i]);
+ hwwrite(vortex->mmio, 0x2b1f8 + (i * 0xc), b[1 + i]);
+ hwwrite(vortex->mmio, 0x2b1fc + (i * 0xc), b[2 + i]);
+ hwwrite(vortex->mmio, 0x2b200 + (i * 0xc), b[3 + i]);
+ i += 4;
+ }
+}
+
+#if 0
+static void vortex_EqHw_GetTimeConsts(vortex_t * vortex, u16 * a, u16 * b)
+{
+ *a = hwread(vortex->mmio, 0x2b3c4);
+ *b = hwread(vortex->mmio, 0x2b3c8);
+}
+
+static void vortex_EqHw_GetLeftCoefs(vortex_t * vortex, u16 a[])
+{
+
+}
+
+static void vortex_EqHw_GetRightCoefs(vortex_t * vortex, u16 a[])
+{
+
+}
+
+static void vortex_EqHw_GetLeftStates(vortex_t * vortex, u16 * a, u16 b[])
+{
+
+}
+
+static void vortex_EqHw_GetRightStates(vortex_t * vortex, u16 * a, u16 b[])
+{
+
+}
+
+#endif
+/* Mix Gains */
+static void vortex_EqHw_SetBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ if (eqhw->this08 == 0) {
+ hwwrite(vortex->mmio, 0x2b3d4, a);
+ hwwrite(vortex->mmio, 0x2b3ec, b);
+ } else {
+ hwwrite(vortex->mmio, 0x2b3d4, sign_invert(a));
+ hwwrite(vortex->mmio, 0x2b3ec, sign_invert(b));
+ }
+}
+
+static void vortex_EqHw_SetA3DBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+ hwwrite(vortex->mmio, 0x2b3e0, a);
+ hwwrite(vortex->mmio, 0x2b3f8, b);
+}
+
+#if 0
+static void vortex_EqHw_SetCurrBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+ hwwrite(vortex->mmio, 0x2b3d0, a);
+ hwwrite(vortex->mmio, 0x2b3e8, b);
+}
+
+static void vortex_EqHw_SetCurrA3DBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+ hwwrite(vortex->mmio, 0x2b3dc, a);
+ hwwrite(vortex->mmio, 0x2b3f4, b);
+}
+
+#endif
+static void
+vortex_EqHw_SetLeftGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
+{
+ hwwrite(vortex->mmio, 0x2b02c + (index * 0x30), b);
+}
+
+static void
+vortex_EqHw_SetRightGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
+{
+ hwwrite(vortex->mmio, 0x2b20c + (index * 0x30), b);
+}
+
+static void vortex_EqHw_SetLeftGainsTarget(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b02c + ebx * 0x30, a[ebx]);
+ }
+}
+
+static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b20c + ebx * 0x30, a[ebx]);
+ }
+}
+
+static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b028 + ebx * 0x30, a[ebx]);
+ }
+}
+
+static void vortex_EqHw_SetRightGainsCurrent(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b208 + ebx * 0x30, a[ebx]);
+ }
+}
+
+#if 0
+static void vortex_EqHw_GetLeftGainsTarget(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b02c + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetRightGainsTarget(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b20c + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b028 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetRightGainsCurrent(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b208 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+#endif
+/* EQ band levels settings */
+static void vortex_EqHw_SetLevels(vortex_t * vortex, u16 peaks[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i;
+
+ /* set left peaks */
+ for (i = 0; i < eqhw->this04; i++) {
+ hwwrite(vortex->mmio, 0x2b024 + i * VORTEX_BAND_COEFF_SIZE, peaks[i]);
+ }
+
+ hwwrite(vortex->mmio, 0x2b3cc, peaks[eqhw->this04]);
+ hwwrite(vortex->mmio, 0x2b3d8, peaks[eqhw->this04 + 1]);
+
+ /* set right peaks */
+ for (i = 0; i < eqhw->this04; i++) {
+ hwwrite(vortex->mmio, 0x2b204 + i * VORTEX_BAND_COEFF_SIZE,
+ peaks[i + (eqhw->this04 + 2)]);
+ }
+
+ hwwrite(vortex->mmio, 0x2b3e4, peaks[2 + (eqhw->this04 * 2)]);
+ hwwrite(vortex->mmio, 0x2b3f0, peaks[3 + (eqhw->this04 * 2)]);
+}
+
+#if 0
+static void vortex_EqHw_GetLevels(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ ebx = 0;
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b024 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+
+ a[eqhw->this04] = hwread(vortex->mmio, 0x2b3cc);
+ a[eqhw->this04 + 1] = hwread(vortex->mmio, 0x2b3d8);
+
+ ebx = 0;
+ do {
+ a[ebx + (eqhw->this04 + 2)] =
+ hwread(vortex->mmio, 0x2b204 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+
+ a[2 + (eqhw->this04 * 2)] = hwread(vortex->mmio, 0x2b3e4);
+ a[3 + (eqhw->this04 * 2)] = hwread(vortex->mmio, 0x2b3f0);
+}
+
+#endif
+/* Global Control */
+static void vortex_EqHw_SetControlReg(vortex_t * vortex, unsigned long reg)
+{
+ hwwrite(vortex->mmio, 0x2b440, reg);
+}
+
+static void vortex_EqHw_SetSampleRate(vortex_t * vortex, int sr)
+{
+ hwwrite(vortex->mmio, 0x2b440, ((sr & 0x1f) << 3) | 0xb800);
+}
+
+#if 0
+static void vortex_EqHw_GetControlReg(vortex_t * vortex, unsigned long *reg)
+{
+ *reg = hwread(vortex->mmio, 0x2b440);
+}
+
+static void vortex_EqHw_GetSampleRate(vortex_t * vortex, int *sr)
+{
+ *sr = (hwread(vortex->mmio, 0x2b440) >> 3) & 0x1f;
+}
+
+#endif
+static void vortex_EqHw_Enable(vortex_t * vortex)
+{
+ hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf001);
+}
+
+static void vortex_EqHw_Disable(vortex_t * vortex)
+{
+ hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf000);
+}
+
+/* Reset (zero) buffers */
+static void vortex_EqHw_ZeroIO(vortex_t * vortex)
+{
+ int i;
+ for (i = 0; i < 0x8; i++)
+ hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0);
+ for (i = 0; i < 0x4; i++)
+ hwwrite(vortex->mmio, VORTEX_EQ_SOURCE + (i << 2), 0x0);
+}
+
+static void vortex_EqHw_ZeroA3DIO(vortex_t * vortex)
+{
+ int i;
+ for (i = 0; i < 0x4; i++)
+ hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0);
+}
+
+static void vortex_EqHw_ZeroState(vortex_t * vortex)
+{
+
+ vortex_EqHw_SetControlReg(vortex, 0);
+ vortex_EqHw_ZeroIO(vortex);
+ hwwrite(vortex->mmio, 0x2b3c0, 0);
+
+ vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+
+ vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsZeros);
+ vortex_EqHw_SetRightCoefs(vortex, asEqCoefsZeros);
+
+ vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_zero);
+ vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_zero);
+ vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_zero);
+ vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_zero);
+
+ vortex_EqHw_SetBypassGain(vortex, 0, 0);
+ //vortex_EqHw_SetCurrBypassGain(vortex, 0, 0);
+ vortex_EqHw_SetA3DBypassGain(vortex, 0, 0);
+ //vortex_EqHw_SetCurrA3DBypassGain(vortex, 0, 0);
+ vortex_EqHw_SetLeftStates(vortex, eq_states_zero, asEqOutStateZeros);
+ vortex_EqHw_SetRightStates(vortex, eq_states_zero, asEqOutStateZeros);
+ vortex_EqHw_SetLevels(vortex, (u16 *) eq_levels);
+}
+
+/* Program coeficients as pass through */
+static void vortex_EqHw_ProgramPipe(vortex_t * vortex)
+{
+ vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+
+ vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsPipes);
+ vortex_EqHw_SetRightCoefs(vortex, asEqCoefsPipes);
+
+ vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_current);
+ vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_current);
+ vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_current);
+ vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_current);
+}
+
+/* Program EQ block as 10 band Equalizer */
+static void
+vortex_EqHw_Program10Band(vortex_t * vortex, auxxEqCoeffSet_t * coefset)
+{
+
+ vortex_EqHw_SetTimeConsts(vortex, 0xc, 0x7fe0);
+
+ vortex_EqHw_SetLeftCoefs(vortex, coefset->LeftCoefs);
+ vortex_EqHw_SetRightCoefs(vortex, coefset->RightCoefs);
+
+ vortex_EqHw_SetLeftGainsCurrent(vortex, coefset->LeftGains);
+
+ vortex_EqHw_SetRightGainsTarget(vortex, coefset->RightGains);
+ vortex_EqHw_SetLeftGainsTarget(vortex, coefset->LeftGains);
+
+ vortex_EqHw_SetRightGainsCurrent(vortex, coefset->RightGains);
+}
+
+/* Read all EQ peaks. (think VU meter) */
+static void vortex_EqHw_GetTenBandLevels(vortex_t * vortex, u16 peaks[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i;
+
+ if (eqhw->this04 <= 0)
+ return;
+
+ for (i = 0; i < eqhw->this04; i++)
+ peaks[i] = hwread(vortex->mmio, 0x2B024 + i * 0x30);
+ for (i = 0; i < eqhw->this04; i++)
+ peaks[i + eqhw->this04] =
+ hwread(vortex->mmio, 0x2B204 + i * 0x30);
+}
+
+/* CEqlzr.s */
+
+static int vortex_Eqlzr_GetLeftGain(vortex_t * vortex, u16 index, u16 * gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28) {
+ *gain = eq->this130[index];
+ return 0;
+ }
+ return 1;
+}
+
+static void vortex_Eqlzr_SetLeftGain(vortex_t * vortex, u16 index, u16 gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28 == 0)
+ return;
+
+ eq->this130[index] = gain;
+ if (eq->this54)
+ return;
+
+ vortex_EqHw_SetLeftGainsSingleTarget(vortex, index, gain);
+}
+
+static int vortex_Eqlzr_GetRightGain(vortex_t * vortex, u16 index, u16 * gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28) {
+ *gain = eq->this130[index + eq->this10];
+ return 0;
+ }
+ return 1;
+}
+
+static void vortex_Eqlzr_SetRightGain(vortex_t * vortex, u16 index, u16 gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28 == 0)
+ return;
+
+ eq->this130[index + eq->this10] = gain;
+ if (eq->this54)
+ return;
+
+ vortex_EqHw_SetRightGainsSingleTarget(vortex, index, gain);
+}
+
+#if 0
+static int
+vortex_Eqlzr_GetAllBands(vortex_t * vortex, u16 * gains, unsigned long *cnt)
+{
+ eqlzr_t *eq = &(vortex->eq);
+ int si = 0;
+
+ if (eq->this10 == 0)
+ return 1;
+
+ {
+ if (vortex_Eqlzr_GetLeftGain(vortex, si, &gains[si]))
+ return 1;
+ if (vortex_Eqlzr_GetRightGain
+ (vortex, si, &gains[si + eq->this10]))
+ return 1;
+ si++;
+ }
+ while (eq->this10 > si) ;
+ *cnt = si * 2;
+ return 0;
+}
+#endif
+static int vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex_t * vortex)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ vortex_EqHw_SetLeftGainsTarget(vortex, eq->this130);
+ vortex_EqHw_SetRightGainsTarget(vortex, &(eq->this130[eq->this10]));
+
+ return 0;
+}
+
+static int
+vortex_Eqlzr_SetAllBands(vortex_t * vortex, u16 gains[], unsigned long count)
+{
+ eqlzr_t *eq = &(vortex->eq);
+ int i;
+
+ if (((eq->this10) * 2 != count) || (eq->this28 == 0))
+ return 1;
+
+ for (i = 0; i < count; i++) {
+ eq->this130[i] = gains[i];
+ }
+
+ if (eq->this54)
+ return 0;
+ return vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+}
+
+static void
+vortex_Eqlzr_SetA3dBypassGain(vortex_t * vortex, unsigned long a,
+ unsigned long b)
+{
+ eqlzr_t *eq = &(vortex->eq);
+ int eax, ebx;
+
+ eq->this58 = a;
+ eq->this5c = b;
+ if (eq->this54)
+ eax = eq->this0e;
+ else
+ eax = eq->this0a;
+ ebx = (eax * eq->this58) >> 0x10;
+ eax = (eax * eq->this5c) >> 0x10;
+ vortex_EqHw_SetA3DBypassGain(vortex, ebx, eax);
+}
+
+static void vortex_Eqlzr_ProgramA3dBypassGain(vortex_t * vortex)
+{
+ eqlzr_t *eq = &(vortex->eq);
+ int eax, ebx;
+
+ if (eq->this54)
+ eax = eq->this0e;
+ else
+ eax = eq->this0a;
+ ebx = (eax * eq->this58) >> 0x10;
+ eax = (eax * eq->this5c) >> 0x10;
+ vortex_EqHw_SetA3DBypassGain(vortex, ebx, eax);
+}
+
+static void vortex_Eqlzr_ShutDownA3d(vortex_t * vortex)
+{
+ if (vortex != NULL)
+ vortex_EqHw_ZeroA3DIO(vortex);
+}
+
+static void vortex_Eqlzr_SetBypass(vortex_t * vortex, long bp)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if ((eq->this28) && (bp == 0)) {
+ /* EQ enabled */
+ vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+ vortex_EqHw_SetBypassGain(vortex, eq->this08, eq->this08);
+ } else {
+ /* EQ disabled. */
+ vortex_EqHw_SetLeftGainsTarget(vortex, (u16 *) (eq->this14));
+ vortex_EqHw_SetRightGainsTarget(vortex, (u16 *) (eq->this14));
+ vortex_EqHw_SetBypassGain(vortex, eq->this0c, eq->this0c);
+ }
+ vortex_Eqlzr_ProgramA3dBypassGain(vortex);
+}
+
+static void vortex_Eqlzr_ReadAndSetActiveCoefSet(vortex_t * vortex)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ /* Set EQ BiQuad filter coeficients */
+ memcpy(&(eq->coefset), &asEqCoefsNormal, sizeof(auxxEqCoeffSet_t));
+ /* Set EQ Band gain levels and dump into hardware registers. */
+ vortex_Eqlzr_SetAllBands(vortex, eq_gains_normal, eq->this10 * 2);
+}
+
+static int vortex_Eqlzr_GetAllPeaks(vortex_t * vortex, u16 * peaks, int *count)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this10 == 0)
+ return 1;
+ *count = eq->this10 * 2;
+ vortex_EqHw_GetTenBandLevels(vortex, peaks);
+ return 0;
+}
+
+#if 0
+static auxxEqCoeffSet_t *vortex_Eqlzr_GetActiveCoefSet(vortex_t * vortex)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ return (&(eq->coefset));
+}
+#endif
+static void vortex_Eqlzr_init(vortex_t * vortex)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ /* Object constructor */
+ //eq->this04 = 0;
+ eq->this08 = 0; /* Bypass gain with EQ in use. */
+ eq->this0a = 0x5999;
+ eq->this0c = 0x5999; /* Bypass gain with EQ disabled. */
+ eq->this0e = 0x5999;
+
+ eq->this10 = 0xa; /* 10 eq frequency bands. */
+ eq->this04.this04 = eq->this10;
+ eq->this28 = 0x1; /* if 1 => Allow read access to this130 (gains) */
+ eq->this54 = 0x0; /* if 1 => Dont Allow access to hardware (gains) */
+ eq->this58 = 0xffff;
+ eq->this5c = 0xffff;
+
+ /* Set gains. */
+ memset(eq->this14, 0, 2 * 10);
+
+ /* Actual init. */
+ vortex_EqHw_ZeroState(vortex);
+ vortex_EqHw_SetSampleRate(vortex, 0x11);
+ vortex_Eqlzr_ReadAndSetActiveCoefSet(vortex);
+
+ vortex_EqHw_Program10Band(vortex, &(eq->coefset));
+ vortex_Eqlzr_SetBypass(vortex, eq->this54);
+ vortex_Eqlzr_SetA3dBypassGain(vortex, 0, 0);
+ vortex_EqHw_Enable(vortex);
+}
+
+static void vortex_Eqlzr_shutdown(vortex_t * vortex)
+{
+ vortex_Eqlzr_ShutDownA3d(vortex);
+ vortex_EqHw_ProgramPipe(vortex);
+ vortex_EqHw_Disable(vortex);
+}
+
+/* ALSA interface */
+
+/* Control interface */
+static int
+snd_vortex_eqtoggle_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int
+snd_vortex_eqtoggle_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ eqlzr_t *eq = &(vortex->eq);
+ //int i = kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = eq->this54 ? 0 : 1;
+
+ return 0;
+}
+
+static int
+snd_vortex_eqtoggle_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ eqlzr_t *eq = &(vortex->eq);
+ //int i = kcontrol->private_value;
+
+ eq->this54 = ucontrol->value.integer.value[0] ? 0 : 1;
+ vortex_Eqlzr_SetBypass(vortex, eq->this54);
+
+ return 1; /* Allways changes */
+}
+
+static snd_kcontrol_new_t vortex_eqtoggle_kcontrol __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EQ Enable",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 0,
+ .info = snd_vortex_eqtoggle_info,
+ .get = snd_vortex_eqtoggle_get,
+ .put = snd_vortex_eqtoggle_put
+};
+
+static int
+snd_vortex_eq_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0x0000;
+ uinfo->value.integer.max = 0x7fff;
+ return 0;
+}
+
+static int
+snd_vortex_eq_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int i = kcontrol->private_value;
+ u16 gainL, gainR;
+
+ vortex_Eqlzr_GetLeftGain(vortex, i, &gainL);
+ vortex_Eqlzr_GetRightGain(vortex, i, &gainR);
+ ucontrol->value.integer.value[0] = gainL;
+ ucontrol->value.integer.value[1] = gainR;
+ return 0;
+}
+
+static int
+snd_vortex_eq_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int changed = 0, i = kcontrol->private_value;
+ u16 gainL, gainR;
+
+ vortex_Eqlzr_GetLeftGain(vortex, i, &gainL);
+ vortex_Eqlzr_GetRightGain(vortex, i, &gainR);
+
+ if (gainL != ucontrol->value.integer.value[0]) {
+ vortex_Eqlzr_SetLeftGain(vortex, i,
+ ucontrol->value.integer.value[0]);
+ changed = 1;
+ }
+ if (gainR != ucontrol->value.integer.value[1]) {
+ vortex_Eqlzr_SetRightGain(vortex, i,
+ ucontrol->value.integer.value[1]);
+ changed = 1;
+ }
+ return changed;
+}
+
+static snd_kcontrol_new_t vortex_eq_kcontrol __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = " .",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 0,
+ .info = snd_vortex_eq_info,
+ .get = snd_vortex_eq_get,
+ .put = snd_vortex_eq_put
+};
+
+static int
+snd_vortex_peaks_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 20;
+ uinfo->value.integer.min = 0x0000;
+ uinfo->value.integer.max = 0x7fff;
+ return 0;
+}
+
+static int
+snd_vortex_peaks_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int i, count;
+ u16 peaks[20];
+
+ vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
+ if (count != 20) {
+ printk("vortex: peak count error 20 != %d \n", count);
+ return -1;
+ }
+ for (i = 0; i < 20; i++)
+ ucontrol->value.integer.value[i] = peaks[i];
+
+ return 0;
+}
+
+static snd_kcontrol_new_t vortex_levels_kcontrol __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EQ Peaks",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_vortex_peaks_info,
+ .get = snd_vortex_peaks_get,
+};
+
+/* EQ band gain labels. */
+static char *EqBandLabels[10] __devinitdata = {
+ "EQ0 31Hz\0",
+ "EQ1 63Hz\0",
+ "EQ2 125Hz\0",
+ "EQ3 250Hz\0",
+ "EQ4 500Hz\0",
+ "EQ5 1KHz\0",
+ "EQ6 2KHz\0",
+ "EQ7 4KHz\0",
+ "EQ8 8KHz\0",
+ "EQ9 16KHz\0",
+};
+
+/* ALSA driver entry points. Init and exit. */
+static int vortex_eq_init(vortex_t * vortex)
+{
+ snd_kcontrol_t *kcontrol;
+ int err, i;
+
+ vortex_Eqlzr_init(vortex);
+
+ if ((kcontrol =
+ snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex)) == NULL)
+ return -ENOMEM;
+ kcontrol->private_value = 0;
+ if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ return err;
+
+ /* EQ gain controls */
+ for (i = 0; i < 10; i++) {
+ if ((kcontrol =
+ snd_ctl_new1(&vortex_eq_kcontrol, vortex)) == NULL)
+ return -ENOMEM;
+ strcpy(kcontrol->id.name, EqBandLabels[i]);
+ kcontrol->private_value = i;
+ if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ return err;
+ //vortex->eqctrl[i] = kcontrol;
+ }
+ /* EQ band levels */
+ if ((kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ return err;
+
+ return 0;
+}
+
+static int vortex_eq_free(vortex_t * vortex)
+{
+ /*
+ //FIXME: segfault because vortex->eqctrl[i] == 4
+ int i;
+ for (i=0; i<10; i++) {
+ if (vortex->eqctrl[i])
+ snd_ctl_remove(vortex->card, vortex->eqctrl[i]);
+ }
+ */
+ vortex_Eqlzr_shutdown(vortex);
+ return 0;
+}
+
+/* End */
diff --git a/sound/pci/au88x0/au88x0_eq.h b/sound/pci/au88x0/au88x0_eq.h
new file mode 100644
index 00000000000..e49bc625c87
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eq.h
@@ -0,0 +1,46 @@
+#ifndef AU88X0_EQ_H
+#define AU88X0_EQ_H
+
+/***************************************************************************
+ * au88x0_eq.h
+ *
+ * Definitions and constant data for the Aureal Hardware EQ.
+ *
+ * Sun Jun 8 18:23:38 2003
+ * Author: Manuel Jander (mjander@users.sourceforge.net)
+ ****************************************************************************/
+
+typedef struct {
+ u16 LeftCoefs[50]; //0x4
+ u16 RightCoefs[50]; // 0x68
+ u16 LeftGains[20]; //0xd0
+ u16 RightGains[20]; //0xe4
+} auxxEqCoeffSet_t;
+
+typedef struct {
+ unsigned int *this00; /*CAsp4HwIO */
+ long this04; /* How many filters for each side (default = 10) */
+ long this08; /* inited to cero. Stereo flag? */
+} eqhw_t;
+
+typedef struct {
+ unsigned int *this00; /*CAsp4Core */
+ eqhw_t this04; /* CHwEq */
+ short this08; /* Bad codec flag ? SetBypassGain: bypass gain */
+ short this0a;
+ short this0c; /* SetBypassGain: bypass gain when this28 is not set. */
+ short this0e;
+
+ long this10; /* How many gains are used for each side (right or left). */
+ u16 this14[32]; /* SetLeftGainsTarget: Left (and right?) EQ gains */
+ long this24;
+ long this28; /* flag related to EQ enabled or not. Gang flag ? */
+ long this54; /* SetBypass */
+ long this58;
+ long this5c;
+ /*0x60 */ auxxEqCoeffSet_t coefset;
+ /* 50 u16 word each channel. */
+ u16 this130[20]; /* Left and Right gains */
+} eqlzr_t;
+
+#endif
diff --git a/sound/pci/au88x0/au88x0_eqdata.c b/sound/pci/au88x0/au88x0_eqdata.c
new file mode 100644
index 00000000000..abf8d6ac4c1
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eqdata.c
@@ -0,0 +1,112 @@
+/* Data structs */
+
+static u16 asEqCoefsZeros[50] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static u16 asEqCoefsPipes[64] = {
+ 0x0000, 0x0000,
+ 0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0666, 0x0000, 0x0000, 0x066a,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000
+};
+
+/* More coef sets can be found in the win2k "inf" file. */
+static auxxEqCoeffSet_t asEqCoefsNormal = {
+ .LeftCoefs = {
+ 0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
+ 0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
+ 0x7f3f, 0xc0bc, 0x00c2, 0x0000, 0xff3e,
+ 0x7e78, 0xc177, 0x011f, 0x0000, 0xfee1,
+ 0x7cd6, 0xc2e5, 0x025c, 0x0000, 0xfda4,
+ 0x7949, 0xc5aa, 0x0467, 0x0000, 0xfb99,
+ 0x7120, 0xcadf, 0x0864, 0x0000, 0xf79c,
+ 0x5d33, 0xd430, 0x0f7e, 0x0000, 0xf082,
+ 0x2beb, 0xe3ca, 0x1bd3, 0x0000, 0xe42d,
+ 0xd740, 0xf01d, 0x2ac5, 0x0000, 0xd53b},
+
+ .RightCoefs = {
+ 0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
+ 0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
+ 0x7f3f, 0xc0bc, 0x00c2, 0x0000, 0xff3e,
+ 0x7e78, 0xc177, 0x011f, 0x0000, 0xfee1,
+ 0x7cd6, 0xc2e5, 0x025c, 0x0000, 0xfda4,
+ 0x7949, 0xc5aa, 0x0467, 0x0000, 0xfb99,
+ 0x7120, 0xcadf, 0x0864, 0x0000, 0xf79c,
+ 0x5d33, 0xd430, 0x0f7e, 0x0000, 0xf082,
+ 0x2beb, 0xe3ca, 0x1bd3, 0x0000, 0xe42d,
+ 0xd740, 0xf01d, 0x2ac5, 0x0000, 0xd53b},
+
+ .LeftGains = {
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96},
+ .RightGains = {
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96}
+};
+
+static u16 eq_gains_normal[20] = {
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+ 0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96
+};
+
+/* _rodatab60 */
+static u16 eq_gains_zero[10] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+/* _rodatab7c: ProgramPipe */
+static u16 eq_gains_current[12] = {
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff,
+ 0x7fff, 0x7fff, 0x7fff
+};
+
+/* _rodatab78 */
+static u16 eq_states_zero[2] = { 0x0000, 0x0000 };
+
+static u16 asEqOutStateZeros[48] = {
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+/*_rodataba0:*/
+static long eq_levels[32] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
new file mode 100644
index 00000000000..a07d1deba32
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -0,0 +1,135 @@
+/*
+ * $Id: au88x0_game.c,v 1.9 2003/09/22 03:51:28 mjander Exp $
+ *
+ * Manuel Jander.
+ *
+ * Based on the work of:
+ * Vojtech Pavlik
+ * Raymond Ingles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ *
+ * Based 90% on Vojtech Pavlik pcigame driver.
+ * Merged and modified by Manuel Jander, for the OpenVortex
+ * driver. (email: mjander@embedded.cl).
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+#include <linux/gameport.h>
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+#define VORTEX_GAME_DWAIT 20 /* 20 ms */
+
+static unsigned char vortex_game_read(struct gameport *gameport)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+ return hwread(vortex->mmio, VORTEX_GAME_LEGACY);
+}
+
+static void vortex_game_trigger(struct gameport *gameport)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+ hwwrite(vortex->mmio, VORTEX_GAME_LEGACY, 0xff);
+}
+
+static int
+vortex_game_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+ int i;
+
+ *buttons = (~hwread(vortex->mmio, VORTEX_GAME_LEGACY) >> 4) & 0xf;
+
+ for (i = 0; i < 4; i++) {
+ axes[i] =
+ hwread(vortex->mmio, VORTEX_GAME_AXIS + (i * AXIS_SIZE));
+ if (axes[i] == AXIS_RANGE)
+ axes[i] = -1;
+ }
+ return 0;
+}
+
+static int vortex_game_open(struct gameport *gameport, int mode)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ hwwrite(vortex->mmio, VORTEX_CTRL2,
+ hwread(vortex->mmio,
+ VORTEX_CTRL2) | CTRL2_GAME_ADCMODE);
+ msleep(VORTEX_GAME_DWAIT);
+ return 0;
+ case GAMEPORT_MODE_RAW:
+ hwwrite(vortex->mmio, VORTEX_CTRL2,
+ hwread(vortex->mmio,
+ VORTEX_CTRL2) & ~CTRL2_GAME_ADCMODE);
+ return 0;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __devinit vortex_gameport_register(vortex_t * vortex)
+{
+ struct gameport *gp;
+
+ vortex->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "vortex: cannot allocate memory for gameport\n");
+ return -ENOMEM;
+ };
+
+ gameport_set_name(gp, "AU88x0 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(vortex->pci_dev));
+ gameport_set_dev_parent(gp, &vortex->pci_dev->dev);
+
+ gp->read = vortex_game_read;
+ gp->trigger = vortex_game_trigger;
+ gp->cooked_read = vortex_game_cooked_read;
+ gp->open = vortex_game_open;
+
+ gameport_set_port_data(gp, vortex);
+ gp->fuzz = 64;
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static void vortex_gameport_unregister(vortex_t * vortex)
+{
+ if (vortex->gameport) {
+ gameport_unregister_port(vortex->gameport);
+ vortex->gameport = NULL;
+ }
+}
+
+#else
+static inline int vortex_gameport_register(vortex_t * vortex) { return -ENOSYS; }
+static inline void vortex_gameport_unregister(vortex_t * vortex) { }
+#endif
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
new file mode 100644
index 00000000000..86e27d695c3
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -0,0 +1,33 @@
+/*
+ * Vortex Mixer support.
+ *
+ * There is much more than just the AC97 mixer...
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+
+static int __devinit snd_vortex_mixer(vortex_t * vortex)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = vortex_codec_write,
+ .read = vortex_codec_read,
+ };
+
+ if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+ memset(&ac97, 0, sizeof(ac97));
+ // Intialize AC97 codec stuff.
+ ac97.private_data = vortex;
+ ac97.scaps = AC97_SCAP_NO_SPDIF;
+ err = snd_ac97_mixer(pbus, &ac97, &vortex->codec);
+ vortex->isquad = ((vortex->codec == NULL) ? 0 : (vortex->codec->ext_id&0x80));
+ return err;
+}
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
new file mode 100644
index 00000000000..c0c23466eb0
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Routines for control of MPU-401 in UART mode
+ *
+ * Modified for the Aureal Vortex based Soundcards
+ * by Manuel Jander (mjande@embedded.cl).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/mpu401.h>
+#include "au88x0.h"
+
+/* Check for mpu401 mmio support. */
+/* MPU401 legacy support is only provided as a emergency fallback *
+ * for older versions of ALSA. Its usage is strongly discouraged. */
+#ifndef MPU401_HW_AUREAL
+#define VORTEX_MPU401_LEGACY
+#endif
+
+/* Vortex MPU401 defines. */
+#define MIDI_CLOCK_DIV 0x61
+/* Standart MPU401 defines. */
+#define MPU401_RESET 0xff
+#define MPU401_ENTER_UART 0x3f
+#define MPU401_ACK 0xfe
+
+static int __devinit snd_vortex_midi(vortex_t * vortex)
+{
+ snd_rawmidi_t *rmidi;
+ int temp, mode;
+ mpu401_t *mpu;
+ int port;
+
+#ifdef VORTEX_MPU401_LEGACY
+ /* EnableHardCodedMPU401Port() */
+ /* Enable Legacy MIDI Interface port. */
+ port = (0x03 << 5); /* FIXME: static address. 0x330 */
+ temp =
+ (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) |
+ CTRL_MIDI_EN | port;
+ hwwrite(vortex->mmio, VORTEX_CTRL, temp);
+#else
+ /* Disable Legacy MIDI Interface port. */
+ temp =
+ (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) &
+ ~CTRL_MIDI_EN;
+ hwwrite(vortex->mmio, VORTEX_CTRL, temp);
+#endif
+ /* Mpu401UartInit() */
+ mode = 1;
+ temp = hwread(vortex->mmio, VORTEX_CTRL2) & 0xffff00cf;
+ temp |= (MIDI_CLOCK_DIV << 8) | ((mode >> 24) & 0xff) << 4;
+ hwwrite(vortex->mmio, VORTEX_CTRL2, temp);
+ hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_RESET);
+ /* Set some kind of mode */
+ if (mode)
+ hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_ENTER_UART);
+
+ /* Check if anything is OK. */
+ temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
+ if (temp != MPU401_ACK /*0xfe */ ) {
+ printk(KERN_ERR "midi port doesn't acknowledge!\n");
+ return -ENODEV;
+ }
+ /* Enable MPU401 interrupts. */
+ hwwrite(vortex->mmio, VORTEX_IRQ_CTRL,
+ hwread(vortex->mmio, VORTEX_IRQ_CTRL) | IRQ_MIDI);
+
+ /* Create MPU401 instance. */
+#ifdef VORTEX_MPU401_LEGACY
+ if ((temp =
+ snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
+ 0, 0, 0, &rmidi)) != 0) {
+ hwwrite(vortex->mmio, VORTEX_CTRL,
+ (hwread(vortex->mmio, VORTEX_CTRL) &
+ ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
+ return temp;
+ }
+#else
+ port = (unsigned long)(vortex->mmio + (VORTEX_MIDI_DATA >> 2));
+ if ((temp =
+ snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
+ 1, 0, 0, &rmidi)) != 0) {
+ hwwrite(vortex->mmio, VORTEX_CTRL,
+ (hwread(vortex->mmio, VORTEX_CTRL) &
+ ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
+ return temp;
+ }
+ mpu = rmidi->private_data;
+ mpu->cport = (unsigned long)(vortex->mmio + (VORTEX_MIDI_CMD >> 2));
+#endif
+ vortex->rmidi = rmidi;
+ return 0;
+}
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
new file mode 100644
index 00000000000..04dcefd8b8f
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -0,0 +1,548 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Vortex PCM ALSA driver.
+ *
+ * Supports ADB and WT DMA. Unfortunately, WT channels do not run yet.
+ * It remains stuck,and DMA transfers do not happen.
+ */
+#include <sound/asoundef.h>
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "au88x0.h"
+
+#define VORTEX_PCM_TYPE(x) (x->name[40])
+
+/* hardware definition */
+static snd_pcm_hardware_t snd_vortex_playback_hw_adb = {
+ .info =
+ (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats =
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5000,
+ .rate_max = 48000,
+ .channels_min = 1,
+#ifdef CHIP_AU8830
+ .channels_max = 4,
+#else
+ .channels_max = 2,
+#endif
+ .buffer_bytes_max = 0x10000,
+ .period_bytes_min = 0x1,
+ .period_bytes_max = 0x1000,
+ .periods_min = 2,
+ .periods_max = 32,
+};
+
+#ifndef CHIP_AU8820
+static snd_pcm_hardware_t snd_vortex_playback_hw_a3d = {
+ .info =
+ (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats =
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 0x10000,
+ .period_bytes_min = 0x100,
+ .period_bytes_max = 0x1000,
+ .periods_min = 2,
+ .periods_max = 64,
+};
+#endif
+static snd_pcm_hardware_t snd_vortex_playback_hw_spdif = {
+ .info =
+ (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats =
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | SNDRV_PCM_FMTBIT_MU_LAW |
+ SNDRV_PCM_FMTBIT_A_LAW,
+ .rates =
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 0x10000,
+ .period_bytes_min = 0x100,
+ .period_bytes_max = 0x1000,
+ .periods_min = 2,
+ .periods_max = 64,
+};
+
+#ifndef CHIP_AU8810
+static snd_pcm_hardware_t snd_vortex_playback_hw_wt = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS, // SNDRV_PCM_RATE_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 0x10000,
+ .period_bytes_min = 0x0400,
+ .period_bytes_max = 0x1000,
+ .periods_min = 2,
+ .periods_max = 64,
+};
+#endif
+/* open callback */
+static int snd_vortex_pcm_open(snd_pcm_substream_t * substream)
+{
+ vortex_t *vortex = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ /* Force equal size periods */
+ if ((err =
+ snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ /* Avoid PAGE_SIZE boundary to fall inside of a period. */
+ if ((err =
+ snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
+ return err;
+
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+#ifndef CHIP_AU8820
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_A3D) {
+ runtime->hw = snd_vortex_playback_hw_a3d;
+ }
+#endif
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_SPDIF) {
+ runtime->hw = snd_vortex_playback_hw_spdif;
+ switch (vortex->spdif_sr) {
+ case 32000:
+ runtime->hw.rates = SNDRV_PCM_RATE_32000;
+ break;
+ case 44100:
+ runtime->hw.rates = SNDRV_PCM_RATE_44100;
+ break;
+ case 48000:
+ runtime->hw.rates = SNDRV_PCM_RATE_48000;
+ break;
+ }
+ }
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB
+ || VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_I2S)
+ runtime->hw = snd_vortex_playback_hw_adb;
+ substream->runtime->private_data = NULL;
+ }
+#ifndef CHIP_AU8810
+ else {
+ runtime->hw = snd_vortex_playback_hw_wt;
+ substream->runtime->private_data = NULL;
+ }
+#endif
+ return 0;
+}
+
+/* close callback */
+static int snd_vortex_pcm_close(snd_pcm_substream_t * substream)
+{
+ //vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) substream->runtime->private_data;
+
+ // the hardware-specific codes will be here
+ if (stream != NULL) {
+ stream->substream = NULL;
+ stream->nr_ch = 0;
+ }
+ substream->runtime->private_data = NULL;
+ return 0;
+}
+
+/* hw_params callback */
+static int
+snd_vortex_pcm_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) (substream->runtime->private_data);
+ snd_pcm_sgbuf_t *sgbuf;
+ int err;
+
+ // Alloc buffer memory.
+ err =
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0) {
+ printk(KERN_ERR "Vortex: pcm page alloc failed!\n");
+ return err;
+ }
+ //sgbuf = (snd_pcm_sgbuf_t *) substream->runtime->dma_private;
+ sgbuf = snd_pcm_substream_sgbuf(substream);
+ /*
+ printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
+ params_period_bytes(hw_params), params_channels(hw_params));
+ */
+ spin_lock_irq(&chip->lock);
+ // Make audio routes and config buffer DMA.
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ int dma, type = VORTEX_PCM_TYPE(substream->pcm);
+ /* Dealloc any routes. */
+ if (stream != NULL)
+ vortex_adb_allocroute(chip, stream->dma,
+ stream->nr_ch, stream->dir,
+ stream->type);
+ /* Alloc routes. */
+ dma =
+ vortex_adb_allocroute(chip, -1,
+ params_channels(hw_params),
+ substream->stream, type);
+ if (dma < 0)
+ return dma;
+ stream = substream->runtime->private_data = &chip->dma_adb[dma];
+ stream->substream = substream;
+ /* Setup Buffers. */
+ vortex_adbdma_setbuffers(chip, dma, sgbuf,
+ params_period_bytes(hw_params),
+ params_periods(hw_params));
+ }
+#ifndef CHIP_AU8810
+ else {
+ /* if (stream != NULL)
+ vortex_wt_allocroute(chip, substream->number, 0); */
+ vortex_wt_allocroute(chip, substream->number,
+ params_channels(hw_params));
+ stream = substream->runtime->private_data =
+ &chip->dma_wt[substream->number];
+ stream->dma = substream->number;
+ stream->substream = substream;
+ vortex_wtdma_setbuffers(chip, substream->number, sgbuf,
+ params_period_bytes(hw_params),
+ params_periods(hw_params));
+ }
+#endif
+ spin_unlock_irq(&chip->lock);
+ return 0;
+}
+
+/* hw_free callback */
+static int snd_vortex_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) (substream->runtime->private_data);
+
+ spin_lock_irq(&chip->lock);
+ // Delete audio routes.
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ if (stream != NULL)
+ vortex_adb_allocroute(chip, stream->dma,
+ stream->nr_ch, stream->dir,
+ stream->type);
+ }
+#ifndef CHIP_AU8810
+ else {
+ if (stream != NULL)
+ vortex_wt_allocroute(chip, stream->dma, 0);
+ }
+#endif
+ substream->runtime->private_data = NULL;
+ spin_unlock_irq(&chip->lock);
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_vortex_pcm_prepare(snd_pcm_substream_t * substream)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ stream_t *stream = (stream_t *) substream->runtime->private_data;
+ int dma = stream->dma, fmt, dir;
+
+ // set up the hardware with the current configuration.
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = 1;
+ else
+ dir = 0;
+ fmt = vortex_alsafmt_aspfmt(runtime->format);
+ spin_lock_irq(&chip->lock);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ vortex_adbdma_setmode(chip, dma, 1, dir, fmt, 0 /*? */ ,
+ 0);
+ vortex_adbdma_setstartbuffer(chip, dma, 0);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF)
+ vortex_adb_setsrc(chip, dma, runtime->rate, dir);
+ }
+#ifndef CHIP_AU8810
+ else {
+ vortex_wtdma_setmode(chip, dma, 1, fmt, 0, 0);
+ // FIXME: Set rate (i guess using vortex_wt_writereg() somehow).
+ vortex_wtdma_setstartbuffer(chip, dma, 0);
+ }
+#endif
+ spin_unlock_irq(&chip->lock);
+ return 0;
+}
+
+/* trigger callback */
+static int snd_vortex_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) substream->runtime->private_data;
+ int dma = stream->dma;
+
+ spin_lock(&chip->lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ // do something to start the PCM engine
+ //printk(KERN_INFO "vortex: start %d\n", dma);
+ stream->fifo_enabled = 1;
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ vortex_adbdma_resetup(chip, dma);
+ vortex_adbdma_startfifo(chip, dma);
+ }
+#ifndef CHIP_AU8810
+ else {
+ printk(KERN_INFO "vortex: wt start %d\n", dma);
+ vortex_wtdma_startfifo(chip, dma);
+ }
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ // do something to stop the PCM engine
+ //printk(KERN_INFO "vortex: stop %d\n", dma);
+ stream->fifo_enabled = 0;
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+ vortex_adbdma_pausefifo(chip, dma);
+ //vortex_adbdma_stopfifo(chip, dma);
+#ifndef CHIP_AU8810
+ else {
+ printk(KERN_INFO "vortex: wt stop %d\n", dma);
+ vortex_wtdma_stopfifo(chip, dma);
+ }
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ //printk(KERN_INFO "vortex: pause %d\n", dma);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+ vortex_adbdma_pausefifo(chip, dma);
+#ifndef CHIP_AU8810
+ else
+ vortex_wtdma_pausefifo(chip, dma);
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ //printk(KERN_INFO "vortex: resume %d\n", dma);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+ vortex_adbdma_resumefifo(chip, dma);
+#ifndef CHIP_AU8810
+ else
+ vortex_wtdma_resumefifo(chip, dma);
+#endif
+ break;
+ default:
+ spin_unlock(&chip->lock);
+ return -EINVAL;
+ }
+ spin_unlock(&chip->lock);
+ return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t snd_vortex_pcm_pointer(snd_pcm_substream_t * substream)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) substream->runtime->private_data;
+ int dma = stream->dma;
+ snd_pcm_uframes_t current_ptr = 0;
+
+ spin_lock(&chip->lock);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+ current_ptr = vortex_adbdma_getlinearpos(chip, dma);
+#ifndef CHIP_AU8810
+ else
+ current_ptr = vortex_wtdma_getlinearpos(chip, dma);
+#endif
+ //printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
+ spin_unlock(&chip->lock);
+ return (bytes_to_frames(substream->runtime, current_ptr));
+}
+
+/* Page callback. */
+/*
+static struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) {
+
+
+}
+*/
+/* operators */
+static snd_pcm_ops_t snd_vortex_playback_ops = {
+ .open = snd_vortex_pcm_open,
+ .close = snd_vortex_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_vortex_pcm_hw_params,
+ .hw_free = snd_vortex_pcm_hw_free,
+ .prepare = snd_vortex_pcm_prepare,
+ .trigger = snd_vortex_pcm_trigger,
+ .pointer = snd_vortex_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+/*
+* definitions of capture are omitted here...
+*/
+
+static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
+ "AU88x0 ADB",
+ "AU88x0 SPDIF",
+ "AU88x0 A3D",
+ "AU88x0 WT",
+ "AU88x0 I2S",
+};
+static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
+ "adb",
+ "spdif",
+ "a3d",
+ "wt",
+ "i2s",
+};
+
+/* SPDIF kcontrol */
+
+static int snd_vortex_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_vortex_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = 0xff;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0xff;
+ ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+ return 0;
+}
+
+static int snd_vortex_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.iec958.status[0] = 0x00;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL|IEC958_AES1_CON_DIGDIGCONV_ID;
+ ucontrol->value.iec958.status[2] = 0x00;
+ switch (vortex->spdif_sr) {
+ case 32000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_32000; break;
+ case 44100: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_44100; break;
+ case 48000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000; break;
+ }
+ return 0;
+}
+
+static int snd_vortex_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int spdif_sr = 48000;
+ switch (ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) {
+ case IEC958_AES3_CON_FS_32000: spdif_sr = 32000; break;
+ case IEC958_AES3_CON_FS_44100: spdif_sr = 44100; break;
+ case IEC958_AES3_CON_FS_48000: spdif_sr = 48000; break;
+ }
+ if (spdif_sr == vortex->spdif_sr)
+ return 0;
+ vortex->spdif_sr = spdif_sr;
+ vortex_spdif_init(vortex, vortex->spdif_sr, 1);
+ return 1;
+}
+
+/* spdif controls */
+static snd_kcontrol_new_t snd_vortex_mixer_spdif[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_vortex_spdif_info,
+ .get = snd_vortex_spdif_get,
+ .put = snd_vortex_spdif_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_vortex_spdif_info,
+ .get = snd_vortex_spdif_mask_get
+ },
+};
+
+/* create a pcm device */
+static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr)
+{
+ snd_pcm_t *pcm;
+ snd_kcontrol_t *kctl;
+ int i;
+ int err, nr_capt;
+
+ if ((chip == 0) || (idx < 0) || (idx > VORTEX_PCM_LAST))
+ return -ENODEV;
+
+ /* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the
+ * same dma engine. WT uses it own separate dma engine whcih cant capture. */
+ if (idx == VORTEX_PCM_ADB)
+ nr_capt = nr;
+ else
+ nr_capt = 0;
+ if ((err =
+ snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
+ nr_capt, &pcm)) < 0)
+ return err;
+ strcpy(pcm->name, vortex_pcm_name[idx]);
+ chip->pcm[idx] = pcm;
+ // This is an evil hack, but it saves a lot of duplicated code.
+ VORTEX_PCM_TYPE(pcm) = idx;
+ pcm->private_data = chip;
+ /* set operators */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_vortex_playback_ops);
+ if (idx == VORTEX_PCM_ADB)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_vortex_playback_ops);
+
+ /* pre-allocation of Scatter-Gather buffers */
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci_dev),
+ 0x10000, 0x10000);
+
+ if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
+ for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
+ kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);
+ if (!kctl)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(chip->card, kctl)) < 0)
+ return err;
+ }
+ }
+ return 0;
+}
diff --git a/sound/pci/au88x0/au88x0_sb.h b/sound/pci/au88x0/au88x0_sb.h
new file mode 100644
index 00000000000..5a4d8fc2bbf
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_sb.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * au88x0_sb.h
+ *
+ * Wed Oct 29 22:10:42 2003
+ *
+ ****************************************************************************/
+
+#ifdef CHIP_AU8820
+/* AU8820 starting @ 64KiB offset */
+#define SBEMU_BASE 0x10000
+#else
+/* AU8810? and AU8830 starting @ 164KiB offset */
+#define SBEMU_BASE 0x29000
+#endif
+
+#define FM_A_STATUS (SBEMU_BASE + 0x00) /* read */
+#define FM_A_ADDRESS (SBEMU_BASE + 0x00) /* write */
+#define FM_A_DATA (SBEMU_BASE + 0x04)
+#define FM_B_STATUS (SBEMU_BASE + 0x08)
+#define FM_B_ADDRESS (SBEMU_BASE + 0x08)
+#define FM_B_DATA (SBEMU_BASE + 0x0C)
+#define SB_MIXER_ADDR (SBEMU_BASE + 0x10)
+#define SB_MIXER_DATA (SBEMU_BASE + 0x14)
+#define SB_RESET (SBEMU_BASE + 0x18)
+#define SB_RESET_ALIAS (SBEMU_BASE + 0x1C)
+#define FM_STATUS2 (SBEMU_BASE + 0x20)
+#define FM_ADDR2 (SBEMU_BASE + 0x20)
+#define FM_DATA2 (SBEMU_BASE + 0x24)
+#define SB_DSP_READ (SBEMU_BASE + 0x28)
+#define SB_DSP_WRITE (SBEMU_BASE + 0x30)
+#define SB_DSP_WRITE_STATUS (SBEMU_BASE + 0x30) /* bit 7 */
+#define SB_DSP_READ_STATUS (SBEMU_BASE + 0x38) /* bit 7 */
+#define SB_LACR (SBEMU_BASE + 0x40) /* ? */
+#define SB_LADCR (SBEMU_BASE + 0x44) /* ? */
+#define SB_LAMR (SBEMU_BASE + 0x48) /* ? */
+#define SB_LARR (SBEMU_BASE + 0x4C) /* ? */
+#define SB_VERSION (SBEMU_BASE + 0x50)
+#define SB_CTRLSTAT (SBEMU_BASE + 0x54)
+#define SB_TIMERSTAT (SBEMU_BASE + 0x58)
+#define FM_RAM (SBEMU_BASE + 0x100) /* 0x40 ULONG */
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
new file mode 100644
index 00000000000..400417d3460
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_synth.c
@@ -0,0 +1,395 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Someday its supposed to make use of the WT DMA engine
+ * for a Wavetable synthesizer.
+ */
+
+#include "au88x0.h"
+#include "au88x0_wt.h"
+
+static void vortex_fifo_setwtvalid(vortex_t * vortex, int fifo, int en);
+static void vortex_connection_adb_mixin(vortex_t * vortex, int en,
+ unsigned char channel,
+ unsigned char source,
+ unsigned char mixin);
+static void vortex_connection_mixin_mix(vortex_t * vortex, int en,
+ unsigned char mixin,
+ unsigned char mix, int a);
+static void vortex_fifo_wtinitialize(vortex_t * vortex, int fifo, int j);
+static int vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
+ unsigned long val);
+
+/* WT */
+
+/* Put 2 WT channels together for one stereo interlaced channel. */
+static void vortex_wt_setstereo(vortex_t * vortex, u32 wt, u32 stereo)
+{
+ int temp;
+
+ //temp = hwread(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2));
+ temp = hwread(vortex->mmio, WT_STEREO(wt));
+ temp = (temp & 0xfe) | (stereo & 1);
+ //hwwrite(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2), temp);
+ hwwrite(vortex->mmio, WT_STEREO(wt), temp);
+}
+
+/* Join to mixdown route. */
+static void vortex_wt_setdsout(vortex_t * vortex, u32 wt, int en)
+{
+ int temp;
+
+ /* There is one DSREG register for each bank (32 voices each). */
+ temp = hwread(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0));
+ if (en)
+ temp |= (1 << (wt & 0x1f));
+ else
+ temp &= (1 << ~(wt & 0x1f));
+ hwwrite(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0), temp);
+}
+
+/* Setup WT route. */
+static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
+{
+ wt_voice_t *voice = &(vortex->wt_voice[wt]);
+ int temp;
+
+ //FIXME: WT audio routing.
+ if (nr_ch) {
+ vortex_fifo_wtinitialize(vortex, wt, 1);
+ vortex_fifo_setwtvalid(vortex, wt, 1);
+ vortex_wt_setstereo(vortex, wt, nr_ch - 1);
+ } else
+ vortex_fifo_setwtvalid(vortex, wt, 0);
+
+ /* Set mixdown mode. */
+ vortex_wt_setdsout(vortex, wt, 1);
+ /* Set other parameter registers. */
+ hwwrite(vortex->mmio, WT_SRAMP(0), 0x880000);
+ //hwwrite(vortex->mmio, WT_GMODE(0), 0xffffffff);
+#ifdef CHIP_AU8830
+ hwwrite(vortex->mmio, WT_SRAMP(1), 0x880000);
+ //hwwrite(vortex->mmio, WT_GMODE(1), 0xffffffff);
+#endif
+ hwwrite(vortex->mmio, WT_PARM(wt, 0), 0);
+ hwwrite(vortex->mmio, WT_PARM(wt, 1), 0);
+ hwwrite(vortex->mmio, WT_PARM(wt, 2), 0);
+
+ temp = hwread(vortex->mmio, WT_PARM(wt, 3));
+ printk("vortex: WT PARM3: %x\n", temp);
+ //hwwrite(vortex->mmio, WT_PARM(wt, 3), temp);
+
+ hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 1), 0);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0);
+
+ printk("vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+
+ hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff);
+ hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810);
+
+ voice->parm0 = voice->parm1 = 0xcfb23e2f;
+ hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
+ hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
+ printk("vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+ return 0;
+}
+
+
+static void vortex_wt_connect(vortex_t * vortex, int en)
+{
+ int i, ii, mix;
+
+#define NR_WTROUTES 6
+#ifdef CHIP_AU8830
+#define NR_WTBLOCKS 2
+#else
+#define NR_WTBLOCKS 1
+#endif
+
+ for (i = 0; i < NR_WTBLOCKS; i++) {
+ for (ii = 0; ii < NR_WTROUTES; ii++) {
+ mix =
+ vortex_adb_checkinout(vortex,
+ vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXIN);
+ vortex->mixwt[(i * NR_WTROUTES) + ii] = mix;
+
+ vortex_route(vortex, en, 0x11,
+ ADB_WTOUT(i, ii + 0x20), ADB_MIXIN(mix));
+
+ vortex_connection_mixin_mix(vortex, en, mix,
+ vortex->mixplayb[ii % 2], 0);
+ if (VORTEX_IS_QUAD(vortex))
+ vortex_connection_mixin_mix(vortex, en,
+ mix,
+ vortex->mixplayb[2 +
+ (ii % 2)], 0);
+ }
+ }
+ for (i = 0; i < NR_WT; i++) {
+ hwwrite(vortex->mmio, WT_RUN(i), 1);
+ }
+}
+
+/* Read WT Register */
+#if 0
+static int vortex_wt_GetReg(vortex_t * vortex, char reg, int wt)
+{
+ //int eax, esi;
+
+ if (reg == 4) {
+ return hwread(vortex->mmio, WT_PARM(wt, 3));
+ }
+ if (reg == 7) {
+ return hwread(vortex->mmio, WT_GMODE(wt));
+ }
+
+ return 0;
+}
+
+/* WT hardware abstraction layer generic register interface. */
+static int
+vortex_wt_SetReg2(vortex_t * vortex, unsigned char reg, int wt,
+ unsigned short val)
+{
+ /*
+ int eax, edx;
+
+ if (wt >= NR_WT) // 0x40 -> NR_WT
+ return 0;
+
+ if ((reg - 0x20) > 0) {
+ if ((reg - 0x21) != 0)
+ return 0;
+ eax = ((((b & 0xff) << 0xb) + (edx & 0xff)) << 4) + 0x208; // param 2
+ } else {
+ eax = ((((b & 0xff) << 0xb) + (edx & 0xff)) << 4) + 0x20a; // param 3
+ }
+ hwwrite(vortex->mmio, eax, c);
+ */
+ return 1;
+}
+
+/*public: static void __thiscall CWTHal::SetReg(unsigned char,int,unsigned long) */
+#endif
+static int
+vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
+ unsigned long val)
+{
+ int ecx;
+
+ if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) {
+ if (wt >= (NR_WT / NR_WT_PB)) {
+ printk
+ ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
+ reg, wt);
+ return 0;
+ }
+ } else {
+ if (wt >= NR_WT) {
+ printk("vortex: WT SetReg: voice out of range\n");
+ return 0;
+ }
+ }
+ if (reg > 0xc)
+ return 0;
+
+ switch (reg) {
+ /* Voice specific parameters */
+ case 0: /* running */
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_RUN(wt), (int)val);
+ hwwrite(vortex->mmio, WT_RUN(wt), val);
+ return 0xc;
+ break;
+ case 1: /* param 0 */
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,0), (int)val);
+ hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
+ return 0xc;
+ break;
+ case 2: /* param 1 */
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,1), (int)val);
+ hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
+ return 0xc;
+ break;
+ case 3: /* param 2 */
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,2), (int)val);
+ hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
+ return 0xc;
+ break;
+ case 4: /* param 3 */
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,3), (int)val);
+ hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
+ return 0xc;
+ break;
+ case 6: /* mute */
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_MUTE(wt), (int)val);
+ hwwrite(vortex->mmio, WT_MUTE(wt), val);
+ return 0xc;
+ break;
+ case 0xb:
+ { /* delay */
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_DELAY(wt,0), (int)val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
+ return 0xc;
+ }
+ break;
+ /* Global WT block parameters */
+ case 5: /* sramp */
+ ecx = WT_SRAMP(wt);
+ break;
+ case 8: /* aramp */
+ ecx = WT_ARAMP(wt);
+ break;
+ case 9: /* mramp */
+ ecx = WT_MRAMP(wt);
+ break;
+ case 0xa: /* ctrl */
+ ecx = WT_CTRL(wt);
+ break;
+ case 0xc: /* ds_reg */
+ ecx = WT_DSREG(wt);
+ break;
+ default:
+ return 0;
+ break;
+ }
+ //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
+ hwwrite(vortex->mmio, ecx, val);
+ return 1;
+}
+
+static void vortex_wt_init(vortex_t * vortex)
+{
+ int var4, var8, varc, var10 = 0, edi;
+
+ var10 &= 0xFFFFFFE3;
+ var10 |= 0x22;
+ var10 &= 0xFFFFFEBF;
+ var10 |= 0x80;
+ var10 |= 0x200;
+ var10 &= 0xfffffffe;
+ var10 &= 0xfffffbff;
+ var10 |= 0x1800;
+ // var10 = 0x1AA2
+ var4 = 0x10000000;
+ varc = 0x00830000;
+ var8 = 0x00830000;
+
+ /* Init Bank registers. */
+ for (edi = 0; edi < (NR_WT / NR_WT_PB); edi++) {
+ vortex_wt_SetReg(vortex, 0xc, edi, 0); /* ds_reg */
+ vortex_wt_SetReg(vortex, 0xa, edi, var10); /* ctrl */
+ vortex_wt_SetReg(vortex, 0x9, edi, var4); /* mramp */
+ vortex_wt_SetReg(vortex, 0x8, edi, varc); /* aramp */
+ vortex_wt_SetReg(vortex, 0x5, edi, var8); /* sramp */
+ }
+ /* Init Voice registers. */
+ for (edi = 0; edi < NR_WT; edi++) {
+ vortex_wt_SetReg(vortex, 0x4, edi, 0); /* param 3 0x20c */
+ vortex_wt_SetReg(vortex, 0x3, edi, 0); /* param 2 0x208 */
+ vortex_wt_SetReg(vortex, 0x2, edi, 0); /* param 1 0x204 */
+ vortex_wt_SetReg(vortex, 0x1, edi, 0); /* param 0 0x200 */
+ vortex_wt_SetReg(vortex, 0xb, edi, 0); /* delay 0x400 - 0x40c */
+ }
+ var10 |= 1;
+ for (edi = 0; edi < (NR_WT / NR_WT_PB); edi++)
+ vortex_wt_SetReg(vortex, 0xa, edi, var10); /* ctrl */
+}
+
+/* Extract of CAdbTopology::SetVolume(struct _ASPVOLUME *) */
+#if 0
+static void vortex_wt_SetVolume(vortex_t * vortex, int wt, int vol[])
+{
+ wt_voice_t *voice = &(vortex->wt_voice[wt]);
+ int ecx = vol[1], eax = vol[0];
+
+ /* This is pure guess */
+ voice->parm0 &= 0xff00ffff;
+ voice->parm0 |= (vol[0] & 0xff) << 0x10;
+ voice->parm1 &= 0xff00ffff;
+ voice->parm1 |= (vol[1] & 0xff) << 0x10;
+
+ /* This is real */
+ hwwrite(vortex, WT_PARM(wt, 0), voice->parm0);
+ hwwrite(vortex, WT_PARM(wt, 1), voice->parm0);
+
+ if (voice->this_1D0 & 4) {
+ eax >>= 8;
+ ecx = eax;
+ if (ecx < 0x80)
+ ecx = 0x7f;
+ voice->parm3 &= 0xFFFFC07F;
+ voice->parm3 |= (ecx & 0x7f) << 7;
+ voice->parm3 &= 0xFFFFFF80;
+ voice->parm3 |= (eax & 0x7f);
+ } else {
+ voice->parm3 &= 0xFFE03FFF;
+ voice->parm3 |= (eax & 0xFE00) << 5;
+ }
+
+ hwwrite(vortex, WT_PARM(wt, 3), voice->parm3);
+}
+
+/* Extract of CAdbTopology::SetFrequency(unsigned long arg_0) */
+static void vortex_wt_SetFrequency(vortex_t * vortex, int wt, unsigned int sr)
+{
+ wt_voice_t *voice = &(vortex->wt_voice[wt]);
+ long int eax, edx;
+
+ //FIXME: 64 bit operation.
+ eax = ((sr << 0xf) * 0x57619F1) & 0xffffffff;
+ edx = (((sr << 0xf) * 0x57619F1)) >> 0x20;
+
+ edx >>= 0xa;
+ edx <<= 1;
+ if (edx) {
+ if (edx & 0x0FFF80000)
+ eax = 0x7fff;
+ else {
+ edx <<= 0xd;
+ eax = 7;
+ while ((edx & 0x80000000) == 0) {
+ edx <<= 1;
+ eax--;
+ if (eax == 0) ;
+ break;
+ }
+ if (eax)
+ edx <<= 1;
+ eax <<= 0xc;
+ edx >>= 0x14;
+ eax |= edx;
+ }
+ } else
+ eax = 0;
+ voice->parm0 &= 0xffff0001;
+ voice->parm0 |= (eax & 0x7fff) << 1;
+ voice->parm1 = voice->parm0 | 1;
+ // Wt: this_1D4
+ //AuWt::WriteReg((ulong)(this_1DC<<4)+0x200, (ulong)this_1E4);
+ //AuWt::WriteReg((ulong)(this_1DC<<4)+0x204, (ulong)this_1E8);
+ hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
+ hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
+}
+#endif
+
+/* End of File */
diff --git a/sound/pci/au88x0/au88x0_wt.h b/sound/pci/au88x0/au88x0_wt.h
new file mode 100644
index 00000000000..d536c88b43b
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_wt.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * WT register offsets.
+ *
+ * Wed Oct 22 13:50:20 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.org
+ ****************************************************************************/
+#ifndef _AU88X0_WT_H
+#define _AU88X0_WT_H
+
+/* WT channels are grouped in banks. Each bank has 0x20 channels. */
+/* Bank register address boundary is 0x8000 */
+
+#define NR_WT_PB 0x20
+
+/* WT bank base register (as dword address). */
+#define WT_BAR(x) (((x)&0xffe0)<<0x8)
+#define WT_BANK(x) (x>>5)
+/* WT Bank registers */
+#define WT_CTRL(bank) (((((bank)&1)<<0xd) + 0x00)<<2) /* 0x0000 */
+#define WT_SRAMP(bank) (((((bank)&1)<<0xd) + 0x01)<<2) /* 0x0004 */
+#define WT_DSREG(bank) (((((bank)&1)<<0xd) + 0x02)<<2) /* 0x0008 */
+#define WT_MRAMP(bank) (((((bank)&1)<<0xd) + 0x03)<<2) /* 0x000c */
+#define WT_GMODE(bank) (((((bank)&1)<<0xd) + 0x04)<<2) /* 0x0010 */
+#define WT_ARAMP(bank) (((((bank)&1)<<0xd) + 0x05)<<2) /* 0x0014 */
+/* WT Voice registers */
+#define WT_STEREO(voice) ((WT_BAR(voice)+ 0x20 +(((voice)&0x1f)>>1))<<2) /* 0x0080 */
+#define WT_MUTE(voice) ((WT_BAR(voice)+ 0x40 +((voice)&0x1f))<<2) /* 0x0100 */
+#define WT_RUN(voice) ((WT_BAR(voice)+ 0x60 +((voice)&0x1f))<<2) /* 0x0180 */
+/* Some kind of parameters. */
+/* PARM0, PARM1 : Filter (0xFF000000), SampleRate (0x0000FFFF) */
+/* PARM2, PARM3 : Still unknown */
+#define WT_PARM(x,y) (((WT_BAR(x))+ 0x80 +(((x)&0x1f)<<2)+(y))<<2) /* 0x0200 */
+#define WT_DELAY(x,y) (((WT_BAR(x))+ 0x100 +(((x)&0x1f)<<2)+(y))<<2) /* 0x0400 */
+
+/* Numeric indexes used by SetReg() and GetReg() */
+#if 0
+enum {
+ run = 0, /* 0 W 1:run 0:stop */
+ parm0, /* 1 W filter, samplerate */
+ parm1, /* 2 W filter, samplerate */
+ parm2, /* 3 W */
+ parm3, /* 4 RW volume. This value is calculated using floating point ops. */
+ sramp, /* 5 W */
+ mute, /* 6 W 1:mute, 0:unmute */
+ gmode, /* 7 RO Looks like only bit0 is used. */
+ aramp, /* 8 W */
+ mramp, /* 9 W */
+ ctrl, /* a W */
+ delay, /* b W All 4 values are written at once with same value. */
+ dsreg, /* c (R)W */
+} wt_reg;
+#endif
+
+typedef struct {
+ unsigned int parm0; /* this_1E4 */
+ unsigned int parm1; /* this_1E8 */
+ unsigned int parm2; /* this_1EC */
+ unsigned int parm3; /* this_1F0 */
+ unsigned int this_1D0;
+} wt_voice_t;
+
+#endif /* _AU88X0_WT_H */
+
+/* End of file */
diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c
new file mode 100644
index 00000000000..df915fa3f88
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_xtalk.c
@@ -0,0 +1,787 @@
+/***************************************************************************
+ * au88x0_cxtalk.c
+ *
+ * Wed Nov 19 16:29:47 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "au88x0_xtalk.h"
+
+/* Data (a whole lot of data.... ) */
+
+static short const sXtalkWideKLeftEq = 0x269C;
+static short const sXtalkWideKRightEq = 0x269C;
+static short const sXtalkWideKLeftXt = 0xF25E;
+static short const sXtalkWideKRightXt = 0xF25E;
+static short const sXtalkWideShiftLeftEq = 1;
+static short const sXtalkWideShiftRightEq = 1;
+static short const sXtalkWideShiftLeftXt = 0;
+static short const sXtalkWideShiftRightXt = 0;
+static unsigned short const wXtalkWideLeftDelay = 0xd;
+static unsigned short const wXtalkWideRightDelay = 0xd;
+static short const sXtalkNarrowKLeftEq = 0x468D;
+static short const sXtalkNarrowKRightEq = 0x468D;
+static short const sXtalkNarrowKLeftXt = 0xF82E;
+static short const sXtalkNarrowKRightXt = 0xF82E;
+static short const sXtalkNarrowShiftLeftEq = 0x3;
+static short const sXtalkNarrowShiftRightEq = 0x3;
+static short const sXtalkNarrowShiftLeftXt = 0;
+static short const sXtalkNarrowShiftRightXt = 0;
+static unsigned short const wXtalkNarrowLeftDelay = 0x7;
+static unsigned short const wXtalkNarrowRightDelay = 0x7;
+
+static xtalk_gains_t const asXtalkGainsDefault = {
+ 0x4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000,
+ 0x4000
+};
+
+static xtalk_gains_t const asXtalkGainsTest = {
+ 0x8000, 0x7FFF, 0, 0xFFFF, 0x0001, 0xC000, 0x4000, 0xFFFE, 0x0002,
+ 0
+};
+static xtalk_gains_t const asXtalkGains1Chan = {
+ 0x7FFF, 0, 0, 0, 0x7FFF, 0, 0, 0, 0, 0
+};
+
+// Input gain for 4 A3D slices. One possible input pair is left zero.
+static xtalk_gains_t const asXtalkGainsAllChan = {
+ 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF,
+ 0
+ //0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff,0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff
+};
+static xtalk_gains_t const asXtalkGainsZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static xtalk_dline_t const alXtalkDlineZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0
+};
+static xtalk_dline_t const alXtalkDlineTest = {
+ 0xFC18, 0x03E8FFFF, 0x186A0, 0x7960FFFE, 1, 0xFFFFFFFF,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static xtalk_instate_t const asXtalkInStateZeros = { 0, 0, 0, 0 };
+static xtalk_instate_t const asXtalkInStateTest =
+ { 0xFF80, 0x0080, 0xFFFF, 0x0001 };
+static xtalk_state_t const asXtalkOutStateZeros = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+static short const sDiamondKLeftEq = 0x401d;
+static short const sDiamondKRightEq = 0x401d;
+static short const sDiamondKLeftXt = 0xF90E;
+static short const sDiamondKRightXt = 0xF90E;
+static short const sDiamondShiftLeftEq = 1; /* 0xF90E Is this a bug ??? */
+static short const sDiamondShiftRightEq = 1;
+static short const sDiamondShiftLeftXt = 0;
+static short const sDiamondShiftRightXt = 0;
+static unsigned short const wDiamondLeftDelay = 0xb;
+static unsigned short const wDiamondRightDelay = 0xb;
+
+static xtalk_coefs_t const asXtalkWideCoefsLeftEq = {
+ {0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
+ {0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
+ {0x340B, 0xf504, 0x6CE8, 0x0D23, 0x00E4},
+ {0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+ {0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsRightEq = {
+ {0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
+ {0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
+ {0x340B, 0xF504, 0x6CE8, 0x0D23, 0x00E4},
+ {0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+ {0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsLeftXt = {
+ {0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
+ {0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
+ {0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
+ {0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+ {0, 0, 0, 0, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsRightXt = {
+ {0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
+ {0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
+ {0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
+ {0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+ {0, 0, 0, 0, 0}
+};
+static xtalk_coefs_t const asXtalkNarrowCoefsLeftEq = {
+ {0x50B5, 0xD07C, 0x026D, 0xFD21, 0},
+ {0x460F, 0xE44F, 0xF75E, 0xEFA6, 0},
+ {0x556D, 0xDCAB, 0x2098, 0xF0F2, 0},
+ {0x7E03, 0xC1F0, 0x007D, 0xFF89, 0},
+ {0x383E, 0xFD9D, 0xB278, 0x4547, 0}
+};
+
+static xtalk_coefs_t const asXtalkNarrowCoefsRightEq = {
+ {0x50B5, 0xD07C, 0x026D, 0xFD21, 0},
+ {0x460F, 0xE44F, 0xF75E, 0xEFA6, 0},
+ {0x556D, 0xDCAB, 0x2098, 0xF0F2, 0},
+ {0x7E03, 0xC1F0, 0x007D, 0xFF89, 0},
+ {0x383E, 0xFD9D, 0xB278, 0x4547, 0}
+};
+
+static xtalk_coefs_t const asXtalkNarrowCoefsLeftXt = {
+ {0x3CB2, 0xDF49, 0xF6EA, 0x095B, 0},
+ {0x6777, 0xC915, 0xFEAF, 0x00B1, 0},
+ {0x7762, 0xC7D9, 0x025B, 0xFDA6, 0},
+ {0x6B7A, 0xD2AA, 0xF2FB, 0x0B64, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
+ {0x3CB2, 0xDF49, 0xF6EA, 0x095B, 0},
+ {0x6777, 0xC915, 0xFEAF, 0x00B1, 0},
+ {0x7762, 0xC7D9, 0x025B, 0xFDA6, 0},
+ {0x6B7A, 0xD2AA, 0xF2FB, 0x0B64, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkCoefsZeros = {
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+static xtalk_coefs_t const asXtalkCoefsPipe = {
+ {0, 0, 0x0FA0, 0, 0},
+ {0, 0, 0x0FA0, 0, 0},
+ {0, 0, 0x0FA0, 0, 0},
+ {0, 0, 0x0FA0, 0, 0},
+ {0, 0, 0x1180, 0, 0},
+};
+static xtalk_coefs_t const asXtalkCoefsNegPipe = {
+ {0, 0, 0xF380, 0, 0},
+ {0, 0, 0xF380, 0, 0},
+ {0, 0, 0xF380, 0, 0},
+ {0, 0, 0xF380, 0, 0},
+ {0, 0, 0xF200, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkCoefsNumTest = {
+ {0, 0, 0xF380, 0x8000, 0x6D60},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkCoefsDenTest = {
+ {0xC000, 0x2000, 0x4000, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static xtalk_state_t const asXtalkOutStateTest = {
+ {0x7FFF, 0x0004, 0xFFFC, 0},
+ {0xFE00, 0x0008, 0xFFF8, 0x4000},
+ {0x200, 0x0010, 0xFFF0, 0xC000},
+ {0x8000, 0x0020, 0xFFE0, 0},
+ {0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsLeftEq = {
+ {0x0F1E, 0x2D05, 0xF8E3, 0x07C8, 0},
+ {0x45E2, 0xCA51, 0x0448, 0xFCE7, 0},
+ {0xA93E, 0xDBD5, 0x022C, 0x028A, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsRightEq = {
+ {0x0F1E, 0x2D05, 0xF8E3, 0x07C8, 0},
+ {0x45E2, 0xCA51, 0x0448, 0xFCE7, 0},
+ {0xA93E, 0xDBD5, 0x022C, 0x028A, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsLeftXt = {
+ {0x3B50, 0xFE08, 0xF959, 0x0060, 0},
+ {0x9FCB, 0xD8F1, 0x00A2, 0x003A, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsRightXt = {
+ {0x3B50, 0xFE08, 0xF959, 0x0060, 0},
+ {0x9FCB, 0xD8F1, 0x00A2, 0x003A, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+ /**/
+/* XTalk EQ and XT */
+static void
+vortex_XtalkHw_SetLeftEQ(vortex_t * vortex, short arg_0, short arg_4,
+ xtalk_coefs_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x24200 + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x24204 + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x24208 + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x2420c + i * 0x24, coefs[i][3]);
+ hwwrite(vortex->mmio, 0x24210 + i * 0x24, coefs[i][4]);
+ }
+ hwwrite(vortex->mmio, 0x24538, arg_0 & 0xffff);
+ hwwrite(vortex->mmio, 0x2453C, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetRightEQ(vortex_t * vortex, short arg_0, short arg_4,
+ xtalk_coefs_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x242b4 + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x242b8 + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x242bc + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x242c0 + i * 0x24, coefs[i][3]);
+ hwwrite(vortex->mmio, 0x242c4 + i * 0x24, coefs[i][4]);
+ }
+ hwwrite(vortex->mmio, 0x24540, arg_0 & 0xffff);
+ hwwrite(vortex->mmio, 0x24544, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetLeftXT(vortex_t * vortex, short arg_0, short arg_4,
+ xtalk_coefs_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x24368 + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x2436c + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x24370 + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x24374 + i * 0x24, coefs[i][3]);
+ hwwrite(vortex->mmio, 0x24378 + i * 0x24, coefs[i][4]);
+ }
+ hwwrite(vortex->mmio, 0x24548, arg_0 & 0xffff);
+ hwwrite(vortex->mmio, 0x2454C, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetRightXT(vortex_t * vortex, short arg_0, short arg_4,
+ xtalk_coefs_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x2441C + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x24420 + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x24424 + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x24428 + i * 0x24, coefs[i][3]);
+ hwwrite(vortex->mmio, 0x2442C + i * 0x24, coefs[i][4]);
+ }
+ hwwrite(vortex->mmio, 0x24550, arg_0 & 0xffff);
+ hwwrite(vortex->mmio, 0x24554, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetLeftEQStates(vortex_t * vortex,
+ xtalk_instate_t const arg_0,
+ xtalk_state_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x24214 + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x24218 + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x2421C + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x24220 + i * 0x24, coefs[i][3]);
+ }
+ hwwrite(vortex->mmio, 0x244F8 + i * 0x24, arg_0[0]);
+ hwwrite(vortex->mmio, 0x244FC + i * 0x24, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24500 + i * 0x24, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24504 + i * 0x24, arg_0[3]);
+}
+
+static void
+vortex_XtalkHw_SetRightEQStates(vortex_t * vortex,
+ xtalk_instate_t const arg_0,
+ xtalk_state_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x242C8 + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x242CC + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x242D0 + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x244D4 + i * 0x24, coefs[i][3]);
+ }
+ hwwrite(vortex->mmio, 0x24508 + i * 0x24, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2450C + i * 0x24, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24510 + i * 0x24, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24514 + i * 0x24, arg_0[3]);
+}
+
+static void
+vortex_XtalkHw_SetLeftXTStates(vortex_t * vortex,
+ xtalk_instate_t const arg_0,
+ xtalk_state_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x2437C + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x24380 + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x24384 + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x24388 + i * 0x24, coefs[i][3]);
+ }
+ hwwrite(vortex->mmio, 0x24518 + i * 0x24, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2451C + i * 0x24, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24520 + i * 0x24, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24524 + i * 0x24, arg_0[3]);
+}
+
+static void
+vortex_XtalkHw_SetRightXTStates(vortex_t * vortex,
+ xtalk_instate_t const arg_0,
+ xtalk_state_t const coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ hwwrite(vortex->mmio, 0x24430 + i * 0x24, coefs[i][0]);
+ hwwrite(vortex->mmio, 0x24434 + i * 0x24, coefs[i][1]);
+ hwwrite(vortex->mmio, 0x24438 + i * 0x24, coefs[i][2]);
+ hwwrite(vortex->mmio, 0x2443C + i * 0x24, coefs[i][3]);
+ }
+ hwwrite(vortex->mmio, 0x24528 + i * 0x24, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2452C + i * 0x24, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24530 + i * 0x24, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24534 + i * 0x24, arg_0[3]);
+}
+
+#if 0
+static void
+vortex_XtalkHw_GetLeftEQ(vortex_t * vortex, short *arg_0, short *arg_4,
+ xtalk_coefs_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x24200 + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x24204 + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x24208 + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x2420c + i * 0x24);
+ coefs[i][4] = hwread(vortex->mmio, 0x24210 + i * 0x24);
+ }
+ *arg_0 = hwread(vortex->mmio, 0x24538) & 0xffff;
+ *arg_4 = hwread(vortex->mmio, 0x2453c) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetRightEQ(vortex_t * vortex, short *arg_0, short *arg_4,
+ xtalk_coefs_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x242b4 + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x242b8 + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x242bc + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x242c0 + i * 0x24);
+ coefs[i][4] = hwread(vortex->mmio, 0x242c4 + i * 0x24);
+ }
+ *arg_0 = hwread(vortex->mmio, 0x24540) & 0xffff;
+ *arg_4 = hwread(vortex->mmio, 0x24544) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetLeftXT(vortex_t * vortex, short *arg_0, short *arg_4,
+ xtalk_coefs_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x24368 + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x2436C + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x24370 + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x24374 + i * 0x24);
+ coefs[i][4] = hwread(vortex->mmio, 0x24378 + i * 0x24);
+ }
+ *arg_0 = hwread(vortex->mmio, 0x24548) & 0xffff;
+ *arg_4 = hwread(vortex->mmio, 0x2454C) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetRightXT(vortex_t * vortex, short *arg_0, short *arg_4,
+ xtalk_coefs_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x2441C + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x24420 + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x24424 + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x24428 + i * 0x24);
+ coefs[i][4] = hwread(vortex->mmio, 0x2442C + i * 0x24);
+ }
+ *arg_0 = hwread(vortex->mmio, 0x24550) & 0xffff;
+ *arg_4 = hwread(vortex->mmio, 0x24554) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetLeftEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
+ xtalk_state_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x24214 + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x24218 + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x2421C + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x24220 + i * 0x24);
+ }
+ arg_0[0] = hwread(vortex->mmio, 0x244F8 + i * 0x24);
+ arg_0[1] = hwread(vortex->mmio, 0x244FC + i * 0x24);
+ arg_0[2] = hwread(vortex->mmio, 0x24500 + i * 0x24);
+ arg_0[3] = hwread(vortex->mmio, 0x24504 + i * 0x24);
+}
+
+static void
+vortex_XtalkHw_GetRightEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
+ xtalk_state_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x242C8 + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x242CC + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x242D0 + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x242D4 + i * 0x24);
+ }
+ arg_0[0] = hwread(vortex->mmio, 0x24508 + i * 0x24);
+ arg_0[1] = hwread(vortex->mmio, 0x2450C + i * 0x24);
+ arg_0[2] = hwread(vortex->mmio, 0x24510 + i * 0x24);
+ arg_0[3] = hwread(vortex->mmio, 0x24514 + i * 0x24);
+}
+
+static void
+vortex_XtalkHw_GetLeftXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
+ xtalk_state_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x2437C + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x24380 + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x24384 + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x24388 + i * 0x24);
+ }
+ arg_0[0] = hwread(vortex->mmio, 0x24518 + i * 0x24);
+ arg_0[1] = hwread(vortex->mmio, 0x2451C + i * 0x24);
+ arg_0[2] = hwread(vortex->mmio, 0x24520 + i * 0x24);
+ arg_0[3] = hwread(vortex->mmio, 0x24524 + i * 0x24);
+}
+
+static void
+vortex_XtalkHw_GetRightXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
+ xtalk_state_t coefs)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ coefs[i][0] = hwread(vortex->mmio, 0x24430 + i * 0x24);
+ coefs[i][1] = hwread(vortex->mmio, 0x24434 + i * 0x24);
+ coefs[i][2] = hwread(vortex->mmio, 0x24438 + i * 0x24);
+ coefs[i][3] = hwread(vortex->mmio, 0x2443C + i * 0x24);
+ }
+ arg_0[0] = hwread(vortex->mmio, 0x24528 + i * 0x24);
+ arg_0[1] = hwread(vortex->mmio, 0x2452C + i * 0x24);
+ arg_0[2] = hwread(vortex->mmio, 0x24530 + i * 0x24);
+ arg_0[3] = hwread(vortex->mmio, 0x24534 + i * 0x24);
+}
+
+#endif
+/* Gains */
+
+static void
+vortex_XtalkHw_SetGains(vortex_t * vortex, xtalk_gains_t const gains)
+{
+ int i;
+
+ for (i = 0; i < XTGAINS_SZ; i++) {
+ hwwrite(vortex->mmio, 0x244D0 + (i * 4), gains[i]);
+ }
+}
+
+static void
+vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex)
+{
+ vortex_XtalkHw_SetGains(vortex, asXtalkGainsAllChan);
+}
+
+#if 0
+static void vortex_XtalkHw_GetGains(vortex_t * vortex, xtalk_gains_t gains)
+{
+ int i;
+
+ for (i = 0; i < XTGAINS_SZ; i++)
+ gains[i] = hwread(vortex->mmio, 0x244D0 + i * 4);
+}
+
+#endif
+/* Delay parameters */
+
+static void
+vortex_XtalkHw_SetDelay(vortex_t * vortex, unsigned short right,
+ unsigned short left)
+{
+ int esp0 = 0;
+
+ esp0 &= 0x1FFFFFFF;
+ esp0 |= 0xA0000000;
+ esp0 = (esp0 & 0xffffE0ff) | ((right & 0x1F) << 8);
+ esp0 = (esp0 & 0xfffc1fff) | ((left & 0x1F) << 0xd);
+
+ hwwrite(vortex->mmio, 0x24660, esp0);
+}
+
+static void
+vortex_XtalkHw_SetLeftDline(vortex_t * vortex, xtalk_dline_t const dline)
+{
+ int i;
+
+ for (i = 0; i < 0x20; i++) {
+ hwwrite(vortex->mmio, 0x24000 + (i << 2), dline[i] & 0xffff);
+ hwwrite(vortex->mmio, 0x24080 + (i << 2), dline[i] >> 0x10);
+ }
+}
+
+static void
+vortex_XtalkHw_SetRightDline(vortex_t * vortex, xtalk_dline_t const dline)
+{
+ int i;
+
+ for (i = 0; i < 0x20; i++) {
+ hwwrite(vortex->mmio, 0x24100 + (i << 2), dline[i] & 0xffff);
+ hwwrite(vortex->mmio, 0x24180 + (i << 2), dline[i] >> 0x10);
+ }
+}
+
+#if 0
+static void
+vortex_XtalkHw_GetDelay(vortex_t * vortex, unsigned short *right,
+ unsigned short *left)
+{
+ int esp0;
+
+ esp0 = hwread(vortex->mmio, 0x24660);
+ *right = (esp0 >> 8) & 0x1f;
+ *left = (esp0 >> 0xd) & 0x1f;
+}
+
+static void vortex_XtalkHw_GetLeftDline(vortex_t * vortex, xtalk_dline_t dline)
+{
+ int i;
+
+ for (i = 0; i < 0x20; i++) {
+ dline[i] =
+ (hwread(vortex->mmio, 0x24000 + (i << 2)) & 0xffff) |
+ (hwread(vortex->mmio, 0x24080 + (i << 2)) << 0x10);
+ }
+}
+
+static void vortex_XtalkHw_GetRightDline(vortex_t * vortex, xtalk_dline_t dline)
+{
+ int i;
+
+ for (i = 0; i < 0x20; i++) {
+ dline[i] =
+ (hwread(vortex->mmio, 0x24100 + (i << 2)) & 0xffff) |
+ (hwread(vortex->mmio, 0x24180 + (i << 2)) << 0x10);
+ }
+}
+
+#endif
+/* Control/Global stuff */
+
+#if 0
+static void vortex_XtalkHw_SetControlReg(vortex_t * vortex, unsigned long ctrl)
+{
+ hwwrite(vortex->mmio, 0x24660, ctrl);
+}
+static void vortex_XtalkHw_GetControlReg(vortex_t * vortex, unsigned long *ctrl)
+{
+ *ctrl = hwread(vortex->mmio, 0x24660);
+}
+#endif
+static void vortex_XtalkHw_SetSampleRate(vortex_t * vortex, int sr)
+{
+ int temp;
+
+ temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+ temp = (temp & 0xffffff07) | ((sr & 0x1f) << 3);
+ hwwrite(vortex->mmio, 0x24660, temp);
+}
+
+#if 0
+static void vortex_XtalkHw_GetSampleRate(vortex_t * vortex, int *sr)
+{
+ *sr = (hwread(vortex->mmio, 0x24660) >> 3) & 0x1f;
+}
+
+#endif
+static void vortex_XtalkHw_Enable(vortex_t * vortex)
+{
+ int temp;
+
+ temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+ temp |= 1;
+ hwwrite(vortex->mmio, 0x24660, temp);
+
+}
+
+static void vortex_XtalkHw_Disable(vortex_t * vortex)
+{
+ int temp;
+
+ temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+ temp &= 0xfffffffe;
+ hwwrite(vortex->mmio, 0x24660, temp);
+
+}
+
+static void vortex_XtalkHw_ZeroIO(vortex_t * vortex)
+{
+ int i;
+
+ for (i = 0; i < 20; i++)
+ hwwrite(vortex->mmio, 0x24600 + (i << 2), 0);
+ for (i = 0; i < 4; i++)
+ hwwrite(vortex->mmio, 0x24650 + (i << 2), 0);
+}
+
+static void vortex_XtalkHw_ZeroState(vortex_t * vortex)
+{
+ vortex_XtalkHw_ZeroIO(vortex); // inlined
+
+ vortex_XtalkHw_SetLeftEQ(vortex, 0, 0, asXtalkCoefsZeros);
+ vortex_XtalkHw_SetRightEQ(vortex, 0, 0, asXtalkCoefsZeros);
+
+ vortex_XtalkHw_SetLeftXT(vortex, 0, 0, asXtalkCoefsZeros);
+ vortex_XtalkHw_SetRightXT(vortex, 0, 0, asXtalkCoefsZeros);
+
+ vortex_XtalkHw_SetGains(vortex, asXtalkGainsZeros); // inlined
+
+ vortex_XtalkHw_SetDelay(vortex, 0, 0); // inlined
+
+ vortex_XtalkHw_SetLeftDline(vortex, alXtalkDlineZeros); // inlined
+ vortex_XtalkHw_SetRightDline(vortex, alXtalkDlineZeros); // inlined
+ vortex_XtalkHw_SetLeftDline(vortex, alXtalkDlineZeros); // inlined
+ vortex_XtalkHw_SetRightDline(vortex, alXtalkDlineZeros); // inlined
+
+ vortex_XtalkHw_SetLeftEQStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+ vortex_XtalkHw_SetRightEQStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+ vortex_XtalkHw_SetLeftXTStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+ vortex_XtalkHw_SetRightXTStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+}
+
+static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex)
+{
+
+ vortex_XtalkHw_SetLeftEQ(vortex, 0, 1, asXtalkCoefsPipe);
+ vortex_XtalkHw_SetRightEQ(vortex, 0, 1, asXtalkCoefsPipe);
+ vortex_XtalkHw_SetLeftXT(vortex, 0, 0, asXtalkCoefsZeros);
+ vortex_XtalkHw_SetRightXT(vortex, 0, 0, asXtalkCoefsZeros);
+
+ vortex_XtalkHw_SetDelay(vortex, 0, 0); // inlined
+}
+
+static void vortex_XtalkHw_ProgramXtalkWide(vortex_t * vortex)
+{
+
+ vortex_XtalkHw_SetLeftEQ(vortex, sXtalkWideKLeftEq,
+ sXtalkWideShiftLeftEq, asXtalkWideCoefsLeftEq);
+ vortex_XtalkHw_SetRightEQ(vortex, sXtalkWideKRightEq,
+ sXtalkWideShiftRightEq,
+ asXtalkWideCoefsRightEq);
+ vortex_XtalkHw_SetLeftXT(vortex, sXtalkWideKLeftXt,
+ sXtalkWideShiftLeftXt, asXtalkWideCoefsLeftXt);
+ vortex_XtalkHw_SetRightXT(vortex, sXtalkWideKLeftXt,
+ sXtalkWideShiftLeftXt,
+ asXtalkWideCoefsLeftXt);
+
+ vortex_XtalkHw_SetDelay(vortex, wXtalkWideRightDelay, wXtalkWideLeftDelay); // inlined
+}
+
+static void vortex_XtalkHw_ProgramXtalkNarrow(vortex_t * vortex)
+{
+
+ vortex_XtalkHw_SetLeftEQ(vortex, sXtalkNarrowKLeftEq,
+ sXtalkNarrowShiftLeftEq,
+ asXtalkNarrowCoefsLeftEq);
+ vortex_XtalkHw_SetRightEQ(vortex, sXtalkNarrowKRightEq,
+ sXtalkNarrowShiftRightEq,
+ asXtalkNarrowCoefsRightEq);
+ vortex_XtalkHw_SetLeftXT(vortex, sXtalkNarrowKLeftXt,
+ sXtalkNarrowShiftLeftXt,
+ asXtalkNarrowCoefsLeftXt);
+ vortex_XtalkHw_SetRightXT(vortex, sXtalkNarrowKLeftXt,
+ sXtalkNarrowShiftLeftXt,
+ asXtalkNarrowCoefsLeftXt);
+
+ vortex_XtalkHw_SetDelay(vortex, wXtalkNarrowRightDelay, wXtalkNarrowLeftDelay); // inlined
+}
+
+static void vortex_XtalkHw_ProgramDiamondXtalk(vortex_t * vortex)
+{
+
+ //sDiamondKLeftEq,sDiamondKRightXt,asDiamondCoefsLeftEq
+ vortex_XtalkHw_SetLeftEQ(vortex, sDiamondKLeftEq,
+ sDiamondShiftLeftEq, asDiamondCoefsLeftEq);
+ vortex_XtalkHw_SetRightEQ(vortex, sDiamondKRightEq,
+ sDiamondShiftRightEq, asDiamondCoefsRightEq);
+ vortex_XtalkHw_SetLeftXT(vortex, sDiamondKLeftXt,
+ sDiamondShiftLeftXt, asDiamondCoefsLeftXt);
+ vortex_XtalkHw_SetRightXT(vortex, sDiamondKLeftXt,
+ sDiamondShiftLeftXt, asDiamondCoefsLeftXt);
+
+ vortex_XtalkHw_SetDelay(vortex, wDiamondRightDelay, wDiamondLeftDelay); // inlined
+}
+
+static void vortex_XtalkHw_init(vortex_t * vortex)
+{
+ vortex_XtalkHw_ZeroState(vortex);
+}
+
+/* End of file */
diff --git a/sound/pci/au88x0/au88x0_xtalk.h b/sound/pci/au88x0/au88x0_xtalk.h
new file mode 100644
index 00000000000..0b8d7b64012
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_xtalk.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * au88x0_cxtalk.h
+ *
+ * Wed Nov 19 19:07:17 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* The crosstalk canceler supports 5 stereo input channels. The result is
+ available at one single output route pair (stereo). */
+
+#ifndef _AU88X0_CXTALK_H
+#define _AU88X0_CXTALK_H
+
+#include "au88x0.h"
+
+#define XTDLINE_SZ 32
+#define XTGAINS_SZ 10
+#define XTINST_SZ 4
+
+#define XT_HEADPHONE 1
+#define XT_SPEAKER0 2
+#define XT_SPEAKER1 3
+#define XT_DIAMOND 4
+
+typedef long xtalk_dline_t[XTDLINE_SZ];
+typedef short xtalk_gains_t[XTGAINS_SZ];
+typedef short xtalk_instate_t[XTINST_SZ];
+typedef short xtalk_coefs_t[5][5];
+typedef short xtalk_state_t[5][4];
+
+static void vortex_XtalkHw_SetGains(vortex_t * vortex,
+ xtalk_gains_t const gains);
+static void vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex);
+static void vortex_XtalkHw_SetSampleRate(vortex_t * vortex, int sr);
+static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramXtalkWide(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramXtalkNarrow(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramDiamondXtalk(vortex_t * vortex);
+static void vortex_XtalkHw_Enable(vortex_t * vortex);
+static void vortex_XtalkHw_Disable(vortex_t * vortex);
+static void vortex_XtalkHw_init(vortex_t * vortex);
+
+#endif /* _AU88X0_CXTALK_H */
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
new file mode 100644
index 00000000000..b8ae534125c
--- /dev/null
+++ b/sound/pci/azt3328.c
@@ -0,0 +1,1536 @@
+/*
+ * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
+ * Copyright (C) 2002 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
+ *
+ * Framework borrowed from Bart Hartgers's als4000.c.
+ * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
+ * found in a Fujitsu-Siemens PC ("Cordant", aluminum case).
+ * Other versions are:
+ * PCI168 A(W), sub ID 1800
+ * PCI168 A/AP, sub ID 8000
+ * Please give me feedback in case you try my driver with one of these!!
+ *
+ * GPL LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NOTES
+ * Since Aztech does not provide any chipset documentation,
+ * even on repeated request to various addresses,
+ * and the answer that was finally given was negative
+ * (and I was stupid enough to manage to get hold of a PCI168 soundcard
+ * in the first place >:-P}),
+ * I was forced to base this driver on reverse engineering
+ * (3 weeks' worth of evenings filled with driver work).
+ * (and no, I did NOT go the easy way: to pick up a PCI128 for 9 Euros)
+ *
+ * The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
+ * for compatibility reasons) has the following features:
+ *
+ * - builtin AC97 conformant codec (SNR over 80dB)
+ * (really AC97 compliant?? I really doubt it when looking
+ * at the mixer register layout)
+ * - builtin genuine OPL3
+ * - full duplex 16bit playback/record at independent sampling rate
+ * - MPU401 (+ legacy address support) FIXME: how to enable legacy addr??
+ * - game port (legacy address support)
+ * - built-in General DirectX timer having a 20 bits counter
+ * with 1us resolution (FIXME: where is it?)
+ * - I2S serial port for external DAC
+ * - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
+ * - supports hardware volume control
+ * - single chip low cost solution (128 pin QFP)
+ * - supports programmable Sub-vendor and Sub-system ID
+ * required for Microsoft's logo compliance (FIXME: where?)
+ * - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
+ *
+ * Certain PCI versions of this card are susceptible to DMA traffic underruns
+ * in some systems (resulting in sound crackling/clicking/popping),
+ * probably because they don't have a DMA FIFO buffer or so.
+ * Overview (PCI ID/PCI subID/PCI rev.):
+ * - no DMA crackling on SiS735: 0x50DC/0x1801/16
+ * - unknown performance: 0x50DC/0x1801/10
+ *
+ * Crackling happens with VIA chipsets or, in my case, an SiS735, which is
+ * supposed to be very fast and supposed to get rid of crackling much
+ * better than a VIA, yet ironically I still get crackling, like many other
+ * people with the same chipset.
+ * Possible remedies:
+ * - plug card into a different PCI slot, preferrably one that isn't shared
+ * too much (this helps a lot, but not completely!)
+ * - get rid of PCI VGA card, use AGP instead
+ * - upgrade or downgrade BIOS
+ * - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
+ * Not too helpful.
+ * - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
+ *
+ * BUGS
+ * - when Ctrl-C'ing mpg321, the playback loops a bit
+ * (premature DMA playback reset?)
+ * - full-duplex sometimes breaks (IRQ management issues?).
+ * Once even a spontaneous REBOOT happened!!!
+ *
+ * TODO
+ * - test MPU401 MIDI playback etc.
+ * - power management (CONFIG_PM). See e.g. intel8x0 or cs4281.
+ * This would be nice since the chip runs a bit hot, and it's *required*
+ * anyway for proper ACPI power management. In other words: rest
+ * assured that I *will* implement this very soon; as soon as Linux 2.5.x
+ * has power management that's bugfree enough to work properly on my desktop.
+ * - figure out what all unknown port bits are responsible for
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+#include "azt3328.h"
+
+MODULE_AUTHOR("Andreas Mohr <hw7oshyuv3001@sneakemail.com>");
+MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+#define DEBUG_MISC 0
+#define DEBUG_CALLS 0
+#define DEBUG_MIXER 0
+#define DEBUG_PLAY_REC 0
+#define DEBUG_IO 0
+#define MIXER_TESTING 0
+
+#if DEBUG_MISC
+#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)
+#else
+#define snd_azf3328_dbgmisc(format, args...)
+#endif
+
+#if DEBUG_CALLS
+#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
+#define snd_azf3328_dbgcallenter() printk(KERN_ERR "entering %s\n", __FUNCTION__)
+#define snd_azf3328_dbgcallleave() printk(KERN_ERR "leaving %s\n", __FUNCTION__)
+#else
+#define snd_azf3328_dbgcalls(format, args...)
+#define snd_azf3328_dbgcallenter()
+#define snd_azf3328_dbgcallleave()
+#endif
+
+#if DEBUG_MIXER
+#define snd_azf3328_dbgmixer(format, args...) printk(format, ##args)
+#else
+#define snd_azf3328_dbgmixer(format, args...)
+#endif
+
+#if DEBUG_PLAY_REC
+#define snd_azf3328_dbgplay(format, args...) printk(KERN_ERR format, ##args)
+#else
+#define snd_azf3328_dbgplay(format, args...)
+#endif
+
+#if DEBUG_IO
+#define snd_azf3328_dbgio(chip, where) \
+ printk(KERN_ERR "%s: IDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQMASK %04x, IDX_IO_IRQSTATUS %04x\n", where, inw(chip->codec_port+IDX_IO_PLAY_FLAGS), inw(chip->codec_port+IDX_IO_PLAY_IRQMASK), inw(chip->codec_port+IDX_IO_IRQSTATUS))
+#else
+#define snd_azf3328_dbgio(chip, where)
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
+
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
+
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
+
+#ifdef SUPPORT_JOYSTICK
+static int joystick[SNDRV_CARDS];
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable joystick for AZF3328 soundcard.");
+#endif
+
+typedef struct _snd_azf3328 azf3328_t;
+
+struct _snd_azf3328 {
+ int irq;
+
+ unsigned long codec_port;
+ unsigned long io2_port;
+ unsigned long mpu_port;
+ unsigned long synth_port;
+ unsigned long mixer_port;
+
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+
+ snd_pcm_t *pcm;
+ snd_rawmidi_t *rmidi;
+ snd_pcm_substream_t *playback_substream;
+ snd_pcm_substream_t *capture_substream;
+ unsigned int is_playing;
+ unsigned int is_recording;
+
+ spinlock_t reg_lock;
+};
+
+static struct pci_device_id snd_azf3328_ids[] = {
+ { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
+ { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
+
+static inline void snd_azf3328_io2_write(azf3328_t *chip, int reg, unsigned char value)
+{
+ outb(value, chip->io2_port + reg);
+}
+
+static inline unsigned char snd_azf3328_io2_read(azf3328_t *chip, int reg)
+{
+ return inb(chip->io2_port + reg);
+}
+
+static void snd_azf3328_mixer_write(azf3328_t *chip, int reg, unsigned long value, int type)
+{
+ switch(type) {
+ case WORD_VALUE:
+ outw(value, chip->mixer_port + reg);
+ break;
+ case DWORD_VALUE:
+ outl(value, chip->mixer_port + reg);
+ break;
+ case BYTE_VALUE:
+ outb(value, chip->mixer_port + reg);
+ break;
+ }
+}
+
+static void snd_azf3328_mixer_set_mute(azf3328_t *chip, int reg, int do_mute)
+{
+ unsigned char oldval;
+
+ /* the mute bit is on the *second* (i.e. right) register of a
+ * left/right channel setting */
+ oldval = inb(chip->mixer_port + reg + 1);
+ if (do_mute)
+ oldval |= 0x80;
+ else
+ oldval &= ~0x80;
+ outb(oldval, chip->mixer_port + reg + 1);
+}
+
+static void snd_azf3328_mixer_write_volume_gradually(azf3328_t *chip, int reg, unsigned char dst_vol_left, unsigned char dst_vol_right, int chan_sel, int delay)
+{
+ unsigned char curr_vol_left = 0, curr_vol_right = 0;
+ int left_done = 0, right_done = 0;
+
+ snd_azf3328_dbgcallenter();
+ if (chan_sel & SET_CHAN_LEFT)
+ curr_vol_left = inb(chip->mixer_port + reg + 1);
+ else
+ left_done = 1;
+ if (chan_sel & SET_CHAN_RIGHT)
+ curr_vol_right = inb(chip->mixer_port + reg + 0);
+ else
+ right_done = 1;
+
+ /* take care of muting flag (0x80) contained in left channel */
+ if (curr_vol_left & 0x80)
+ dst_vol_left |= 0x80;
+ else
+ dst_vol_left &= ~0x80;
+
+ do
+ {
+ if (!left_done)
+ {
+ if (curr_vol_left > dst_vol_left)
+ curr_vol_left--;
+ else
+ if (curr_vol_left < dst_vol_left)
+ curr_vol_left++;
+ else
+ left_done = 1;
+ outb(curr_vol_left, chip->mixer_port + reg + 1);
+ }
+ if (!right_done)
+ {
+ if (curr_vol_right > dst_vol_right)
+ curr_vol_right--;
+ else
+ if (curr_vol_right < dst_vol_right)
+ curr_vol_right++;
+ else
+ right_done = 1;
+ /* during volume change, the right channel is crackling
+ * somewhat more than the left channel, unfortunately.
+ * This seems to be a hardware issue. */
+ outb(curr_vol_right, chip->mixer_port + reg + 0);
+ }
+ if (delay)
+ mdelay(delay);
+ }
+ while ((!left_done) || (!right_done));
+ snd_azf3328_dbgcallleave();
+}
+
+/*
+ * general mixer element
+ */
+typedef struct azf3328_mixer_reg {
+ unsigned int reg;
+ unsigned int lchan_shift, rchan_shift;
+ unsigned int mask;
+ unsigned int invert: 1;
+ unsigned int stereo: 1;
+ unsigned int enum_c: 4;
+} azf3328_mixer_reg_t;
+
+#define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \
+ ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | (mask << 16) | (invert << 24) | (stereo << 25) | (enum_c << 26))
+
+static void snd_azf3328_mixer_reg_decode(azf3328_mixer_reg_t *r, unsigned long val)
+{
+ r->reg = val & 0xff;
+ r->lchan_shift = (val >> 8) & 0x0f;
+ r->rchan_shift = (val >> 12) & 0x0f;
+ r->mask = (val >> 16) & 0xff;
+ r->invert = (val >> 24) & 1;
+ r->stereo = (val >> 25) & 1;
+ r->enum_c = (val >> 26) & 0x0f;
+}
+
+/*
+ * mixer switches/volumes
+ */
+
+#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_azf3328_info_mixer, \
+ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
+ .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \
+}
+
+#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_azf3328_info_mixer, \
+ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
+ .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \
+}
+
+#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_azf3328_info_mixer, \
+ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
+ .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \
+}
+
+#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_azf3328_info_mixer, \
+ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
+ .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \
+}
+
+#define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_azf3328_info_mixer_enum, \
+ .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \
+ .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \
+}
+
+static int snd_azf3328_info_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ azf3328_mixer_reg_t reg;
+
+ snd_azf3328_dbgcallenter();
+ snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
+ uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = reg.stereo + 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = reg.mask;
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_get_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ azf3328_t *chip = snd_kcontrol_chip(kcontrol);
+ azf3328_mixer_reg_t reg;
+ unsigned int oreg, val;
+
+ snd_azf3328_dbgcallenter();
+ snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
+
+ oreg = inw(chip->mixer_port + reg.reg);
+ val = (oreg >> reg.lchan_shift) & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ ucontrol->value.integer.value[0] = val;
+ if (reg.stereo) {
+ val = (oreg >> reg.rchan_shift) & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ ucontrol->value.integer.value[1] = val;
+ }
+ snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx (shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n", reg.reg, oreg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1], reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_put_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ azf3328_t *chip = snd_kcontrol_chip(kcontrol);
+ azf3328_mixer_reg_t reg;
+ unsigned int oreg, nreg, val;
+
+ snd_azf3328_dbgcallenter();
+ snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
+ oreg = inw(chip->mixer_port + reg.reg);
+ val = ucontrol->value.integer.value[0] & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ nreg = oreg & ~(reg.mask << reg.lchan_shift);
+ nreg |= (val << reg.lchan_shift);
+ if (reg.stereo) {
+ val = ucontrol->value.integer.value[1] & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ nreg &= ~(reg.mask << reg.rchan_shift);
+ nreg |= (val << reg.rchan_shift);
+ }
+ if (reg.mask >= 0x07) /* it's a volume control, so better take care */
+ snd_azf3328_mixer_write_volume_gradually(chip, reg.reg, nreg >> 8, nreg & 0xff, SET_CHAN_LEFT|SET_CHAN_RIGHT, 0); /* just set both channels, doesn't matter */
+ else
+ outw(nreg, chip->mixer_port + reg.reg);
+
+ snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n", reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1], oreg, reg.lchan_shift, reg.rchan_shift, nreg, inw(chip->mixer_port + reg.reg));
+ snd_azf3328_dbgcallleave();
+ return (nreg != oreg);
+}
+
+static int snd_azf3328_info_mixer_enum(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ azf3328_mixer_reg_t reg;
+ static char *texts1[2] = { "ModemOut1", "ModemOut2" };
+ static char *texts2[2] = { "MonoSelectSource1", "MonoSelectSource2" };
+ static char *texts3[8] = {
+ "Mic", "CD", "Video", "Aux", "Line",
+ "Mix", "Mix Mono", "Phone"
+ };
+
+ snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
+ uinfo->value.enumerated.items = reg.enum_c;
+ if (uinfo->value.enumerated.item > reg.enum_c - 1U)
+ uinfo->value.enumerated.item = reg.enum_c - 1U;
+ if (reg.reg == IDX_MIXER_ADVCTL2)
+ {
+ if (reg.lchan_shift == 8) /* modem out sel */
+ strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]);
+ else /* mono sel source */
+ strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]);
+ }
+ else
+ strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]
+);
+ return 0;
+}
+
+static int snd_azf3328_get_mixer_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ azf3328_mixer_reg_t reg;
+ azf3328_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
+ val = inw(chip->mixer_port + reg.reg);
+ if (reg.reg == IDX_MIXER_REC_SELECT)
+ {
+ ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
+ ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
+ }
+ else
+ ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
+ snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n", reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1], reg.lchan_shift, reg.enum_c);
+ return 0;
+}
+
+static int snd_azf3328_put_mixer_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ azf3328_mixer_reg_t reg;
+ azf3328_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int oreg, nreg, val;
+
+ snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
+ oreg = inw(chip->mixer_port + reg.reg);
+ val = oreg;
+ if (reg.reg == IDX_MIXER_REC_SELECT)
+ {
+ if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
+ ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
+ return -EINVAL;
+ val = (ucontrol->value.enumerated.item[0] << 8) |
+ (ucontrol->value.enumerated.item[1] << 0);
+ }
+ else
+ {
+ if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
+ return -EINVAL;
+ val &= ~((reg.enum_c - 1) << reg.lchan_shift);
+ val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
+ }
+ outw(val, chip->mixer_port + reg.reg);
+ nreg = val;
+
+ snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
+ return (nreg != oreg);
+}
+
+static snd_kcontrol_new_t snd_azf3328_mixer_controls[] __devinitdata = {
+ AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("Wave Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("Wave Playback Volume", IDX_MIXER_WAVEOUT, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("Wave Playback 3D Bypass", IDX_MIXER_ADVCTL2, 7, 1),
+ AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
+ AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
+ AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
+ AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
+ AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("PCBeep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
+ AZF3328_MIXER_VOL_SPECIAL("PCBeep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
+ AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
+ AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
+ AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
+ AZF3328_MIXER_ENUM("Modem Out Select", IDX_MIXER_ADVCTL2, 2, 8),
+ AZF3328_MIXER_ENUM("Mono Select Source", IDX_MIXER_ADVCTL2, 2, 9),
+ AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
+ AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
+ AZF3328_MIXER_SWITCH("3D Control - Toggle", IDX_MIXER_ADVCTL2, 13, 0),
+ AZF3328_MIXER_VOL_SPECIAL("3D Control - Volume", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
+ AZF3328_MIXER_VOL_SPECIAL("3D Control - Space", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
+#if MIXER_TESTING
+ AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
+ AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
+ AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
+ AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
+ AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
+ AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
+ AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
+ AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
+ AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
+ AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
+ AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
+ AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
+ AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
+ AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
+ AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
+ AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
+#endif
+};
+
+#define AZF3328_INIT_VALUES (sizeof(snd_azf3328_init_values)/sizeof(unsigned int)/2)
+
+static unsigned int snd_azf3328_init_values[][2] = {
+ { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_BASSTREBLE, 0x0000 },
+ { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f },
+ { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f },
+ { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
+};
+
+static int __devinit snd_azf3328_mixer_new(azf3328_t *chip)
+{
+ snd_card_t *card;
+ snd_kcontrol_new_t *sw;
+ unsigned int idx;
+ int err;
+
+ snd_azf3328_dbgcallenter();
+ snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+ card = chip->card;
+
+ /* mixer reset */
+ snd_azf3328_mixer_write(chip, IDX_MIXER_RESET, 0x0, WORD_VALUE);
+
+ /* mute and zero volume channels */
+ for (idx = 0; idx < AZF3328_INIT_VALUES; idx++) {
+ snd_azf3328_mixer_write(chip, snd_azf3328_init_values[idx][0], snd_azf3328_init_values[idx][1], WORD_VALUE);
+ }
+
+ /* add mixer controls */
+ sw = snd_azf3328_mixer_controls;
+ for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls); idx++, sw++) {
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
+ return err;
+ }
+ snd_component_add(card, "AZF3328 mixer");
+ strcpy(card->mixername, "AZF3328 mixer");
+
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ int res;
+ snd_azf3328_dbgcallenter();
+ res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ snd_azf3328_dbgcallleave();
+ return res;
+}
+
+static int snd_azf3328_hw_free(snd_pcm_substream_t * substream)
+{
+ snd_azf3328_dbgcallenter();
+ snd_pcm_lib_free_pages(substream);
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static void snd_azf3328_setfmt(azf3328_t *chip,
+ unsigned int reg,
+ unsigned int bitrate,
+ unsigned int format_width,
+ unsigned int channels
+)
+{
+ unsigned int val = 0xff00;
+ unsigned long flags;
+
+ snd_azf3328_dbgcallenter();
+ switch (bitrate) {
+ case 5512: val |= 0x0d; break; /* the AZF3328 names it "5510" for some strange reason */
+ case 6620: val |= 0x0b; break;
+ case 8000: val |= 0x00; break;
+ case 9600: val |= 0x08; break;
+ case 11025: val |= 0x01; break;
+ case 16000: val |= 0x02; break;
+ case 22050: val |= 0x03; break;
+ case 32000: val |= 0x04; break;
+ case 44100: val |= 0x05; break;
+ case 48000: val |= 0x06; break;
+ case 64000: val |= 0x07; break;
+ default:
+ snd_printk("unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
+ val |= 0x05; /* 44100 */
+ break;
+ }
+ /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) */
+ /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) */
+ /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) */
+ /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) */
+ /* val = 0xff05; 5m11.556s (... -> 44100Hz) */
+ /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
+ /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
+ /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
+ /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
+ if (channels == 2)
+ val |= SOUNDFORMAT_FLAG_2CHANNELS;
+
+ if (format_width == 16)
+ val |= SOUNDFORMAT_FLAG_16BIT;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+
+ /* set bitrate/format */
+ outw(val, chip->codec_port+reg);
+
+ /* changing the bitrate/format settings switches off the
+ * audio output with an annoying click in case of 8/16bit format change
+ * (maybe shutting down DAC/ADC?), thus immediately
+ * do some tweaking to reenable it and get rid of the clicking
+ * (FIXME: yes, it works, but what exactly am I doing here?? :)
+ * FIXME: does this have some side effects for full-duplex
+ * or other dramatic side effects? */
+ if (reg == IDX_IO_PLAY_SOUNDFORMAT) /* only do it for playback */
+ outw(inw(chip->codec_port + IDX_IO_PLAY_FLAGS)|DMA_PLAY_SOMETHING1|DMA_PLAY_SOMETHING2|SOMETHING_ALMOST_ALWAYS_SET|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_PLAY_FLAGS);
+
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_azf3328_dbgcallleave();
+}
+
+static void snd_azf3328_setdmaa(azf3328_t *chip,
+ long unsigned int addr,
+ unsigned int count,
+ unsigned int size,
+ int do_recording)
+{
+ long unsigned int addr1;
+ long unsigned int addr2;
+ unsigned int count1;
+ unsigned int count2;
+ unsigned long flags;
+ int reg_offs = do_recording ? 0x20 : 0x00;
+
+ snd_azf3328_dbgcallenter();
+ /* AZF3328 uses a two buffer pointer DMA playback approach */
+ if (!chip->is_playing)
+ {
+ addr1 = addr;
+ addr2 = addr+(size/2);
+ count1 = (size/2)-1;
+ count2 = (size/2)-1;
+#if DEBUG_PLAY_REC
+ snd_azf3328_dbgplay("setting dma: buf1 %08lx[%d], buf2 %08lx[%d]\n", addr1, count1, addr2, count2);
+#endif
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ outl(addr1, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_START_1);
+ outl(addr2, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_START_2);
+ outw(count1, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_LEN_1);
+ outw(count2, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_LEN_2);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ }
+ snd_azf3328_dbgcallleave();
+}
+
+static int snd_azf3328_playback_prepare(snd_pcm_substream_t *substream)
+{
+#if 0
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+#endif
+
+ snd_azf3328_dbgcallenter();
+#if 0
+ snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels);
+ snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 0);
+#endif
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_capture_prepare(snd_pcm_substream_t * substream)
+{
+#if 0
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+#endif
+
+ snd_azf3328_dbgcallenter();
+#if 0
+ snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels);
+ snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 1);
+#endif
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int result = 0;
+ unsigned int status1;
+
+ snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+
+ snd_azf3328_dbgio(chip, "trigger1");
+
+ /* mute WaveOut */
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+
+ snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels);
+
+ spin_lock(&chip->reg_lock);
+ /* stop playback */
+ status1 = inw(chip->codec_port+IDX_IO_PLAY_FLAGS);
+ status1 &= ~DMA_RESUME;
+ outw(status1, chip->codec_port+IDX_IO_PLAY_FLAGS);
+
+ /* FIXME: clear interrupts or what??? */
+ outw(0xffff, chip->codec_port+IDX_IO_PLAY_IRQMASK);
+ spin_unlock(&chip->reg_lock);
+
+ snd_azf3328_setdmaa(chip, runtime->dma_addr, snd_pcm_lib_period_bytes(substream), snd_pcm_lib_buffer_bytes(substream), 0);
+
+ spin_lock(&chip->reg_lock);
+#ifdef WIN9X
+ /* FIXME: enable playback/recording??? */
+ status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2;
+ outw(status1, chip->codec_port+IDX_IO_PLAY_FLAGS);
+
+ /* start playback again */
+ /* FIXME: what is this value (0x0010)??? */
+ status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
+ outw(status1, chip->codec_port+IDX_IO_PLAY_FLAGS);
+#else /* NT4 */
+ outw(0x00, chip->codec_port+IDX_IO_PLAY_FLAGS);
+ outw(DMA_PLAY_SOMETHING1, chip->codec_port+IDX_IO_PLAY_FLAGS);
+ outw(DMA_PLAY_SOMETHING1|DMA_PLAY_SOMETHING2, chip->codec_port+IDX_IO_PLAY_FLAGS);
+ outw(DMA_RESUME|SOMETHING_ALMOST_ALWAYS_SET|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port+IDX_IO_PLAY_FLAGS);
+#endif
+ spin_unlock(&chip->reg_lock);
+
+ /* now unmute WaveOut */
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+
+ snd_azf3328_dbgio(chip, "trigger2");
+ chip->is_playing = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* mute WaveOut */
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+
+ spin_lock(&chip->reg_lock);
+ /* stop playback */
+ status1 = inw(chip->codec_port+IDX_IO_PLAY_FLAGS);
+
+ status1 &= ~DMA_RESUME;
+ outw(status1, chip->codec_port+IDX_IO_PLAY_FLAGS);
+
+ status1 |= DMA_PLAY_SOMETHING1;
+ outw(status1, chip->codec_port+IDX_IO_PLAY_FLAGS);
+
+ status1 &= ~DMA_PLAY_SOMETHING1;
+ outw(status1, chip->codec_port+IDX_IO_PLAY_FLAGS);
+ spin_unlock(&chip->reg_lock);
+
+ /* now unmute WaveOut */
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+ chip->is_playing = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_printk("FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_printk("FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_azf3328_dbgcallleave();
+ return result;
+}
+
+/* this is just analogous to playback; I'm not quite sure whether recording
+ * should actually be triggered like that */
+static int snd_azf3328_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int result = 0;
+ unsigned int status1;
+
+ snd_azf3328_dbgcalls("snd_azf3328_capture_trigger cmd %d\n", cmd);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+
+ snd_azf3328_dbgio(chip, "trigger1");
+
+ snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels);
+
+ spin_lock(&chip->reg_lock);
+ /* stop recording */
+ status1 = inw(chip->codec_port+IDX_IO_REC_FLAGS);
+ status1 &= ~DMA_RESUME;
+ outw(status1, chip->codec_port+IDX_IO_REC_FLAGS);
+
+ /* FIXME: clear interrupts or what??? */
+ outw(0xffff, chip->codec_port+IDX_IO_REC_IRQMASK);
+ spin_unlock(&chip->reg_lock);
+
+ snd_azf3328_setdmaa(chip, runtime->dma_addr, snd_pcm_lib_period_bytes(substream), snd_pcm_lib_buffer_bytes(substream), 1);
+
+ spin_lock(&chip->reg_lock);
+#ifdef WIN9X
+ /* FIXME: enable playback/recording??? */
+ status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2;
+ outw(status1, chip->codec_port+IDX_IO_REC_FLAGS);
+
+ /* start playback again */
+ /* FIXME: what is this value (0x0010)??? */
+ status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
+ outw(status1, chip->codec_port+IDX_IO_REC_FLAGS);
+#else
+ outw(0x00, chip->codec_port+IDX_IO_REC_FLAGS);
+ outw(DMA_PLAY_SOMETHING1, chip->codec_port+IDX_IO_REC_FLAGS);
+ outw(DMA_PLAY_SOMETHING1|DMA_PLAY_SOMETHING2, chip->codec_port+IDX_IO_REC_FLAGS);
+ outw(DMA_RESUME|SOMETHING_ALMOST_ALWAYS_SET|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port+IDX_IO_REC_FLAGS);
+#endif
+ spin_unlock(&chip->reg_lock);
+
+ snd_azf3328_dbgio(chip, "trigger2");
+ chip->is_playing = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ spin_lock(&chip->reg_lock);
+ /* stop recording */
+ status1 = inw(chip->codec_port+IDX_IO_REC_FLAGS);
+
+ status1 &= ~DMA_RESUME;
+ outw(status1, chip->codec_port+IDX_IO_REC_FLAGS);
+
+ status1 |= DMA_PLAY_SOMETHING1;
+ outw(status1, chip->codec_port+IDX_IO_REC_FLAGS);
+
+ status1 &= ~DMA_PLAY_SOMETHING1;
+ outw(status1, chip->codec_port+IDX_IO_REC_FLAGS);
+ spin_unlock(&chip->reg_lock);
+
+ chip->is_playing = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_printk("FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_printk("FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_azf3328_dbgcallleave();
+ return result;
+}
+
+static snd_pcm_uframes_t snd_azf3328_playback_pointer(snd_pcm_substream_t * substream)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ unsigned long bufptr, playptr;
+ unsigned long result;
+ snd_pcm_uframes_t frmres;
+
+#ifdef QUERY_HARDWARE
+ bufptr = inl(chip->codec_port+IDX_IO_PLAY_DMA_START_1);
+#else
+ bufptr = substream->runtime->dma_addr;
+#endif
+ playptr = inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS);
+
+ result = playptr - bufptr;
+ frmres = bytes_to_frames( substream->runtime, result );
+ snd_azf3328_dbgplay("result %lx, playptr %lx (base %x), frames %ld\n", result, playptr, substream->runtime->dma_addr, frmres);
+ return frmres;
+}
+
+static snd_pcm_uframes_t snd_azf3328_capture_pointer(snd_pcm_substream_t * substream)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ unsigned long bufptr, recptr;
+ unsigned long result;
+ snd_pcm_uframes_t frmres;
+
+#ifdef QUERY_HARDWARE
+ bufptr = inl(chip->codec_port+IDX_IO_REC_DMA_START_1);
+#else
+ bufptr = substream->runtime->dma_addr;
+#endif
+ recptr = inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS);
+
+ result = recptr - bufptr;
+ frmres = bytes_to_frames( substream->runtime, result );
+ snd_azf3328_dbgplay("result %lx, rec ptr %lx (base %x), frames %ld\n", result, recptr, substream->runtime->dma_addr, frmres);
+ return frmres;
+}
+
+static irqreturn_t snd_azf3328_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ azf3328_t *chip = dev_id;
+ unsigned int status, which;
+ static unsigned long count;
+
+ status = inw(chip->codec_port+IDX_IO_IRQSTATUS);
+
+ /* fast path out, to ease interrupt sharing */
+ if (!(status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_MPU401|IRQ_SOMEIRQ)))
+ return IRQ_NONE; /* must be interrupt for another device */
+
+ snd_azf3328_dbgplay("Interrupt %ld!\nIDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQMASK %04x, IDX_IO_IRQSTATUS %04x\n", count, inw(chip->codec_port+IDX_IO_PLAY_FLAGS), inw(chip->codec_port+IDX_IO_PLAY_IRQMASK), inw(chip->codec_port+IDX_IO_IRQSTATUS));
+
+ if (status & IRQ_PLAYBACK)
+ {
+ spin_lock(&chip->reg_lock);
+ which = inw(chip->codec_port+IDX_IO_PLAY_IRQMASK);
+ if (which & IRQ_FINISHED_PLAYBUF_1)
+ /* ack IRQ */
+ outw(which | IRQ_FINISHED_PLAYBUF_1, chip->codec_port+IDX_IO_PLAY_IRQMASK);
+ if (which & IRQ_FINISHED_PLAYBUF_2)
+ /* ack IRQ */
+ outw(which | IRQ_FINISHED_PLAYBUF_2, chip->codec_port+IDX_IO_PLAY_IRQMASK);
+ if (which & IRQ_PLAY_SOMETHING)
+ {
+ snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n");
+ }
+ if (chip->pcm && chip->playback_substream)
+ {
+ snd_azf3328_dbgplay("which %x, playptr %lx\n", which, inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS));
+ snd_pcm_period_elapsed(chip->playback_substream);
+ snd_azf3328_dbgplay("period done, playptr %lx.\n", inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS));
+ }
+ else
+ snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
+ spin_unlock(&chip->reg_lock);
+ }
+ if (status & IRQ_RECORDING)
+ {
+ spin_lock(&chip->reg_lock);
+ which = inw(chip->codec_port+IDX_IO_REC_IRQMASK);
+ if (which & IRQ_FINISHED_RECBUF_1)
+ /* ack interrupt */
+ outw(which | IRQ_FINISHED_RECBUF_1, chip->codec_port+IDX_IO_REC_IRQMASK);
+ if (which & IRQ_FINISHED_RECBUF_2)
+ /* ack interrupt */
+ outw(which | IRQ_FINISHED_RECBUF_2, chip->codec_port+IDX_IO_REC_IRQMASK);
+ if (which & IRQ_REC_SOMETHING)
+ {
+ snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n");
+ }
+ if (chip->pcm && chip->capture_substream)
+ {
+ snd_azf3328_dbgplay("which %x, recptr %lx\n", which, inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS));
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(chip->capture_substream);
+ spin_lock(&chip->reg_lock);
+ snd_azf3328_dbgplay("period done, recptr %lx.\n", inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS));
+ }
+ spin_unlock(&chip->reg_lock);
+ }
+ if (status & IRQ_MPU401)
+ snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+ if (status & IRQ_SOMEIRQ)
+ snd_azf3328_dbgplay("azt3328: unknown IRQ type occurred, please report!\n");
+ count++;
+ return IRQ_HANDLED;
+}
+
+/*****************************************************************/
+
+static snd_pcm_hardware_t snd_azf3328_playback =
+{
+ /* FIXME!! Correct? */
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 5512,
+ .rate_max = 64000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 64,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ /* FIXME: maybe that card actually has a FIFO?
+ * Hmm, it seems newer revisions do have one, but we still don't know
+ * its size... */
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_azf3328_capture =
+{
+ /* FIXME */
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 5512,
+ .rate_max = 64000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 64,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+
+static unsigned int snd_azf3328_fixed_rates[] = {
+ 5512, 6620, 8000, 9600, 11025, 16000, 22050, 32000, 44100, 48000, 64000
+};
+static snd_pcm_hw_constraint_list_t snd_azf3328_hw_constraints_rates = {
+ .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
+ .list = snd_azf3328_fixed_rates,
+ .mask = 0,
+};
+
+/*****************************************************************/
+
+static int snd_azf3328_playback_open(snd_pcm_substream_t * substream)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_azf3328_dbgcallenter();
+ chip->playback_substream = substream;
+ runtime->hw = snd_azf3328_playback;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_azf3328_hw_constraints_rates);
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_capture_open(snd_pcm_substream_t * substream)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_azf3328_dbgcallenter();
+ chip->capture_substream = substream;
+ runtime->hw = snd_azf3328_capture;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_azf3328_hw_constraints_rates);
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_playback_close(snd_pcm_substream_t * substream)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_azf3328_dbgcallenter();
+
+ chip->playback_substream = NULL;
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static int snd_azf3328_capture_close(snd_pcm_substream_t * substream)
+{
+ azf3328_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_azf3328_dbgcallenter();
+ chip->capture_substream = NULL;
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+/******************************************************************/
+
+static snd_pcm_ops_t snd_azf3328_playback_ops = {
+ .open = snd_azf3328_playback_open,
+ .close = snd_azf3328_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_azf3328_hw_params,
+ .hw_free = snd_azf3328_hw_free,
+ .prepare = snd_azf3328_playback_prepare,
+ .trigger = snd_azf3328_playback_trigger,
+ .pointer = snd_azf3328_playback_pointer
+};
+
+static snd_pcm_ops_t snd_azf3328_capture_ops = {
+ .open = snd_azf3328_capture_open,
+ .close = snd_azf3328_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_azf3328_hw_params,
+ .hw_free = snd_azf3328_hw_free,
+ .prepare = snd_azf3328_capture_prepare,
+ .trigger = snd_azf3328_capture_trigger,
+ .pointer = snd_azf3328_capture_pointer
+};
+
+static void snd_azf3328_pcm_free(snd_pcm_t *pcm)
+{
+ azf3328_t *chip = pcm->private_data;
+ chip->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_azf3328_pcm(azf3328_t *chip, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ snd_azf3328_dbgcallenter();
+ if ((err = snd_pcm_new(chip->card, "AZF3328 DSP", device, 1, 1, &pcm)) < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_azf3328_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_azf3328_capture_ops);
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_azf3328_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+/******************************************************************/
+
+#ifdef SUPPORT_JOYSTICK
+static int __devinit snd_azf3328_config_joystick(azf3328_t *chip, int dev)
+{
+ struct gameport *gp;
+ struct resource *r;
+
+ if (!joystick[dev])
+ return -ENODEV;
+
+ if (!(r = request_region(0x200, 8, "AZF3328 gameport"))) {
+ printk(KERN_WARNING "azt3328: cannot reserve joystick ports\n");
+ return -EBUSY;
+ }
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "azt3328: cannot allocate memory for gameport\n");
+ release_resource(r);
+ kfree_nocheck(r);
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "AZF3328 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gp->io = 0x200;
+ gameport_set_port_data(gp, r);
+
+ snd_azf3328_io2_write(chip, IDX_IO2_LEGACY_ADDR,
+ snd_azf3328_io2_read(chip, IDX_IO2_LEGACY_ADDR) | LEGACY_JOY);
+
+ gameport_register_port(chip->gameport);
+
+ return 0;
+}
+
+static void snd_azf3328_free_joystick(azf3328_t *chip)
+{
+ if (chip->gameport) {
+ struct resource *r = gameport_get_port_data(chip->gameport);
+
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+ /* disable gameport */
+ snd_azf3328_io2_write(chip, IDX_IO2_LEGACY_ADDR,
+ snd_azf3328_io2_read(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
+ release_resource(r);
+ kfree_nocheck(r);
+ }
+}
+#else
+static inline int snd_azf3328_config_joystick(azf3328_t *chip, int dev) { return -ENOSYS; }
+static inline void snd_azf3328_free_joystick(azf3328_t *chip) { }
+#endif
+
+/******************************************************************/
+
+static int snd_azf3328_free(azf3328_t *chip)
+{
+ if (chip->irq < 0)
+ goto __end_hw;
+
+ /* reset (close) mixer */
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); /* first mute master volume */
+ snd_azf3328_mixer_write(chip, IDX_MIXER_RESET, 0x0, WORD_VALUE);
+
+ /* interrupt setup - mask everything */
+ /* FIXME */
+
+ synchronize_irq(chip->irq);
+ __end_hw:
+ snd_azf3328_free_joystick(chip);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+
+ kfree(chip);
+ return 0;
+}
+
+static int snd_azf3328_dev_free(snd_device_t *device)
+{
+ azf3328_t *chip = device->device_data;
+ return snd_azf3328_free(chip);
+}
+
+#if 0
+/* check whether a bit can be modified */
+static void snd_azf3328_test_bit(unsigned int reg, int bit)
+{
+ unsigned char val, valoff, valon;
+
+ val = inb(reg);
+
+ outb(val & ~(1 << bit), reg);
+ valoff = inb(reg);
+
+ outb(val|(1 << bit), reg);
+ valon = inb(reg);
+
+ outb(val, reg);
+
+ printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n", reg, bit, val, valoff, valon);
+}
+#endif
+
+static int __devinit snd_azf3328_create(snd_card_t * card,
+ struct pci_dev *pci,
+ unsigned long device_type,
+ azf3328_t ** rchip)
+{
+ azf3328_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_azf3328_dev_free,
+ };
+ u16 tmp;
+
+ *rchip = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&chip->reg_lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ /* check if we can restrict PCI DMA transfers to 24 bits */
+ if (pci_set_dma_mask(pci, 0x00ffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) {
+ snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ if ((err = pci_request_regions(pci, "Aztech AZF3328")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ chip->codec_port = pci_resource_start(pci, 0);
+ chip->io2_port = pci_resource_start(pci, 1);
+ chip->mpu_port = pci_resource_start(pci, 2);
+ chip->synth_port = pci_resource_start(pci, 3);
+ chip->mixer_port = pci_resource_start(pci, 4);
+
+ if (request_irq(pci->irq, snd_azf3328_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_azf3328_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
+ synchronize_irq(chip->irq);
+
+ snd_azf3328_dbgmisc("codec_port 0x%lx, io2_port 0x%lx, mpu_port 0x%lx, synth_port 0x%lx, mixer_port 0x%lx, irq %d\n", chip->codec_port, chip->io2_port, chip->mpu_port, chip->synth_port, chip->mixer_port, chip->irq);
+
+ snd_azf3328_dbgmisc("io2 %02x %02x %02x %02x %02x %02x\n", snd_azf3328_io2_read(chip, 0), snd_azf3328_io2_read(chip, 1), snd_azf3328_io2_read(chip, 2), snd_azf3328_io2_read(chip, 3), snd_azf3328_io2_read(chip, 4), snd_azf3328_io2_read(chip, 5));
+
+ for (tmp=0; tmp <= 0x01; tmp += 1)
+ snd_azf3328_dbgmisc("0x%02x: opl 0x%04x, mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, mpu330 0x%04x\n", tmp, inb(0x388 + tmp), inb(0x300 + tmp), inb(0x310 + tmp), inb(0x320 + tmp), inb(0x330 + tmp));
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_azf3328_free(chip);
+ return err;
+ }
+
+ /* create mixer interface & switches */
+ if ((err = snd_azf3328_mixer_new(chip)) < 0)
+ return err;
+
+#if 0
+ /* set very low bitrate to reduce noise and power consumption? */
+ snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, 5512, 8, 1);
+#endif
+
+ /* standard chip init stuff */
+ spin_lock_irq(&chip->reg_lock);
+ outb(DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_PLAY_FLAGS);
+ outb(DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_SOMETHING_FLAGS);
+ outb(DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_REC_FLAGS);
+ outb(0x0, chip->codec_port + IDX_IO_IRQ63H);
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = chip;
+ return 0;
+}
+
+static int __devinit snd_azf3328_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ azf3328_t *chip;
+ opl3_t *opl3;
+ int err;
+
+ snd_azf3328_dbgcallenter();
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0 );
+ if (card == NULL)
+ return -ENOMEM;
+
+ strcpy(card->driver, "AZF3328");
+ strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
+
+ if ((err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401,
+ chip->mpu_port, 1, pci->irq, 0,
+ &chip->rmidi)) < 0) {
+ snd_printk("azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_azf3328_pcm(chip, 0)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (snd_opl3_create(card, chip->synth_port, chip->synth_port+2,
+ OPL3_HW_AUTO, 1, &opl3) < 0) {
+ snd_printk("azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
+ chip->synth_port, chip->synth_port+2 );
+ } else {
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+
+ snd_azf3328_dbgio(chip, "create");
+
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, chip->codec_port, chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+#ifdef MODULE
+ printk(
+"azt3328: Experimental driver for Aztech AZF3328-based soundcards such as PCI168.\n"
+"azt3328: ZERO support from Aztech: you might think hard about future purchase.\n"
+"azt3328: Feel free to contact hw7oshyuv3001@sneakemail.com for bug reports etc.!\n");
+#endif
+
+ if (snd_azf3328_config_joystick(chip, dev) < 0)
+ snd_azf3328_io2_write(chip, IDX_IO2_LEGACY_ADDR,
+ snd_azf3328_io2_read(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
+
+ pci_set_drvdata(pci, card);
+ dev++;
+
+ snd_azf3328_dbgcallleave();
+ return 0;
+}
+
+static void __devexit snd_azf3328_remove(struct pci_dev *pci)
+{
+ snd_azf3328_dbgcallenter();
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+ snd_azf3328_dbgcallleave();
+}
+
+static struct pci_driver driver = {
+ .name = "AZF3328",
+ .id_table = snd_azf3328_ids,
+ .probe = snd_azf3328_probe,
+ .remove = __devexit_p(snd_azf3328_remove),
+};
+
+static int __init alsa_card_azf3328_init(void)
+{
+ int err;
+ snd_azf3328_dbgcallenter();
+ err = pci_module_init(&driver);
+ snd_azf3328_dbgcallleave();
+ return err;
+}
+
+static void __exit alsa_card_azf3328_exit(void)
+{
+ snd_azf3328_dbgcallenter();
+ pci_unregister_driver(&driver);
+ snd_azf3328_dbgcallleave();
+}
+
+module_init(alsa_card_azf3328_init)
+module_exit(alsa_card_azf3328_exit)
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h
new file mode 100644
index 00000000000..7e0e7918097
--- /dev/null
+++ b/sound/pci/azt3328.h
@@ -0,0 +1,165 @@
+#ifndef __SOUND_AZF3328_H
+#define __SOUND_AZF3328_H
+
+/* type argument to use for the I/O functions */
+#define WORD_VALUE 0x1000
+#define DWORD_VALUE 0x2000
+#define BYTE_VALUE 0x4000
+
+/*** main I/O area port indices ***/
+/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
+/* the driver initialisation suggests a layout of 3 main areas:
+ * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe DirectX
+ * timer ???). and probably another area from 0x60 to 0x6f
+ * (IRQ management, power management etc. ???). */
+/* playback area */
+#define IDX_IO_PLAY_FLAGS 0x00
+ /* able to reactivate output after output muting due to 8/16bit
+ * output change, just like 0x0002.
+ * 0x0001 is the only bit that's able to start the DMA counter */
+ #define DMA_RESUME 0x0001 /* paused if cleared ? */
+ /* 0x0002 *temporarily* set during DMA stopping. hmm
+ * both 0x0002 and 0x0004 set in playback setup. */
+ /* able to reactivate output after output muting due to 8/16bit
+ * output change, just like 0x0001. */
+ #define DMA_PLAY_SOMETHING1 0x0002 /* \ alternated (toggled) */
+ /* 0x0004: NOT able to reactivate output */
+ #define DMA_PLAY_SOMETHING2 0x0004 /* / bits */
+ #define SOMETHING_ALMOST_ALWAYS_SET 0x0008 /* ???; can be modified */
+ #define DMA_EPILOGUE_SOMETHING 0x0010
+ #define DMA_SOMETHING_ELSE 0x0020 /* ??? */
+ #define SOMETHING_UNMODIFIABLE 0xffc0 /* unused ? not modifiable */
+#define IDX_IO_PLAY_IRQMASK 0x02
+ /* write back to flags in case flags are set, in order to ACK IRQ in handler
+ * (bit 1 of port 0x64 indicates interrupt for one of these three types)
+ * sometimes in this case it just writes 0xffff to globally ACK all IRQs
+ * settings written are not reflected when reading back, though.
+ * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */
+ #define IRQ_PLAY_SOMETHING 0x0001 /* something & ACK */
+ #define IRQ_FINISHED_PLAYBUF_1 0x0002 /* 1st dmabuf finished & ACK */
+ #define IRQ_FINISHED_PLAYBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
+ #define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
+ #define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
+ #define IRQMASK_UNMODIFIABLE 0xffe0 /* unused ? not modifiable */
+#define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area */
+#define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area */
+#define IDX_IO_PLAY_DMA_LEN_1 0x0c /* length of 1st DMA play area */
+#define IDX_IO_PLAY_DMA_LEN_2 0x0e /* length of 2nd DMA play area */
+#define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position */
+#define IDX_IO_PLAY_DMA_CURROFS 0x14 /* offset within current DMA play area */
+#define IDX_IO_PLAY_SOUNDFORMAT 0x16
+ /* all unspecified bits can't be modified */
+ #define SOUNDFORMAT_FREQUENCY_MASK 0x000f
+ /* all _SUSPECTED_ values are not used by Windows drivers, so we don't
+ * have any hard facts, only rough measurements */
+ #define SOUNDFORMAT_FREQ_SUSPECTED_4000 0x0c
+ #define SOUNDFORMAT_FREQ_SUSPECTED_4800 0x0a
+ #define SOUNDFORMAT_FREQ_5510 0x0d
+ #define SOUNDFORMAT_FREQ_6620 0x0b
+ #define SOUNDFORMAT_FREQ_8000 0x00 /* also 0x0e ? */
+ #define SOUNDFORMAT_FREQ_9600 0x08
+ #define SOUNDFORMAT_FREQ_SUSPECTED_12000 0x09
+ #define SOUNDFORMAT_FREQ_11025 0x01 /* also 0x0f ? */
+ #define SOUNDFORMAT_FREQ_16000 0x02
+ #define SOUNDFORMAT_FREQ_22050 0x03
+ #define SOUNDFORMAT_FREQ_32000 0x04
+ #define SOUNDFORMAT_FREQ_44100 0x05
+ #define SOUNDFORMAT_FREQ_48000 0x06
+ #define SOUNDFORMAT_FREQ_SUSPECTED_64000 0x07
+ #define SOUNDFORMAT_FLAG_16BIT 0x0010
+ #define SOUNDFORMAT_FLAG_2CHANNELS 0x0020
+/* recording area (see also: playback bit flag definitions) */
+#define IDX_IO_REC_FLAGS 0x20 /* ?? */
+#define IDX_IO_REC_IRQMASK 0x22 /* ?? */
+ #define IRQ_REC_SOMETHING 0x0001 /* something & ACK */
+ #define IRQ_FINISHED_RECBUF_1 0x0002 /* 1st dmabuf finished & ACK */
+ #define IRQ_FINISHED_RECBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
+ /* hmm, maybe these are just the corresponding *recording* flags ?
+ * but OTOH they are most likely at port 0x22 instead */
+ #define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
+ #define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
+#define IDX_IO_REC_DMA_START_1 0x24
+#define IDX_IO_REC_DMA_START_2 0x28
+#define IDX_IO_REC_DMA_LEN_1 0x2c
+#define IDX_IO_REC_DMA_LEN_2 0x2e
+#define IDX_IO_REC_DMA_CURRPOS 0x30
+#define IDX_IO_REC_DMA_CURROFS 0x34
+#define IDX_IO_REC_SOUNDFORMAT 0x36
+/* some third area ? (after playback and recording) */
+#define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init */
+/* general */
+#define IDX_IO_60H 0x60 /* writing 0xffff returns 0xffff */
+#define IDX_IO_62H 0x62 /* writing to WORD 0x0062 can hang the box ! --> responsible for IRQ management as a whole ?? */
+#define IDX_IO_IRQ63H 0x63 /* FIXME !! */
+ #define IO_IRQ63H_SOMETHING 0x04 /* being set in IRQ handler in case port 0x00 had 0x0020 set upon IRQ handler */
+#define IDX_IO_IRQSTATUS 0x64
+ #define IRQ_PLAYBACK 0x0001
+ #define IRQ_RECORDING 0x0002
+ #define IRQ_MPU401 0x0010
+ #define IRQ_SOMEIRQ 0x0020 /* ???? */
+ #define IRQ_WHO_KNOWS_UNUSED 0x00e0 /* probably unused */
+#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
+#define IDX_IO_SOME_VALUE 0x68 /* this is always set to 0x3ff, and writable; maybe some buffer limit, but I couldn't find out more */
+#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated; actually inhibits PCM playback !!! maybe power management ?? */
+#define IDX_IO_6CH 0x6C /* this WORD can have all its bits activated ? */
+#define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */
+/* further I/O indices not saved/restored, so probably not used */
+
+/*** I/O 2 area port indices ***/
+/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
+#define IDX_IO2_LEGACY_ADDR 0x04
+ #define LEGACY_SOMETHING 0x01 /* OPL3 ?? */
+ #define LEGACY_JOY 0x08
+
+/*** mixer I/O area port indices ***/
+/* (only 0x22 of 0x40 bytes saved/restored by Windows driver)
+ * generally spoken: AC97 register index = AZF3328 mixer reg index + 2
+ * (in other words: AZF3328 NOT fully AC97 compliant) */
+ #define MIXER_VOLUME_RIGHT_MASK 0x001f
+ #define MIXER_VOLUME_LEFT_MASK 0x1f00
+ #define MIXER_MUTE_MASK 0x8000
+#define IDX_MIXER_RESET 0x00 /* does NOT seem to have AC97 ID bits */
+#define IDX_MIXER_PLAY_MASTER 0x02
+#define IDX_MIXER_MODEMOUT 0x04
+#define IDX_MIXER_BASSTREBLE 0x06
+ #define MIXER_BASSTREBLE_TREBLE_VOLUME_MASK 0x000e
+ #define MIXER_BASSTREBLE_BASS_VOLUME_MASK 0x0e00
+#define IDX_MIXER_PCBEEP 0x08
+#define IDX_MIXER_MODEMIN 0x0a
+#define IDX_MIXER_MIC 0x0c
+ #define MIXER_MIC_MICGAIN_20DB_ENHANCEMENT_MASK 0x0040
+#define IDX_MIXER_LINEIN 0x0e
+#define IDX_MIXER_CDAUDIO 0x10
+#define IDX_MIXER_VIDEO 0x12
+#define IDX_MIXER_AUX 0x14
+#define IDX_MIXER_WAVEOUT 0x16
+#define IDX_MIXER_FMSYNTH 0x18
+#define IDX_MIXER_REC_SELECT 0x1a
+ #define MIXER_REC_SELECT_MIC 0x00
+ #define MIXER_REC_SELECT_CD 0x01
+ #define MIXER_REC_SELECT_VIDEO 0x02
+ #define MIXER_REC_SELECT_AUX 0x03
+ #define MIXER_REC_SELECT_LINEIN 0x04
+ #define MIXER_REC_SELECT_MIXSTEREO 0x05
+ #define MIXER_REC_SELECT_MIXMONO 0x06
+ #define MIXER_REC_SELECT_MONOIN 0x07
+#define IDX_MIXER_REC_VOLUME 0x1c
+#define IDX_MIXER_ADVCTL1 0x1e
+ /* unlisted bits are unmodifiable */
+ #define MIXER_ADVCTL1_3DWIDTH_MASK 0x000e
+ #define MIXER_ADVCTL1_HIFI3D_MASK 0x0300
+#define IDX_MIXER_ADVCTL2 0x20 /* resembles AC97_GENERAL_PURPOSE reg ! */
+ /* unlisted bits are unmodifiable */
+ #define MIXER_ADVCTL2_BIT7 0x0080 /* WaveOut 3D Bypass ? mutes WaveOut at LineOut */
+ #define MIXER_ADVCTL2_BIT8 0x0100 /* is this Modem Out Select ? */
+ #define MIXER_ADVCTL2_BIT9 0x0200 /* Mono Select Source ? */
+ #define MIXER_ADVCTL2_BIT13 0x2000 /* 3D enable ? */
+ #define MIXER_ADVCTL2_BIT15 0x8000 /* unknown */
+
+#define IDX_MIXER_SOMETHING30H 0x30 /* used, but unknown ??? */
+
+/* driver internal flags */
+#define SET_CHAN_LEFT 1
+#define SET_CHAN_RIGHT 2
+
+#endif /* __SOUND_AZF3328_H */
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
new file mode 100644
index 00000000000..89a7ffe5e7d
--- /dev/null
+++ b/sound/pci/bt87x.c
@@ -0,0 +1,930 @@
+/*
+ * bt87x.c - Brooktree Bt878/Bt879 driver for ALSA
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ * based on btaudio.c by Gerd Knorr <kraxel@bytesex.org>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("Brooktree Bt87x audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Brooktree,Bt878},"
+ "{Brooktree,Bt879}}");
+
+static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int digital_rate[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* digital input rate */
+static int load_all; /* allow to load the non-whitelisted cards */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Bt87x soundcard");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Bt87x soundcard");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Bt87x soundcard");
+module_param_array(digital_rate, int, NULL, 0444);
+MODULE_PARM_DESC(digital_rate, "Digital input rate for Bt87x soundcard");
+module_param(load_all, bool, 0444);
+MODULE_PARM_DESC(load_all, "Allow to load the non-whitelisted cards");
+
+
+#ifndef PCI_VENDOR_ID_BROOKTREE
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#endif
+#ifndef PCI_DEVICE_ID_BROOKTREE_878
+#define PCI_DEVICE_ID_BROOKTREE_878 0x0878
+#endif
+#ifndef PCI_DEVICE_ID_BROOKTREE_879
+#define PCI_DEVICE_ID_BROOKTREE_879 0x0879
+#endif
+
+/* register offsets */
+#define REG_INT_STAT 0x100 /* interrupt status */
+#define REG_INT_MASK 0x104 /* interrupt mask */
+#define REG_GPIO_DMA_CTL 0x10c /* audio control */
+#define REG_PACKET_LEN 0x110 /* audio packet lengths */
+#define REG_RISC_STRT_ADD 0x114 /* RISC program start address */
+#define REG_RISC_COUNT 0x120 /* RISC program counter */
+
+/* interrupt bits */
+#define INT_OFLOW (1 << 3) /* audio A/D overflow */
+#define INT_RISCI (1 << 11) /* RISC instruction IRQ bit set */
+#define INT_FBUS (1 << 12) /* FIFO overrun due to bus access latency */
+#define INT_FTRGT (1 << 13) /* FIFO overrun due to target latency */
+#define INT_FDSR (1 << 14) /* FIFO data stream resynchronization */
+#define INT_PPERR (1 << 15) /* PCI parity error */
+#define INT_RIPERR (1 << 16) /* RISC instruction parity error */
+#define INT_PABORT (1 << 17) /* PCI master or target abort */
+#define INT_OCERR (1 << 18) /* invalid opcode */
+#define INT_SCERR (1 << 19) /* sync counter overflow */
+#define INT_RISC_EN (1 << 27) /* DMA controller running */
+#define INT_RISCS_SHIFT 28 /* RISC status bits */
+
+/* audio control bits */
+#define CTL_FIFO_ENABLE (1 << 0) /* enable audio data FIFO */
+#define CTL_RISC_ENABLE (1 << 1) /* enable audio DMA controller */
+#define CTL_PKTP_4 (0 << 2) /* packet mode FIFO trigger point - 4 DWORDs */
+#define CTL_PKTP_8 (1 << 2) /* 8 DWORDs */
+#define CTL_PKTP_16 (2 << 2) /* 16 DWORDs */
+#define CTL_ACAP_EN (1 << 4) /* enable audio capture */
+#define CTL_DA_APP (1 << 5) /* GPIO input */
+#define CTL_DA_IOM_AFE (0 << 6) /* audio A/D input */
+#define CTL_DA_IOM_DA (1 << 6) /* digital audio input */
+#define CTL_DA_SDR_SHIFT 8 /* DDF first stage decimation rate */
+#define CTL_DA_SDR_MASK (0xf<< 8)
+#define CTL_DA_LMT (1 << 12) /* limit audio data values */
+#define CTL_DA_ES2 (1 << 13) /* enable DDF stage 2 */
+#define CTL_DA_SBR (1 << 14) /* samples rounded to 8 bits */
+#define CTL_DA_DPM (1 << 15) /* data packet mode */
+#define CTL_DA_LRD_SHIFT 16 /* ALRCK delay */
+#define CTL_DA_MLB (1 << 21) /* MSB/LSB format */
+#define CTL_DA_LRI (1 << 22) /* left/right indication */
+#define CTL_DA_SCE (1 << 23) /* sample clock edge */
+#define CTL_A_SEL_STV (0 << 24) /* TV tuner audio input */
+#define CTL_A_SEL_SFM (1 << 24) /* FM audio input */
+#define CTL_A_SEL_SML (2 << 24) /* mic/line audio input */
+#define CTL_A_SEL_SMXC (3 << 24) /* MUX bypass */
+#define CTL_A_SEL_SHIFT 24
+#define CTL_A_SEL_MASK (3 << 24)
+#define CTL_A_PWRDN (1 << 26) /* analog audio power-down */
+#define CTL_A_G2X (1 << 27) /* audio gain boost */
+#define CTL_A_GAIN_SHIFT 28 /* audio input gain */
+#define CTL_A_GAIN_MASK (0xf<<28)
+
+/* RISC instruction opcodes */
+#define RISC_WRITE (0x1 << 28) /* write FIFO data to memory at address */
+#define RISC_WRITEC (0x5 << 28) /* write FIFO data to memory at current address */
+#define RISC_SKIP (0x2 << 28) /* skip FIFO data */
+#define RISC_JUMP (0x7 << 28) /* jump to address */
+#define RISC_SYNC (0x8 << 28) /* synchronize with FIFO */
+
+/* RISC instruction bits */
+#define RISC_BYTES_ENABLE (0xf << 12) /* byte enable bits */
+#define RISC_RESYNC ( 1 << 15) /* disable FDSR errors */
+#define RISC_SET_STATUS_SHIFT 16 /* set status bits */
+#define RISC_RESET_STATUS_SHIFT 20 /* clear status bits */
+#define RISC_IRQ ( 1 << 24) /* interrupt */
+#define RISC_EOL ( 1 << 26) /* end of line */
+#define RISC_SOL ( 1 << 27) /* start of line */
+
+/* SYNC status bits values */
+#define RISC_SYNC_FM1 0x6
+#define RISC_SYNC_VRO 0xc
+
+#define ANALOG_CLOCK 1792000
+#ifdef CONFIG_SND_BT87X_OVERCLOCK
+#define CLOCK_DIV_MIN 1
+#else
+#define CLOCK_DIV_MIN 4
+#endif
+#define CLOCK_DIV_MAX 15
+
+#define ERROR_INTERRUPTS (INT_FBUS | INT_FTRGT | INT_PPERR | \
+ INT_RIPERR | INT_PABORT | INT_OCERR)
+#define MY_INTERRUPTS (INT_RISCI | ERROR_INTERRUPTS)
+
+/* SYNC, one WRITE per line, one extra WRITE per page boundary, SYNC, JUMP */
+#define MAX_RISC_SIZE ((1 + 255 + (PAGE_ALIGN(255 * 4092) / PAGE_SIZE - 1) + 1 + 1) * 8)
+
+typedef struct snd_bt87x bt87x_t;
+struct snd_bt87x {
+ snd_card_t *card;
+ struct pci_dev *pci;
+
+ void __iomem *mmio;
+ int irq;
+
+ int dig_rate;
+
+ spinlock_t reg_lock;
+ long opened;
+ snd_pcm_substream_t *substream;
+
+ struct snd_dma_buffer dma_risc;
+ unsigned int line_bytes;
+ unsigned int lines;
+
+ u32 reg_control;
+ u32 interrupt_mask;
+
+ int current_line;
+
+ int pci_parity_errors;
+};
+
+enum { DEVICE_DIGITAL, DEVICE_ANALOG };
+
+static inline u32 snd_bt87x_readl(bt87x_t *chip, u32 reg)
+{
+ return readl(chip->mmio + reg);
+}
+
+static inline void snd_bt87x_writel(bt87x_t *chip, u32 reg, u32 value)
+{
+ writel(value, chip->mmio + reg);
+}
+
+static int snd_bt87x_create_risc(bt87x_t *chip, snd_pcm_substream_t *substream,
+ unsigned int periods, unsigned int period_bytes)
+{
+ struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+ unsigned int i, offset;
+ u32 *risc;
+
+ if (chip->dma_risc.area == NULL) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0)
+ return -ENOMEM;
+ }
+ risc = (u32 *)chip->dma_risc.area;
+ offset = 0;
+ *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_FM1);
+ *risc++ = cpu_to_le32(0);
+ for (i = 0; i < periods; ++i) {
+ u32 rest;
+
+ rest = period_bytes;
+ do {
+ u32 cmd, len;
+
+ len = PAGE_SIZE - (offset % PAGE_SIZE);
+ if (len > rest)
+ len = rest;
+ cmd = RISC_WRITE | len;
+ if (rest == period_bytes) {
+ u32 block = i * 16 / periods;
+ cmd |= RISC_SOL;
+ cmd |= block << RISC_SET_STATUS_SHIFT;
+ cmd |= (~block & 0xf) << RISC_RESET_STATUS_SHIFT;
+ }
+ if (len == rest)
+ cmd |= RISC_EOL | RISC_IRQ;
+ *risc++ = cpu_to_le32(cmd);
+ *risc++ = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, offset));
+ offset += len;
+ rest -= len;
+ } while (rest > 0);
+ }
+ *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_VRO);
+ *risc++ = cpu_to_le32(0);
+ *risc++ = cpu_to_le32(RISC_JUMP);
+ *risc++ = cpu_to_le32(chip->dma_risc.addr);
+ chip->line_bytes = period_bytes;
+ chip->lines = periods;
+ return 0;
+}
+
+static void snd_bt87x_free_risc(bt87x_t *chip)
+{
+ if (chip->dma_risc.area) {
+ snd_dma_free_pages(&chip->dma_risc);
+ chip->dma_risc.area = NULL;
+ }
+}
+
+static void snd_bt87x_pci_error(bt87x_t *chip, unsigned int status)
+{
+ u16 pci_status;
+
+ pci_read_config_word(chip->pci, PCI_STATUS, &pci_status);
+ pci_status &= PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT |
+ PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
+ PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY;
+ pci_write_config_word(chip->pci, PCI_STATUS, pci_status);
+ if (pci_status != PCI_STATUS_DETECTED_PARITY)
+ snd_printk(KERN_ERR "Aieee - PCI error! status %#08x, PCI status %#04x\n",
+ status & ERROR_INTERRUPTS, pci_status);
+ else {
+ snd_printk(KERN_ERR "Aieee - PCI parity error detected!\n");
+ /* error 'handling' similar to aic7xxx_pci.c: */
+ chip->pci_parity_errors++;
+ if (chip->pci_parity_errors > 20) {
+ snd_printk(KERN_ERR "Too many PCI parity errors observed.\n");
+ snd_printk(KERN_ERR "Some device on this bus is generating bad parity.\n");
+ snd_printk(KERN_ERR "This is an error *observed by*, not *generated by*, this card.\n");
+ snd_printk(KERN_ERR "PCI parity error checking has been disabled.\n");
+ chip->interrupt_mask &= ~(INT_PPERR | INT_RIPERR);
+ snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
+ }
+ }
+}
+
+static irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ bt87x_t *chip = dev_id;
+ unsigned int status, irq_status;
+
+ status = snd_bt87x_readl(chip, REG_INT_STAT);
+ irq_status = status & chip->interrupt_mask;
+ if (!irq_status)
+ return IRQ_NONE;
+ snd_bt87x_writel(chip, REG_INT_STAT, irq_status);
+
+ if (irq_status & ERROR_INTERRUPTS) {
+ if (irq_status & (INT_FBUS | INT_FTRGT))
+ snd_printk(KERN_WARNING "FIFO overrun, status %#08x\n", status);
+ if (irq_status & INT_OCERR)
+ snd_printk(KERN_ERR "internal RISC error, status %#08x\n", status);
+ if (irq_status & (INT_PPERR | INT_RIPERR | INT_PABORT))
+ snd_bt87x_pci_error(chip, irq_status);
+ }
+ if ((irq_status & INT_RISCI) && (chip->reg_control & CTL_ACAP_EN)) {
+ int current_block, irq_block;
+
+ /* assume that exactly one line has been recorded */
+ chip->current_line = (chip->current_line + 1) % chip->lines;
+ /* but check if some interrupts have been skipped */
+ current_block = chip->current_line * 16 / chip->lines;
+ irq_block = status >> INT_RISCS_SHIFT;
+ if (current_block != irq_block)
+ chip->current_line = (irq_block * chip->lines + 15) / 16;
+
+ snd_pcm_period_elapsed(chip->substream);
+ }
+ return IRQ_HANDLED;
+}
+
+static snd_pcm_hardware_t snd_bt87x_digital_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = 0, /* set at runtime */
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 255 * 4092,
+ .period_bytes_min = 32,
+ .period_bytes_max = 4092,
+ .periods_min = 2,
+ .periods_max = 255,
+};
+
+static snd_pcm_hardware_t snd_bt87x_analog_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = ANALOG_CLOCK / CLOCK_DIV_MAX,
+ .rate_max = ANALOG_CLOCK / CLOCK_DIV_MIN,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 255 * 4092,
+ .period_bytes_min = 32,
+ .period_bytes_max = 4092,
+ .periods_min = 2,
+ .periods_max = 255,
+};
+
+static int snd_bt87x_set_digital_hw(bt87x_t *chip, snd_pcm_runtime_t *runtime)
+{
+ static struct {
+ int rate;
+ unsigned int bit;
+ } ratebits[] = {
+ {8000, SNDRV_PCM_RATE_8000},
+ {11025, SNDRV_PCM_RATE_11025},
+ {16000, SNDRV_PCM_RATE_16000},
+ {22050, SNDRV_PCM_RATE_22050},
+ {32000, SNDRV_PCM_RATE_32000},
+ {44100, SNDRV_PCM_RATE_44100},
+ {48000, SNDRV_PCM_RATE_48000}
+ };
+ int i;
+
+ chip->reg_control |= CTL_DA_IOM_DA;
+ runtime->hw = snd_bt87x_digital_hw;
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+ for (i = 0; i < ARRAY_SIZE(ratebits); ++i)
+ if (chip->dig_rate == ratebits[i].rate) {
+ runtime->hw.rates = ratebits[i].bit;
+ break;
+ }
+ runtime->hw.rate_min = chip->dig_rate;
+ runtime->hw.rate_max = chip->dig_rate;
+ return 0;
+}
+
+static int snd_bt87x_set_analog_hw(bt87x_t *chip, snd_pcm_runtime_t *runtime)
+{
+ static ratnum_t analog_clock = {
+ .num = ANALOG_CLOCK,
+ .den_min = CLOCK_DIV_MIN,
+ .den_max = CLOCK_DIV_MAX,
+ .den_step = 1
+ };
+ static snd_pcm_hw_constraint_ratnums_t constraint_rates = {
+ .nrats = 1,
+ .rats = &analog_clock
+ };
+
+ chip->reg_control &= ~CTL_DA_IOM_DA;
+ runtime->hw = snd_bt87x_analog_hw;
+ return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraint_rates);
+}
+
+static int snd_bt87x_pcm_open(snd_pcm_substream_t *substream)
+{
+ bt87x_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if (test_and_set_bit(0, &chip->opened))
+ return -EBUSY;
+
+ if (substream->pcm->device == DEVICE_DIGITAL)
+ err = snd_bt87x_set_digital_hw(chip, runtime);
+ else
+ err = snd_bt87x_set_analog_hw(chip, runtime);
+ if (err < 0)
+ goto _error;
+
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+ return 0;
+
+_error:
+ clear_bit(0, &chip->opened);
+ smp_mb__after_clear_bit();
+ return err;
+}
+
+static int snd_bt87x_close(snd_pcm_substream_t *substream)
+{
+ bt87x_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->substream = NULL;
+ clear_bit(0, &chip->opened);
+ smp_mb__after_clear_bit();
+ return 0;
+}
+
+static int snd_bt87x_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *hw_params)
+{
+ bt87x_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+ return snd_bt87x_create_risc(chip, substream,
+ params_periods(hw_params),
+ params_period_bytes(hw_params));
+}
+
+static int snd_bt87x_hw_free(snd_pcm_substream_t *substream)
+{
+ bt87x_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_bt87x_free_risc(chip);
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_bt87x_prepare(snd_pcm_substream_t *substream)
+{
+ bt87x_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int decimation;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR);
+ decimation = (ANALOG_CLOCK + runtime->rate / 4) / runtime->rate;
+ chip->reg_control |= decimation << CTL_DA_SDR_SHIFT;
+ if (runtime->format == SNDRV_PCM_FORMAT_S8)
+ chip->reg_control |= CTL_DA_SBR;
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_bt87x_start(bt87x_t *chip)
+{
+ spin_lock(&chip->reg_lock);
+ chip->current_line = 0;
+ chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN;
+ snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr);
+ snd_bt87x_writel(chip, REG_PACKET_LEN,
+ chip->line_bytes | (chip->lines << 16));
+ snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_bt87x_stop(bt87x_t *chip)
+{
+ spin_lock(&chip->reg_lock);
+ chip->reg_control &= ~(CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN);
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ snd_bt87x_writel(chip, REG_INT_MASK, 0);
+ snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_bt87x_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ bt87x_t *chip = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ return snd_bt87x_start(chip);
+ case SNDRV_PCM_TRIGGER_STOP:
+ return snd_bt87x_stop(chip);
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t snd_bt87x_pointer(snd_pcm_substream_t *substream)
+{
+ bt87x_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ return (snd_pcm_uframes_t)bytes_to_frames(runtime, chip->current_line * chip->line_bytes);
+}
+
+static snd_pcm_ops_t snd_bt87x_pcm_ops = {
+ .open = snd_bt87x_pcm_open,
+ .close = snd_bt87x_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_bt87x_hw_params,
+ .hw_free = snd_bt87x_hw_free,
+ .prepare = snd_bt87x_prepare,
+ .trigger = snd_bt87x_trigger,
+ .pointer = snd_bt87x_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static int snd_bt87x_capture_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = 0;
+ info->value.integer.max = 15;
+ return 0;
+}
+
+static int snd_bt87x_capture_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+
+ value->value.integer.value[0] = (chip->reg_control & CTL_A_GAIN_MASK) >> CTL_A_GAIN_SHIFT;
+ return 0;
+}
+
+static int snd_bt87x_capture_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+ u32 old_control;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_control = chip->reg_control;
+ chip->reg_control = (chip->reg_control & ~CTL_A_GAIN_MASK)
+ | (value->value.integer.value[0] << CTL_A_GAIN_SHIFT);
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ changed = old_control != chip->reg_control;
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
+
+static snd_kcontrol_new_t snd_bt87x_capture_volume = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .info = snd_bt87x_capture_volume_info,
+ .get = snd_bt87x_capture_volume_get,
+ .put = snd_bt87x_capture_volume_put,
+};
+
+static int snd_bt87x_capture_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ info->count = 1;
+ info->value.integer.min = 0;
+ info->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_bt87x_capture_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+
+ value->value.integer.value[0] = !! (chip->reg_control & CTL_A_G2X);
+ return 0;
+}
+
+static int snd_bt87x_capture_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+ u32 old_control;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_control = chip->reg_control;
+ chip->reg_control = (chip->reg_control & ~CTL_A_G2X)
+ | (value->value.integer.value[0] ? CTL_A_G2X : 0);
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ changed = chip->reg_control != old_control;
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
+
+static snd_kcontrol_new_t snd_bt87x_capture_boost = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Boost",
+ .info = snd_bt87x_capture_boost_info,
+ .get = snd_bt87x_capture_boost_get,
+ .put = snd_bt87x_capture_boost_put,
+};
+
+static int snd_bt87x_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+ static char *texts[3] = {"TV Tuner", "FM", "Mic/Line"};
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 3;
+ if (info->value.enumerated.item > 2)
+ info->value.enumerated.item = 2;
+ strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_bt87x_capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+
+ value->value.enumerated.item[0] = (chip->reg_control & CTL_A_SEL_MASK) >> CTL_A_SEL_SHIFT;
+ return 0;
+}
+
+static int snd_bt87x_capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+ u32 old_control;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_control = chip->reg_control;
+ chip->reg_control = (chip->reg_control & ~CTL_A_SEL_MASK)
+ | (value->value.enumerated.item[0] << CTL_A_SEL_SHIFT);
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ changed = chip->reg_control != old_control;
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
+
+static snd_kcontrol_new_t snd_bt87x_capture_source = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_bt87x_capture_source_info,
+ .get = snd_bt87x_capture_source_get,
+ .put = snd_bt87x_capture_source_put,
+};
+
+static int snd_bt87x_free(bt87x_t *chip)
+{
+ if (chip->mmio) {
+ snd_bt87x_stop(chip);
+ if (chip->irq >= 0)
+ synchronize_irq(chip->irq);
+
+ iounmap(chip->mmio);
+ }
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_bt87x_dev_free(snd_device_t *device)
+{
+ bt87x_t *chip = device->device_data;
+ return snd_bt87x_free(chip);
+}
+
+static int __devinit snd_bt87x_pcm(bt87x_t *chip, int device, char *name)
+{
+ int err;
+ snd_pcm_t *pcm;
+
+ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = chip;
+ strcpy(pcm->name, name);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops);
+ return snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 128 * 1024,
+ (255 * 4092 + 1023) & ~1023);
+}
+
+static int __devinit snd_bt87x_create(snd_card_t *card,
+ struct pci_dev *pci,
+ bt87x_t **rchip)
+{
+ bt87x_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_bt87x_dev_free
+ };
+
+ *rchip = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ spin_lock_init(&chip->reg_lock);
+
+ if ((err = pci_request_regions(pci, "Bt87x audio")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->mmio = ioremap_nocache(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ if (!chip->mmio) {
+ snd_bt87x_free(chip);
+ snd_printk(KERN_ERR "cannot remap io memory\n");
+ return -ENOMEM;
+ }
+
+ chip->reg_control = CTL_DA_ES2 | CTL_PKTP_16 | (15 << CTL_DA_SDR_SHIFT);
+ chip->interrupt_mask = MY_INTERRUPTS;
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ snd_bt87x_writel(chip, REG_INT_MASK, 0);
+ snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
+
+ if (request_irq(pci->irq, snd_bt87x_interrupt, SA_INTERRUPT | SA_SHIRQ,
+ "Bt87x audio", chip)) {
+ snd_bt87x_free(chip);
+ snd_printk(KERN_ERR "cannot grab irq\n");
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
+ synchronize_irq(chip->irq);
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_bt87x_free(chip);
+ return err;
+ }
+ snd_card_set_dev(card, &pci->dev);
+ *rchip = chip;
+ return 0;
+}
+
+#define BT_DEVICE(chip, subvend, subdev, rate) \
+ { .vendor = PCI_VENDOR_ID_BROOKTREE, \
+ .device = PCI_DEVICE_ID_BROOKTREE_##chip, \
+ .subvendor = subvend, .subdevice = subdev, \
+ .driver_data = rate }
+
+/* driver_data is the default digital_rate value for that device */
+static struct pci_device_id snd_bt87x_ids[] = {
+ BT_DEVICE(878, 0x0070, 0x13eb, 32000), /* Hauppauge WinTV series */
+ BT_DEVICE(879, 0x0070, 0x13eb, 32000), /* Hauppauge WinTV series */
+ BT_DEVICE(878, 0x0070, 0xff01, 44100), /* Viewcast Osprey 200 */
+ { }
+};
+MODULE_DEVICE_TABLE(pci, snd_bt87x_ids);
+
+/* cards known not to have audio
+ * (DVB cards use the audio function to transfer MPEG data) */
+static struct {
+ unsigned short subvendor, subdevice;
+} blacklist[] __devinitdata = {
+ {0x0071, 0x0101}, /* Nebula Electronics DigiTV */
+ {0x11bd, 0x0026}, /* Pinnacle PCTV SAT CI */
+ {0x1461, 0x0761}, /* AVermedia AverTV DVB-T */
+ {0x1461, 0x0771}, /* AVermedia DVB-T 771 */
+ {0x1822, 0x0001}, /* Twinhan VisionPlus DVB-T */
+ {0x18ac, 0xdb10}, /* DVICO FusionHDTV DVB-T Lite */
+ {0x270f, 0xfc00}, /* Chaintech Digitop DST-1000 DVB-S */
+};
+
+/* return the rate of the card, or a negative value if it's blacklisted */
+static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
+{
+ int i;
+ const struct pci_device_id *supported;
+
+ supported = pci_match_device(snd_bt87x_ids, pci);
+ if (supported)
+ return supported->driver_data;
+
+ for (i = 0; i < ARRAY_SIZE(blacklist); ++i)
+ if (blacklist[i].subvendor == pci->subsystem_vendor &&
+ blacklist[i].subdevice == pci->subsystem_device) {
+ snd_printdd(KERN_INFO "card %#04x:%#04x has no audio\n",
+ pci->subsystem_vendor, pci->subsystem_device);
+ return -EBUSY;
+ }
+
+ snd_printk(KERN_INFO "unknown card %#04x:%#04x, using default rate 32000\n",
+ pci->subsystem_vendor, pci->subsystem_device);
+ snd_printk(KERN_DEBUG "please mail id, board name, and, "
+ "if it works, the correct digital_rate option to "
+ "<alsa-devel@lists.sf.net>\n");
+ return 32000; /* default rate */
+}
+
+static int __devinit snd_bt87x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ bt87x_t *chip;
+ int err, rate;
+
+ rate = pci_id->driver_data;
+ if (! rate)
+ if ((rate = snd_bt87x_detect_card(pci)) <= 0)
+ return -ENODEV;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ ++dev;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (!card)
+ return -ENOMEM;
+
+ err = snd_bt87x_create(card, pci, &chip);
+ if (err < 0)
+ goto _error;
+
+ if (digital_rate[dev] > 0)
+ chip->dig_rate = digital_rate[dev];
+ else
+ chip->dig_rate = rate;
+
+ err = snd_bt87x_pcm(chip, DEVICE_DIGITAL, "Bt87x Digital");
+ if (err < 0)
+ goto _error;
+ err = snd_bt87x_pcm(chip, DEVICE_ANALOG, "Bt87x Analog");
+ if (err < 0)
+ goto _error;
+
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_bt87x_capture_volume, chip));
+ if (err < 0)
+ goto _error;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_bt87x_capture_boost, chip));
+ if (err < 0)
+ goto _error;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_bt87x_capture_source, chip));
+ if (err < 0)
+ goto _error;
+
+ strcpy(card->driver, "Bt87x");
+ sprintf(card->shortname, "Brooktree Bt%x", pci->device);
+ sprintf(card->longname, "%s at %#lx, irq %i",
+ card->shortname, pci_resource_start(pci, 0), chip->irq);
+ strcpy(card->mixername, "Bt87x");
+
+ 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 snd_bt87x_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/* default entries for all Bt87x cards - it's not exported */
+/* driver_data is set to 0 to call detection */
+static struct pci_device_id snd_bt87x_default_ids[] = {
+ BT_DEVICE(878, PCI_ANY_ID, PCI_ANY_ID, 0),
+ BT_DEVICE(879, PCI_ANY_ID, PCI_ANY_ID, 0),
+ { }
+};
+
+static struct pci_driver driver = {
+ .name = "Bt87x",
+ .id_table = snd_bt87x_ids,
+ .probe = snd_bt87x_probe,
+ .remove = __devexit_p(snd_bt87x_remove),
+};
+
+static int __init alsa_card_bt87x_init(void)
+{
+ if (load_all)
+ driver.id_table = snd_bt87x_default_ids;
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_bt87x_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_bt87x_init)
+module_exit(alsa_card_bt87x_exit)
diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile
new file mode 100644
index 00000000000..89c6ceee21f
--- /dev/null
+++ b/sound/pci/ca0106/Makefile
@@ -0,0 +1,3 @@
+snd-ca0106-objs := ca0106_main.o ca0106_proc.o ca0106_mixer.o
+
+obj-$(CONFIG_SND_CA0106) += snd-ca0106.o
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
new file mode 100644
index 00000000000..deb02885105
--- /dev/null
+++ b/sound/pci/ca0106/ca0106.h
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ * Version: 0.0.20
+ *
+ * FEATURES currently supported:
+ * See ca0106_main.c for features.
+ *
+ * Changelog:
+ * Support interrupts per period.
+ * Removed noise from Center/LFE channel when in Analog mode.
+ * Rename and remove mixer controls.
+ * 0.0.6
+ * Use separate card based DMA buffer for periods table list.
+ * 0.0.7
+ * Change remove and rename ctrls into lists.
+ * 0.0.8
+ * Try to fix capture sources.
+ * 0.0.9
+ * Fix AC3 output.
+ * Enable S32_LE format support.
+ * 0.0.10
+ * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ * 0.0.11
+ * Add Model name recognition.
+ * 0.0.12
+ * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ * Remove redundent "voice" handling.
+ * 0.0.13
+ * Single trigger call for multi channels.
+ * 0.0.14
+ * Set limits based on what the sound card hardware can do.
+ * playback periods_min=2, periods_max=8
+ * capture hw constraints require period_size = n * 64 bytes.
+ * playback hw constraints require period_size = n * 64 bytes.
+ * 0.0.15
+ * Separated ca0106.c into separate functional .c files.
+ * 0.0.16
+ * Implement 192000 sample rate.
+ * 0.0.17
+ * Add support for SB0410 and SB0413.
+ * 0.0.18
+ * Modified Copyright message.
+ * 0.0.19
+ * Added I2C and SPI registers. Filled in interrupt enable.
+ * 0.0.20
+ * Added GPIO info for SB Live 24bit.
+ *
+ *
+ * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0 */
+/************************************************************************************************/
+
+#define PTR 0x00 /* Indexed register set pointer register */
+ /* NOTE: The CHANNELNUM and ADDRESS words can */
+ /* be modified independently of each other. */
+ /* CNL[1:0], ADDR[27:16] */
+
+#define DATA 0x04 /* Indexed register set data register */
+ /* DATA[31:0] */
+
+#define IPR 0x08 /* Global interrupt pending register */
+ /* Clear pending interrupts by writing a 1 to */
+ /* the relevant bits and zero to the other bits */
+#define IPR_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
+#define IPR_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */
+#define IPR_SPDIF_IN_USER 0x00004000 /* SPDIF input user data has 16 more bits */
+#define IPR_SPDIF_OUT_USER 0x00002000 /* SPDIF output user data needs 16 more bits */
+#define IPR_SPDIF_OUT_FRAME 0x00001000 /* SPDIF frame about to start */
+#define IPR_SPI 0x00000800 /* SPI transaction completed */
+#define IPR_I2C_EEPROM 0x00000400 /* I2C EEPROM transaction completed */
+#define IPR_I2C_DAC 0x00000200 /* I2C DAC transaction completed */
+#define IPR_AI 0x00000100 /* Audio pending register changed. See PTR reg 0x76 */
+#define IPR_GPI 0x00000080 /* General Purpose input changed */
+#define IPR_SRC_LOCKED 0x00000040 /* SRC lock status changed */
+#define IPR_SPDIF_STATUS 0x00000020 /* SPDIF status changed */
+#define IPR_TIMER2 0x00000010 /* 192000Hz Timer */
+#define IPR_TIMER1 0x00000008 /* 44100Hz Timer */
+#define IPR_MIDI_RX_A 0x00000004 /* MIDI UART-A Receive buffer non-empty */
+#define IPR_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
+#define IPR_PCI 0x00000001 /* PCI Bus error */
+
+#define INTE 0x0c /* Interrupt enable register */
+
+#define INTE_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
+#define INTE_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */
+#define INTE_SPDIF_IN_USER 0x00004000 /* SPDIF input user data has 16 more bits */
+#define INTE_SPDIF_OUT_USER 0x00002000 /* SPDIF output user data needs 16 more bits */
+#define INTE_SPDIF_OUT_FRAME 0x00001000 /* SPDIF frame about to start */
+#define INTE_SPI 0x00000800 /* SPI transaction completed */
+#define INTE_I2C_EEPROM 0x00000400 /* I2C EEPROM transaction completed */
+#define INTE_I2C_DAC 0x00000200 /* I2C DAC transaction completed */
+#define INTE_AI 0x00000100 /* Audio pending register changed. See PTR reg 0x75 */
+#define INTE_GPI 0x00000080 /* General Purpose input changed */
+#define INTE_SRC_LOCKED 0x00000040 /* SRC lock status changed */
+#define INTE_SPDIF_STATUS 0x00000020 /* SPDIF status changed */
+#define INTE_TIMER2 0x00000010 /* 192000Hz Timer */
+#define INTE_TIMER1 0x00000008 /* 44100Hz Timer */
+#define INTE_MIDI_RX_A 0x00000004 /* MIDI UART-A Receive buffer non-empty */
+#define INTE_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
+#define INTE_PCI 0x00000001 /* PCI Bus error */
+
+#define UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */
+#define HCFG 0x14 /* Hardware config register */
+ /* 0x1000 causes AC3 to fails. It adds a dither bit. */
+
+#define HCFG_STAC 0x10000000 /* Special mode for STAC9460 Codec. */
+#define HCFG_CAPTURE_I2S_BYPASS 0x08000000 /* 1 = bypass I2S input async SRC. */
+#define HCFG_CAPTURE_SPDIF_BYPASS 0x04000000 /* 1 = bypass SPDIF input async SRC. */
+#define HCFG_PLAYBACK_I2S_BYPASS 0x02000000 /* 0 = I2S IN mixer output, 1 = I2S IN1. */
+#define HCFG_FORCE_LOCK 0x01000000 /* For test only. Force input SRC tracker to lock. */
+#define HCFG_PLAYBACK_ATTENUATION 0x00006000 /* Playback attenuation mask. 0 = 0dB, 1 = 6dB, 2 = 12dB, 3 = Mute. */
+#define HCFG_PLAYBACK_DITHER 0x00001000 /* 1 = Add dither bit to all playback channels. */
+#define HCFG_PLAYBACK_S32_LE 0x00000800 /* 1 = S32_LE, 0 = S16_LE */
+#define HCFG_CAPTURE_S32_LE 0x00000400 /* 1 = S32_LE, 0 = S16_LE (S32_LE current not working) */
+#define HCFG_8_CHANNEL_PLAY 0x00000200 /* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_8_CHANNEL_CAPTURE 0x00000100 /* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_MONO 0x00000080 /* 1 = I2S Input mono */
+#define HCFG_I2S_OUTPUT 0x00000010 /* 1 = I2S Output disabled */
+#define HCFG_AC97 0x00000008 /* 0 = AC97 1.0, 1 = AC97 2.0 */
+#define HCFG_LOCK_PLAYBACK_CACHE 0x00000004 /* 1 = Cancel bustmaster accesses to soundcache */
+ /* NOTE: This should generally never be used. */
+#define HCFG_LOCK_CAPTURE_CACHE 0x00000002 /* 1 = Cancel bustmaster accesses to soundcache */
+ /* NOTE: This should generally never be used. */
+#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */
+ /* Should be set to 1 when the EMU10K1 is */
+ /* completely initialized. */
+#define GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */
+ /* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
+ /* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
+ /* SB Live 24bit:
+ * bit 8 0 = SPDIF in and out / 1 = Analog (Mic or Line)-in.
+ * bit 9 0 = Mute / 1 = Analog out.
+ * bit 10 0 = Line-in / 1 = Mic-in.
+ * bit 11 0 = ? / 1 = ?
+ * bit 12 0 = ? / 1 = ?
+ * bit 13 0 = ? / 1 = ?
+ * bit 14 0 = Mute / 1 = Analog out
+ * bit 15 0 = ? / 1 = ?
+ * Both bit 9 and bit 14 have to be set for analog sound to work on the SB Live 24bit.
+ */
+ /* 8 general purpose programmable In/Out pins.
+ * GPI [8:0] Read only. Default 0.
+ * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
+ * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
+ */
+#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */
+
+#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
+
+/********************************************************************************************************/
+/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers */
+/********************************************************************************************************/
+
+/* Initally all registers from 0x00 to 0x3f have zero contents. */
+#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
+ /* One list entry: 4 bytes for DMA address,
+ * 4 bytes for period_size << 16.
+ * One list entry is 8 bytes long.
+ * One list entry for each period in the buffer.
+ */
+ /* ADDR[31:0], Default: 0x0 */
+#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
+ /* SIZE[21:16], Default: 0x8 */
+#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
+ /* PTR[5:0], Default: 0x0 */
+#define PLAYBACK_UNKNOWN3 0x03 /* Not used ?? */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+ /* DMA[31:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */
+ /* SIZE[31:16], Default: 0x0 */
+#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
+ /* POINTER[15:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_END_ADDR 0x07 /* Playback fifo end address */
+ /* END_ADDR[15:0], FLAG[16] 0 = don't stop, 1 = stop */
+#define PLAYBACK_FIFO_OFFSET_ADDRESS 0x08 /* Current fifo offset address [21:16] */
+ /* Cache size valid [5:0] */
+#define PLAYBACK_UNKNOWN9 0x09 /* 0x9 to 0xf Unused */
+#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */
+ /* DMA[31:0], Default: 0x0 */
+#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */
+ /* SIZE[31:16], Default: 0x0 */
+#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */
+ /* POINTER[15:0], Default: 0x0 */
+#define CAPTURE_FIFO_OFFSET_ADDRESS 0x13 /* Current fifo offset address [21:16] */
+ /* Cache size valid [5:0] */
+#define PLAYBACK_LAST_SAMPLE 0x20 /* The sample currently being played */
+/* 0x21 - 0x3f unused */
+#define BASIC_INTERRUPT 0x40 /* Used by both playback and capture interrupt handler */
+ /* Playback (0x1<<channel_id) */
+ /* Capture (0x100<<channel_id) */
+ /* Playback sample rate 96000 = 0x20000 */
+ /* Start Playback [3:0] (one bit per channel)
+ * Start Capture [11:8] (one bit per channel)
+ * Playback rate [23:16] (2 bits per channel) (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+ * Playback mixer in enable [27:24] (one bit per channel)
+ * Playback mixer out enable [31:28] (one bit per channel)
+ */
+/* The Digital out jack is shared with the Center/LFE Analogue output.
+ * The jack has 4 poles. I will call 1 - Tip, 2 - Next to 1, 3 - Next to 2, 4 - Next to 3
+ * For Analogue: 1 -> Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground
+ * For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground.
+ * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red.
+ * So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card.
+ */
+/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS
+ * The Rear SPDIF can be used for Stereo PCM and also AC3/DTS
+ * The Center/LFE SPDIF cannot be used for AC3/DTS, but can be used for Stereo PCM.
+ * Summary: For ALSA we use the Rear channel for SPDIF Digital AC3/DTS output
+ */
+/* A standard 2 pole mono mini-jack to RCA plug can be used for SPDIF Stereo PCM output from the Front channel.
+ * A standard 3 pole stereo mini-jack to 2 RCA plugs can be used for SPDIF AC3/DTS and Stereo PCM output utilising the Rear channel and just one of the RCA plugs.
+ */
+#define SPCS0 0x41 /* SPDIF output Channel Status 0 register. For Rear. default=0x02108004, non-audio=0x02108006 */
+#define SPCS1 0x42 /* SPDIF output Channel Status 1 register. For Front */
+#define SPCS2 0x43 /* SPDIF output Channel Status 2 register. For Center/LFE */
+#define SPCS3 0x44 /* SPDIF output Channel Status 3 register. Unknown */
+ /* When Channel set to 0: */
+#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */
+#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */
+#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */
+#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */
+#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */
+#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */
+#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */
+#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */
+#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */
+#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */
+#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */
+#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */
+#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */
+#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */
+#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */
+#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */
+#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */
+#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */
+#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */
+#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */
+#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */
+#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */
+#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */
+
+ /* When Channel set to 1: */
+#define SPCS_WORD_LENGTH_MASK 0x0000000f /* Word Length Mask */
+#define SPCS_WORD_LENGTH_16 0x00000008 /* Word Length 16 bit */
+#define SPCS_WORD_LENGTH_17 0x00000006 /* Word Length 17 bit */
+#define SPCS_WORD_LENGTH_18 0x00000004 /* Word Length 18 bit */
+#define SPCS_WORD_LENGTH_19 0x00000002 /* Word Length 19 bit */
+#define SPCS_WORD_LENGTH_20A 0x0000000a /* Word Length 20 bit */
+#define SPCS_WORD_LENGTH_20 0x00000009 /* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */
+#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */
+#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */
+#define SPCS_WORD_LENGTH_22 0x00000005 /* Word Length 22 bit */
+#define SPCS_WORD_LENGTH_23 0x00000003 /* Word Length 23 bit */
+#define SPCS_WORD_LENGTH_24 0x0000000b /* Word Length 24 bit */
+#define SPCS_ORIGINAL_SAMPLE_RATE_MASK 0x000000f0 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_NONE 0x00000000 /* Original Sample rate not indicated */
+#define SPCS_ORIGINAL_SAMPLE_RATE_16000 0x00000010 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES1 0x00000020 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_32000 0x00000030 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_12000 0x00000040 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_11025 0x00000050 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_8000 0x00000060 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES2 0x00000070 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_192000 0x00000080 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_24000 0x00000090 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_96000 0x000000a0 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_48000 0x000000b0 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_176400 0x000000c0 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_22050 0x000000d0 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_88200 0x000000e0 /* Original Sample rate */
+#define SPCS_ORIGINAL_SAMPLE_RATE_44100 0x000000f0 /* Original Sample rate */
+
+#define SPDIF_SELECT1 0x45 /* Enables SPDIF or Analogue outputs 0-SPDIF, 0xf00-Analogue */
+ /* 0x100 - Front, 0x800 - Rear, 0x200 - Center/LFE.
+ * But as the jack is shared, use 0xf00.
+ * The Windows2000 driver uses 0x0000000f for both digital and analog.
+ * 0xf00 introduces interesting noises onto the Center/LFE.
+ * If you turn the volume up, you hear computer noise,
+ * e.g. mouse moving, changing between app windows etc.
+ * So, I am going to set this to 0x0000000f all the time now,
+ * same as the windows driver does.
+ * Use register SPDIF_SELECT2(0x72) to switch between SPDIF and Analog.
+ */
+ /* When Channel = 0:
+ * Wide SPDIF format [3:0] (one bit for each channel) (0=20bit, 1=24bit)
+ * Tristate SPDIF Output [11:8] (one bit for each channel) (0=Not tristate, 1=Tristate)
+ * SPDIF Bypass enable [19:16] (one bit for each channel) (0=Not bypass, 1=Bypass)
+ */
+ /* When Channel = 1:
+ * SPDIF 0 User data [7:0]
+ * SPDIF 1 User data [15:8]
+ * SPDIF 0 User data [23:16]
+ * SPDIF 0 User data [31:24]
+ * User data can be sent by using the SPDIF output frame pending and SPDIF output user bit interrupts.
+ */
+#define WATERMARK 0x46 /* Test bit to indicate cache usage level */
+#define SPDIF_INPUT_STATUS 0x49 /* SPDIF Input status register. Bits the same as SPCS.
+ * When Channel = 0: Bits the same as SPCS channel 0.
+ * When Channel = 1: Bits the same as SPCS channel 1.
+ * When Channel = 2:
+ * SPDIF Input User data [16:0]
+ * SPDIF Input Frame count [21:16]
+ */
+#define CAPTURE_CACHE_DATA 0x50 /* 0x50-0x5f Recorded samples. */
+#define CAPTURE_SOURCE 0x60 /* Capture Source 0 = MIC */
+#define CAPTURE_SOURCE_CHANNEL0 0xf0000000 /* Mask for selecting the Capture sources */
+#define CAPTURE_SOURCE_CHANNEL1 0x0f000000 /* 0 - SPDIF mixer output. */
+#define CAPTURE_SOURCE_CHANNEL2 0x00f00000 /* 1 - What you hear or . 2 - ?? */
+#define CAPTURE_SOURCE_CHANNEL3 0x000f0000 /* 3 - Mic in, Line in, TAD in, Aux in. */
+#define CAPTURE_SOURCE_RECORD_MAP 0x0000ffff /* Default 0x00e4 */
+ /* Record Map [7:0] (2 bits per channel) 0=mapped to channel 0, 1=mapped to channel 1, 2=mapped to channel2, 3=mapped to channel3
+ * Record source select for channel 0 [18:16]
+ * Record source select for channel 1 [22:20]
+ * Record source select for channel 2 [26:24]
+ * Record source select for channel 3 [30:28]
+ * 0 - SPDIF mixer output.
+ * 1 - i2s mixer output.
+ * 2 - SPDIF input.
+ * 3 - i2s input.
+ * 4 - AC97 capture.
+ * 5 - SRC output.
+ */
+#define CAPTURE_VOLUME1 0x61 /* Capture volume per channel 0-3 */
+#define CAPTURE_VOLUME2 0x62 /* Capture volume per channel 4-7 */
+
+#define PLAYBACK_ROUTING1 0x63 /* Playback routing of channels 0-7. Effects AC3 output. Default 0x32765410 */
+#define ROUTING1_REAR 0x77000000 /* Channel_id 0 sends to 10, Channel_id 1 sends to 32 */
+#define ROUTING1_NULL 0x00770000 /* Channel_id 2 sends to 54, Channel_id 3 sends to 76 */
+#define ROUTING1_CENTER_LFE 0x00007700 /* 0x32765410 means, send Channel_id 0 to FRONT, Channel_id 1 to REAR */
+#define ROUTING1_FRONT 0x00000077 /* Channel_id 2 to CENTER_LFE, Channel_id 3 to NULL. */
+ /* Channel_id's handle stereo channels. Channel X is a single mono channel */
+ /* Host is input from the PCI bus. */
+ /* Host channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+ * Host channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+ * Host channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+ * Host channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+ * Host channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+ * Host channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+ * Host channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+ * Host channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+ */
+
+#define PLAYBACK_ROUTING2 0x64 /* Playback Routing . Feeding Capture channels back into Playback. Effects AC3 output. Default 0x76767676 */
+ /* SRC is input from the capture inputs. */
+ /* SRC channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+ * SRC channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+ * SRC channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+ * SRC channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+ * SRC channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+ * SRC channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+ * SRC channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+ * SRC channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+ */
+
+#define PLAYBACK_MUTE 0x65 /* Unknown. While playing 0x0, while silent 0x00fc0000 */
+ /* SPDIF Mixer input control:
+ * Invert SRC to SPDIF Mixer [7-0] (One bit per channel)
+ * Invert Host to SPDIF Mixer [15:8] (One bit per channel)
+ * SRC to SPDIF Mixer disable [23:16] (One bit per channel)
+ * Host to SPDIF Mixer disable [31:24] (One bit per channel)
+ */
+#define PLAYBACK_VOLUME1 0x66 /* Playback SPDIF volume per channel. Set to the same PLAYBACK_VOLUME(0x6a) */
+ /* PLAYBACK_VOLUME1 must be set to 30303030 for SPDIF AC3 Playback */
+ /* SPDIF mixer input volume. 0=12dB, 0x30=0dB, 0xFE=-51.5dB, 0xff=Mute */
+ /* One register for each of the 4 stereo streams. */
+ /* SRC Right volume [7:0]
+ * SRC Left volume [15:8]
+ * Host Right volume [23:16]
+ * Host Left volume [31:24]
+ */
+#define CAPTURE_ROUTING1 0x67 /* Capture Routing. Default 0x32765410 */
+ /* Similar to register 0x63, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_ROUTING2 0x68 /* Unknown Routing. Default 0x76767676 */
+ /* Similar to register 0x64, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_MUTE 0x69 /* Unknown. While capturing 0x0, while silent 0x00fc0000 */
+ /* Similar to register 0x65, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define PLAYBACK_VOLUME2 0x6a /* Playback Analog volume per channel. Does not effect AC3 output */
+ /* Similar to register 0x66, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define UNKNOWN6b 0x6b /* Unknown. Readonly. Default 00400000 00400000 00400000 00400000 */
+#define UART_A_DATA 0x6c /* Uart, used in setting sample rates, bits per sample etc. */
+#define UART_A_CMD 0x6d /* Uart, used in setting sample rates, bits per sample etc. */
+#define UART_B_DATA 0x6e /* Uart, Unknown. */
+#define UART_B_CMD 0x6f /* Uart, Unknown. */
+#define SAMPLE_RATE_TRACKER_STATUS 0x70 /* Readonly. Default 00108000 00108000 00500000 00500000 */
+ /* Estimated sample rate [19:0] Relative to 48kHz. 0x8000 = 1.0
+ * Rate Locked [20]
+ * SPDIF Locked [21] For SPDIF channel only.
+ * Valid Audio [22] For SPDIF channel only.
+ */
+#define CAPTURE_CONTROL 0x71 /* Some sort of routing. default = 40c81000 30303030 30300000 00700000 */
+ /* Channel_id 0: 0x40c81000 must be changed to 0x40c80000 for SPDIF AC3 input or output. */
+ /* Channel_id 1: 0xffffffff(mute) 0x30303030(max) controls CAPTURE feedback into PLAYBACK. */
+ /* Sample rate output control register Channel=0
+ * Sample output rate [1:0] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+ * Sample input rate [3:2] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+ * SRC input source select [4] 0=Audio from digital mixer, 1=Audio from analog source.
+ * Record rate [9:8] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+ * Record mixer output enable [12:10]
+ * I2S input rate master mode [15:14] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+ * I2S output rate [17:16] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+ * I2S output source select [18] (0=Audio from host, 1=Audio from SRC)
+ * Record mixer I2S enable [20:19] (enable/disable i2sin1 and i2sin0)
+ * I2S output master clock select [21] (0=256*I2S output rate, 1=512*I2S output rate.)
+ * I2S input master clock select [22] (0=256*I2S input rate, 1=512*I2S input rate.)
+ * I2S input mode [23] (0=Slave, 1=Master)
+ * SPDIF output rate [25:24] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+ * SPDIF output source select [26] (0=host, 1=SRC)
+ * Not used [27]
+ * Record Source 0 input [29:28] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+ * Record Source 1 input [31:30] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+ */
+ /* Sample rate output control register Channel=1
+ * I2S Input 0 volume Right [7:0]
+ * I2S Input 0 volume Left [15:8]
+ * I2S Input 1 volume Right [23:16]
+ * I2S Input 1 volume Left [31:24]
+ */
+ /* Sample rate output control register Channel=2
+ * SPDIF Input volume Right [23:16]
+ * SPDIF Input volume Left [31:24]
+ */
+ /* Sample rate output control register Channel=3
+ * No used
+ */
+#define SPDIF_SELECT2 0x72 /* Some sort of routing. Channel_id 0 only. default = 0x0f0f003f. Analog 0x000b0000, Digital 0x0b000000 */
+#define ROUTING2_FRONT_MASK 0x00010000 /* Enable for Front speakers. */
+#define ROUTING2_CENTER_LFE_MASK 0x00020000 /* Enable for Center/LFE speakers. */
+#define ROUTING2_REAR_MASK 0x00080000 /* Enable for Rear speakers. */
+ /* Audio output control
+ * AC97 output enable [5:0]
+ * I2S output enable [19:16]
+ * SPDIF output enable [27:24]
+ */
+#define UNKNOWN73 0x73 /* Unknown. Readonly. Default 0x0 */
+#define CHIP_VERSION 0x74 /* P17 Chip version. Channel_id 0 only. Default 00000071 */
+#define EXTENDED_INT_MASK 0x75 /* Used by both playback and capture interrupt handler */
+ /* Sets which Interrupts are enabled. */
+ /* 0x00000001 = Half period. Playback.
+ * 0x00000010 = Full period. Playback.
+ * 0x00000100 = Half buffer. Playback.
+ * 0x00001000 = Full buffer. Playback.
+ * 0x00010000 = Half buffer. Capture.
+ * 0x00100000 = Full buffer. Capture.
+ * Capture can only do 2 periods.
+ * 0x01000000 = End audio. Playback.
+ * 0x40000000 = Half buffer Playback,Caputre xrun.
+ * 0x80000000 = Full buffer Playback,Caputre xrun.
+ */
+#define EXTENDED_INT 0x76 /* Used by both playback and capture interrupt handler */
+ /* Shows which interrupts are active at the moment. */
+ /* Same bit layout as EXTENDED_INT_MASK */
+#define COUNTER77 0x77 /* Counter range 0 to 0x3fffff, 192000 counts per second. */
+#define COUNTER78 0x78 /* Counter range 0 to 0x3fffff, 44100 counts per second. */
+#define EXTENDED_INT_TIMER 0x79 /* Channel_id 0 only. Used by both playback and capture interrupt handler */
+ /* Causes interrupts based on timer intervals. */
+#define SPI 0x7a /* SPI: Serial Interface Register */
+#define I2C_A 0x7b /* I2C Address. 32 bit */
+#define I2C_0 0x7c /* I2C Data Port 0. 32 bit */
+#define I2C_1 0x7d /* I2C Data Port 1. 32 bit */
+
+
+#define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
+#define PCM_FRONT_CHANNEL 0
+#define PCM_REAR_CHANNEL 1
+#define PCM_CENTER_LFE_CHANNEL 2
+#define PCM_UNKNOWN_CHANNEL 3
+#define CONTROL_FRONT_CHANNEL 0
+#define CONTROL_REAR_CHANNEL 3
+#define CONTROL_CENTER_LFE_CHANNEL 1
+#define CONTROL_UNKNOWN_CHANNEL 2
+
+typedef struct snd_ca0106_channel ca0106_channel_t;
+typedef struct snd_ca0106 ca0106_t;
+typedef struct snd_ca0106_pcm ca0106_pcm_t;
+
+struct snd_ca0106_channel {
+ ca0106_t *emu;
+ int number;
+ int use;
+ void (*interrupt)(ca0106_t *emu, ca0106_channel_t *channel);
+ ca0106_pcm_t *epcm;
+};
+
+struct snd_ca0106_pcm {
+ ca0106_t *emu;
+ snd_pcm_substream_t *substream;
+ int channel_id;
+ unsigned short running;
+};
+
+// definition of the chip-specific record
+struct snd_ca0106 {
+ snd_card_t *card;
+ struct pci_dev *pci;
+
+ unsigned long port;
+ struct resource *res_port;
+ int irq;
+
+ unsigned int revision; /* chip revision */
+ unsigned int serial; /* serial number */
+ unsigned short model; /* subsystem id */
+
+ spinlock_t emu_lock;
+
+ ac97_t *ac97;
+ snd_pcm_t *pcm;
+
+ ca0106_channel_t playback_channels[4];
+ ca0106_channel_t capture_channels[4];
+ u32 spdif_bits[4]; /* s/pdif out setup */
+ int spdif_enable;
+ int capture_source;
+
+ struct snd_dma_buffer buffer;
+};
+
+int __devinit snd_ca0106_mixer(ca0106_t *emu);
+int __devinit snd_ca0106_proc_init(ca0106_t * emu);
+
+unsigned int snd_ca0106_ptr_read(ca0106_t * emu,
+ unsigned int reg,
+ unsigned int chn);
+
+void snd_ca0106_ptr_write(ca0106_t *emu,
+ unsigned int reg,
+ unsigned int chn,
+ unsigned int data);
+
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
new file mode 100644
index 00000000000..82533b45bc8
--- /dev/null
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ * Version: 0.0.22
+ *
+ * FEATURES currently supported:
+ * Front, Rear and Center/LFE.
+ * Surround40 and Surround51.
+ * Capture from MIC an LINE IN input.
+ * SPDIF digital playback of PCM stereo and AC3/DTS works.
+ * (One can use a standard mono mini-jack to one RCA plugs cable.
+ * or one can use a standard stereo mini-jack to two RCA plugs cable.
+ * Plug one of the RCA plugs into the Coax input of the external decoder/receiver.)
+ * ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. )
+ * Notes on how to capture sound:
+ * The AC97 is used in the PLAYBACK direction.
+ * The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC.
+ * So, to record from the MIC, set the MIC Playback volume to max,
+ * unmute the MIC and turn up the MASTER Playback volume.
+ * So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume.
+ *
+ * The only playback controls that currently do anything are: -
+ * Analog Front
+ * Analog Rear
+ * Analog Center/LFE
+ * SPDIF Front
+ * SPDIF Rear
+ * SPDIF Center/LFE
+ *
+ * For capture from Mic in or Line in.
+ * Digital/Analog ( switch must be in Analog mode for CAPTURE. )
+ *
+ * CAPTURE feedback into PLAYBACK
+ *
+ * Changelog:
+ * Support interrupts per period.
+ * Removed noise from Center/LFE channel when in Analog mode.
+ * Rename and remove mixer controls.
+ * 0.0.6
+ * Use separate card based DMA buffer for periods table list.
+ * 0.0.7
+ * Change remove and rename ctrls into lists.
+ * 0.0.8
+ * Try to fix capture sources.
+ * 0.0.9
+ * Fix AC3 output.
+ * Enable S32_LE format support.
+ * 0.0.10
+ * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ * 0.0.11
+ * Add Model name recognition.
+ * 0.0.12
+ * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ * Remove redundent "voice" handling.
+ * 0.0.13
+ * Single trigger call for multi channels.
+ * 0.0.14
+ * Set limits based on what the sound card hardware can do.
+ * playback periods_min=2, periods_max=8
+ * capture hw constraints require period_size = n * 64 bytes.
+ * playback hw constraints require period_size = n * 64 bytes.
+ * 0.0.15
+ * Minor updates.
+ * 0.0.16
+ * Implement 192000 sample rate.
+ * 0.0.17
+ * Add support for SB0410 and SB0413.
+ * 0.0.18
+ * Modified Copyright message.
+ * 0.0.19
+ * Finally fix support for SB Live 24 bit. SB0410 and SB0413.
+ * The output codec needs resetting, otherwise all output is muted.
+ * 0.0.20
+ * Merge "pci_disable_device(pci);" fixes.
+ * 0.0.21
+ * Add 4 capture channels. (SPDIF only comes in on channel 0. )
+ * Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
+ * 0.0.22
+ * Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
+ *
+ * BUGS:
+ * Some stability problems when unloading the snd-ca0106 kernel module.
+ * --
+ *
+ * TODO:
+ * 4 Capture channels, only one implemented so far.
+ * Other capture rates apart from 48khz not implemented.
+ * MIDI
+ * --
+ * GENERAL INFO:
+ * Model: SB0310
+ * P17 Chip: CA0106-DAT
+ * AC97 Codec: STAC 9721
+ * ADC: Philips 1361T (Stereo 24bit)
+ * DAC: WM8746EDS (6-channel, 24bit, 192Khz)
+ *
+ * GENERAL INFO:
+ * Model: SB0410
+ * P17 Chip: CA0106-DAT
+ * AC97 Codec: None
+ * ADC: WM8775EDS (4 Channel)
+ * DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support)
+ * SPDIF Out control switches between Mic in and SPDIF out.
+ * No sound out or mic input working yet.
+ *
+ * GENERAL INFO:
+ * Model: SB0413
+ * P17 Chip: CA0106-DAT
+ * AC97 Codec: None.
+ * ADC: Unknown
+ * DAC: Unknown
+ * Trying to handle it like the SB0410.
+ *
+ * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");
+MODULE_DESCRIPTION("CA0106");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}");
+
+// module parameters (see "Module Parameters")
+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;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the CA0106 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the CA0106 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");
+
+#include "ca0106.h"
+
+typedef struct {
+ u32 serial;
+ char * name;
+} ca0106_names_t;
+
+static ca0106_names_t ca0106_chip_names[] = {
+ { 0x10021102, "AudigyLS [SB0310]"} ,
+ { 0x10051102, "AudigyLS [SB0310b]"} , /* Unknown AudigyLS that also says SB0310 on it */
+ { 0x10061102, "Live! 7.1 24bit [SB0410]"} , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
+ { 0x10071102, "Live! 7.1 24bit [SB0413]"} , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */
+ { 0x10091462, "MSI K8N Diamond MB [SB0438]"}, /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
+ { 0, "AudigyLS [Unknown]" }
+};
+
+/* hardware definition */
+static snd_pcm_hardware_t snd_ca0106_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+ .rate_min = 48000,
+ .rate_max = 192000,
+ .channels_min = 2, //1,
+ .channels_max = 2, //6,
+ .buffer_bytes_max = ((65536 - 64) * 8),
+ .period_bytes_min = 64,
+ .period_bytes_max = (65536 - 64),
+ .periods_min = 2,
+ .periods_max = 8,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_ca0106_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = ((65536 - 64) * 8),
+ .period_bytes_min = 64,
+ .period_bytes_max = (65536 - 64),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+unsigned int snd_ca0106_ptr_read(ca0106_t * emu,
+ unsigned int reg,
+ unsigned int chn)
+{
+ unsigned long flags;
+ unsigned int regptr, val;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+}
+
+void snd_ca0106_ptr_write(ca0106_t *emu,
+ unsigned int reg,
+ unsigned int chn,
+ unsigned int data)
+{
+ unsigned int regptr;
+ unsigned long flags;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ outl(data, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb)
+{
+ unsigned long flags;
+ unsigned int enable;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ enable = inl(emu->port + INTE) | intrenb;
+ outl(enable, emu->port + INTE);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_ca0106_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ ca0106_pcm_t *epcm = runtime->private_data;
+
+ if (epcm) {
+ kfree(epcm);
+ }
+}
+
+/* open_playback callback */
+static int snd_ca0106_pcm_open_playback_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+ ca0106_t *chip = snd_pcm_substream_chip(substream);
+ ca0106_channel_t *channel = &(chip->playback_channels[channel_id]);
+ ca0106_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = chip;
+ epcm->substream = substream;
+ epcm->channel_id=channel_id;
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_ca0106_pcm_free_substream;
+
+ runtime->hw = snd_ca0106_playback_hw;
+
+ channel->emu = chip;
+ channel->number = channel_id;
+
+ channel->use=1;
+ //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+ //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+ channel->epcm=epcm;
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ return err;
+ return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_playback(snd_pcm_substream_t *substream)
+{
+ ca0106_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ca0106_pcm_t *epcm = runtime->private_data;
+ chip->playback_channels[epcm->channel_id].use=0;
+/* FIXME: maybe zero others */
+ return 0;
+}
+
+static int snd_ca0106_pcm_open_playback_front(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_center_lfe(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_playback_channel(substream, PCM_CENTER_LFE_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_unknown(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_playback_channel(substream, PCM_UNKNOWN_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_rear(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_playback_channel(substream, PCM_REAR_CHANNEL);
+}
+
+/* open_capture callback */
+static int snd_ca0106_pcm_open_capture_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+ ca0106_t *chip = snd_pcm_substream_chip(substream);
+ ca0106_channel_t *channel = &(chip->capture_channels[channel_id]);
+ ca0106_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL) {
+ snd_printk("open_capture_channel: failed epcm alloc\n");
+ return -ENOMEM;
+ }
+ epcm->emu = chip;
+ epcm->substream = substream;
+ epcm->channel_id=channel_id;
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_ca0106_pcm_free_substream;
+
+ runtime->hw = snd_ca0106_capture_hw;
+
+ channel->emu = chip;
+ channel->number = channel_id;
+
+ channel->use=1;
+ //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+ //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+ channel->epcm=epcm;
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ //snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
+ if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ return err;
+ return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_capture(snd_pcm_substream_t *substream)
+{
+ ca0106_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ca0106_pcm_t *epcm = runtime->private_data;
+ chip->capture_channels[epcm->channel_id].use=0;
+/* FIXME: maybe zero others */
+ return 0;
+}
+
+static int snd_ca0106_pcm_open_0_capture(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_capture_channel(substream, 0);
+}
+
+static int snd_ca0106_pcm_open_1_capture(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_capture_channel(substream, 1);
+}
+
+static int snd_ca0106_pcm_open_2_capture(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_capture_channel(substream, 2);
+}
+
+static int snd_ca0106_pcm_open_3_capture(snd_pcm_substream_t *substream)
+{
+ return snd_ca0106_pcm_open_capture_channel(substream, 3);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_playback(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_playback(snd_pcm_substream_t *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_capture(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_capture(snd_pcm_substream_t *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare playback callback */
+static int snd_ca0106_pcm_prepare_playback(snd_pcm_substream_t *substream)
+{
+ ca0106_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ca0106_pcm_t *epcm = runtime->private_data;
+ int channel = epcm->channel_id;
+ u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel));
+ u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+ u32 hcfg_mask = HCFG_PLAYBACK_S32_LE;
+ u32 hcfg_set = 0x00000000;
+ u32 hcfg;
+ u32 reg40_mask = 0x30000 << (channel<<1);
+ u32 reg40_set = 0;
+ u32 reg40;
+ /* FIXME: Depending on mixer selection of SPDIF out or not, select the spdif rate or the DAC rate. */
+ u32 reg71_mask = 0x03030000 ; /* Global. Set SPDIF rate. We only support 44100 to spdif, not to DAC. */
+ u32 reg71_set = 0;
+ u32 reg71;
+ int i;
+
+ //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+ //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+ //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+ /* Rate can be set per channel. */
+ /* reg40 control host to fifo */
+ /* reg71 controls DAC rate. */
+ switch (runtime->rate) {
+ case 44100:
+ reg40_set = 0x10000 << (channel<<1);
+ reg71_set = 0x01010000;
+ break;
+ case 48000:
+ reg40_set = 0;
+ reg71_set = 0;
+ break;
+ case 96000:
+ reg40_set = 0x20000 << (channel<<1);
+ reg71_set = 0x02020000;
+ break;
+ case 192000:
+ reg40_set = 0x30000 << (channel<<1);
+ reg71_set = 0x03030000;
+ break;
+ default:
+ reg40_set = 0;
+ reg71_set = 0;
+ break;
+ }
+ /* Format is a global setting */
+ /* FIXME: Only let the first channel accessed set this. */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ hcfg_set = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ hcfg_set = HCFG_PLAYBACK_S32_LE;
+ break;
+ default:
+ hcfg_set = 0;
+ break;
+ }
+ hcfg = inl(emu->port + HCFG) ;
+ hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+ outl(hcfg, emu->port + HCFG);
+ reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
+ reg40 = (reg40 & ~reg40_mask) | reg40_set;
+ snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
+ reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+ reg71 = (reg71 & ~reg71_mask) | reg71_set;
+ snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+
+ /* FIXME: Check emu->buffer.size before actually writing to it. */
+ for(i=0; i < runtime->periods; i++) {
+ table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
+ table_base[(i*2)+1]=period_size_bytes<<16;
+ }
+
+ snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel));
+ snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+ snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+ snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+ snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+ /* FIXME test what 0 bytes does. */
+ snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
+ snd_ca0106_ptr_write(emu, PLAYBACK_POINTER, channel, 0);
+ snd_ca0106_ptr_write(emu, 0x07, channel, 0x0);
+ snd_ca0106_ptr_write(emu, 0x08, channel, 0);
+ snd_ca0106_ptr_write(emu, PLAYBACK_MUTE, 0x0, 0x0); /* Unmute output */
+#if 0
+ snd_ca0106_ptr_write(emu, SPCS0, 0,
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT );
+ }
+#endif
+
+ return 0;
+}
+
+/* prepare capture callback */
+static int snd_ca0106_pcm_prepare_capture(snd_pcm_substream_t *substream)
+{
+ ca0106_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ca0106_pcm_t *epcm = runtime->private_data;
+ int channel = epcm->channel_id;
+ //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1));
+ snd_ca0106_ptr_write(emu, 0x13, channel, 0);
+ snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+ snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+ snd_ca0106_ptr_write(emu, CAPTURE_POINTER, channel, 0);
+
+ return 0;
+}
+
+/* trigger_playback callback */
+static int snd_ca0106_pcm_trigger_playback(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ ca0106_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime;
+ ca0106_pcm_t *epcm;
+ int channel;
+ int result = 0;
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ u32 basic = 0;
+ u32 extended = 0;
+ int running=0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ running=1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ default:
+ running=0;
+ break;
+ }
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ runtime = s->runtime;
+ epcm = runtime->private_data;
+ channel = epcm->channel_id;
+ //snd_printk("channel=%d\n",channel);
+ epcm->running = running;
+ basic |= (0x1<<channel);
+ extended |= (0x10<<channel);
+ snd_pcm_trigger_done(s, substream);
+ }
+ //snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
+ snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+ snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/* trigger_capture callback */
+static int snd_ca0106_pcm_trigger_capture(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ ca0106_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ca0106_pcm_t *epcm = runtime->private_data;
+ int channel = epcm->channel_id;
+ int result = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+ snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+ epcm->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+ snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+ epcm->running = 0;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_playback(snd_pcm_substream_t *substream)
+{
+ ca0106_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ca0106_pcm_t *epcm = runtime->private_data;
+ snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+ int channel = epcm->channel_id;
+
+ if (!epcm->running)
+ return 0;
+
+ ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+ ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+ ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+ if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+ ptr2 = bytes_to_frames(runtime, ptr1);
+ ptr2+= (ptr4 >> 3) * runtime->period_size;
+ ptr=ptr2;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+ return ptr;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_capture(snd_pcm_substream_t *substream)
+{
+ ca0106_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ca0106_pcm_t *epcm = runtime->private_data;
+ snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+ int channel = channel=epcm->channel_id;
+
+ if (!epcm->running)
+ return 0;
+
+ ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel);
+ ptr2 = bytes_to_frames(runtime, ptr1);
+ ptr=ptr2;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+ return ptr;
+}
+
+/* operators */
+static snd_pcm_ops_t snd_ca0106_playback_front_ops = {
+ .open = snd_ca0106_pcm_open_playback_front,
+ .close = snd_ca0106_pcm_close_playback,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_playback,
+ .hw_free = snd_ca0106_pcm_hw_free_playback,
+ .prepare = snd_ca0106_pcm_prepare_playback,
+ .trigger = snd_ca0106_pcm_trigger_playback,
+ .pointer = snd_ca0106_pcm_pointer_playback,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_0_ops = {
+ .open = snd_ca0106_pcm_open_0_capture,
+ .close = snd_ca0106_pcm_close_capture,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_capture,
+ .hw_free = snd_ca0106_pcm_hw_free_capture,
+ .prepare = snd_ca0106_pcm_prepare_capture,
+ .trigger = snd_ca0106_pcm_trigger_capture,
+ .pointer = snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_1_ops = {
+ .open = snd_ca0106_pcm_open_1_capture,
+ .close = snd_ca0106_pcm_close_capture,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_capture,
+ .hw_free = snd_ca0106_pcm_hw_free_capture,
+ .prepare = snd_ca0106_pcm_prepare_capture,
+ .trigger = snd_ca0106_pcm_trigger_capture,
+ .pointer = snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_2_ops = {
+ .open = snd_ca0106_pcm_open_2_capture,
+ .close = snd_ca0106_pcm_close_capture,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_capture,
+ .hw_free = snd_ca0106_pcm_hw_free_capture,
+ .prepare = snd_ca0106_pcm_prepare_capture,
+ .trigger = snd_ca0106_pcm_trigger_capture,
+ .pointer = snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_3_ops = {
+ .open = snd_ca0106_pcm_open_3_capture,
+ .close = snd_ca0106_pcm_close_capture,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_capture,
+ .hw_free = snd_ca0106_pcm_hw_free_capture,
+ .prepare = snd_ca0106_pcm_prepare_capture,
+ .trigger = snd_ca0106_pcm_trigger_capture,
+ .pointer = snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_playback_center_lfe_ops = {
+ .open = snd_ca0106_pcm_open_playback_center_lfe,
+ .close = snd_ca0106_pcm_close_playback,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_playback,
+ .hw_free = snd_ca0106_pcm_hw_free_playback,
+ .prepare = snd_ca0106_pcm_prepare_playback,
+ .trigger = snd_ca0106_pcm_trigger_playback,
+ .pointer = snd_ca0106_pcm_pointer_playback,
+};
+
+static snd_pcm_ops_t snd_ca0106_playback_unknown_ops = {
+ .open = snd_ca0106_pcm_open_playback_unknown,
+ .close = snd_ca0106_pcm_close_playback,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_playback,
+ .hw_free = snd_ca0106_pcm_hw_free_playback,
+ .prepare = snd_ca0106_pcm_prepare_playback,
+ .trigger = snd_ca0106_pcm_trigger_playback,
+ .pointer = snd_ca0106_pcm_pointer_playback,
+};
+
+static snd_pcm_ops_t snd_ca0106_playback_rear_ops = {
+ .open = snd_ca0106_pcm_open_playback_rear,
+ .close = snd_ca0106_pcm_close_playback,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ca0106_pcm_hw_params_playback,
+ .hw_free = snd_ca0106_pcm_hw_free_playback,
+ .prepare = snd_ca0106_pcm_prepare_playback,
+ .trigger = snd_ca0106_pcm_trigger_playback,
+ .pointer = snd_ca0106_pcm_pointer_playback,
+};
+
+
+static unsigned short snd_ca0106_ac97_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ ca0106_t *emu = ac97->private_data;
+ unsigned long flags;
+ unsigned short val;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outb(reg, emu->port + AC97ADDRESS);
+ val = inw(emu->port + AC97DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+}
+
+static void snd_ca0106_ac97_write(ac97_t *ac97,
+ unsigned short reg, unsigned short val)
+{
+ ca0106_t *emu = ac97->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outb(reg, emu->port + AC97ADDRESS);
+ outw(val, emu->port + AC97DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_ca0106_ac97(ca0106_t *chip)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_ca0106_ac97_write,
+ .read = snd_ca0106_ac97_read,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+ pbus->no_vra = 1; /* we don't need VRA */
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_ca0106_free(ca0106_t *chip)
+{
+ if (chip->res_port != NULL) { /* avoid access to already used hardware */
+ // disable interrupts
+ snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
+ outl(0, chip->port + INTE);
+ snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
+ udelay(1000);
+ // disable audio
+ //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+ outl(0, chip->port + HCFG);
+ /* FIXME: We need to stop and DMA transfers here.
+ * But as I am not sure how yet, we cannot from the dma pages.
+ * So we can fix: snd-malloc: Memory leak? pages not freed = 8
+ */
+ }
+ // release the data
+#if 1
+ if (chip->buffer.area)
+ snd_dma_free_pages(&chip->buffer);
+#endif
+
+ // release the i/o port
+ if (chip->res_port) {
+ release_resource(chip->res_port);
+ kfree_nocheck(chip->res_port);
+ }
+ // release the irq
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_ca0106_dev_free(snd_device_t *device)
+{
+ ca0106_t *chip = device->device_data;
+ return snd_ca0106_free(chip);
+}
+
+static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned int status;
+
+ ca0106_t *chip = dev_id;
+ int i;
+ int mask;
+ unsigned int stat76;
+ ca0106_channel_t *pchannel;
+
+ spin_lock(&chip->emu_lock);
+
+ status = inl(chip->port + IPR);
+
+ // call updater, unlock before it
+ spin_unlock(&chip->emu_lock);
+
+ if (! status)
+ return IRQ_NONE;
+
+ stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
+ //snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76);
+ //snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
+ mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
+ for(i = 0; i < 4; i++) {
+ pchannel = &(chip->playback_channels[i]);
+ if(stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+ if(pchannel->use) {
+ snd_pcm_period_elapsed(pchannel->epcm->substream);
+ //printk(KERN_INFO "interrupt [%d] used\n", i);
+ }
+ }
+ //printk(KERN_INFO "channel=%p\n",pchannel);
+ //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ mask <<= 1;
+ }
+ mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */
+ for(i = 0; i < 4; i++) {
+ pchannel = &(chip->capture_channels[i]);
+ if(stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+ if(pchannel->use) {
+ snd_pcm_period_elapsed(pchannel->epcm->substream);
+ //printk(KERN_INFO "interrupt [%d] used\n", i);
+ }
+ }
+ //printk(KERN_INFO "channel=%p\n",pchannel);
+ //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ mask <<= 1;
+ }
+
+ snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76);
+ spin_lock(&chip->emu_lock);
+ // acknowledge the interrupt if necessary
+ outl(status, chip->port+IPR);
+
+ spin_unlock(&chip->emu_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void snd_ca0106_pcm_free(snd_pcm_t *pcm)
+{
+ ca0106_t *emu = pcm->private_data;
+ emu->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ca0106_pcm(ca0106_t *emu, int device, snd_pcm_t **rpcm)
+{
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = emu;
+ pcm->private_free = snd_ca0106_pcm_free;
+
+ switch (device) {
+ case 0:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
+ break;
+ case 1:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
+ break;
+ case 2:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
+ break;
+ case 3:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
+ break;
+ }
+
+ pcm->info_flags = 0;
+ pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+ strcpy(pcm->name, "CA0106");
+ emu->pcm = pcm;
+
+ for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ substream;
+ substream = substream->next) {
+ if ((err = snd_pcm_lib_preallocate_pages(substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(emu->pci),
+ 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
+ return err;
+ }
+
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ substream;
+ substream = substream->next) {
+ if ((err = snd_pcm_lib_preallocate_pages(substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(emu->pci),
+ 64*1024, 64*1024)) < 0)
+ return err;
+ }
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+static int __devinit snd_ca0106_create(snd_card_t *card,
+ struct pci_dev *pci,
+ ca0106_t **rchip)
+{
+ ca0106_t *chip;
+ int err;
+ int ch;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_ca0106_dev_free,
+ };
+
+ *rchip = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ if (pci_set_dma_mask(pci, 0xffffffffUL) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0xffffffffUL) < 0) {
+ printk(KERN_ERR "error to set 32bit mask DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ spin_lock_init(&chip->emu_lock);
+
+ chip->port = pci_resource_start(pci, 0);
+ if ((chip->res_port = request_region(chip->port, 0x20,
+ "snd_ca0106")) == NULL) {
+ snd_ca0106_free(chip);
+ printk(KERN_ERR "cannot allocate the port\n");
+ return -EBUSY;
+ }
+
+ if (request_irq(pci->irq, snd_ca0106_interrupt,
+ SA_INTERRUPT|SA_SHIRQ, "snd_ca0106",
+ (void *)chip)) {
+ snd_ca0106_free(chip);
+ printk(KERN_ERR "cannot grab irq\n");
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ /* This stores the periods table. */
+ if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
+ snd_ca0106_free(chip);
+ return -ENOMEM;
+ }
+
+ pci_set_master(pci);
+ /* read revision & serial */
+ pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&chip->revision);
+ pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+#if 1
+ printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+ chip->revision, chip->serial);
+#endif
+
+ outl(0, chip->port + INTE);
+
+ /*
+ * Init to 0x02109204 :
+ * Clock accuracy = 0 (1000ppm)
+ * Sample Rate = 2 (48kHz)
+ * Audio Channel = 1 (Left of 2)
+ * Source Number = 0 (Unspecified)
+ * Generation Status = 1 (Original for Cat Code 12)
+ * Cat Code = 12 (Digital Signal Mixer)
+ * Mode = 0 (Mode 0)
+ * Emphasis = 0 (None)
+ * CP = 1 (Copyright unasserted)
+ * AN = 0 (Audio data)
+ * P = 0 (Consumer)
+ */
+ snd_ca0106_ptr_write(chip, SPCS0, 0,
+ chip->spdif_bits[0] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ /* Only SPCS1 has been tested */
+ snd_ca0106_ptr_write(chip, SPCS1, 0,
+ chip->spdif_bits[1] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ snd_ca0106_ptr_write(chip, SPCS2, 0,
+ chip->spdif_bits[2] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ snd_ca0106_ptr_write(chip, SPCS3, 0,
+ chip->spdif_bits[3] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+ snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
+ snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
+
+ /* Write 0x8000 to AC97_REC_GAIN to mute it. */
+ outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
+ outw(0x8000, chip->port + AC97DATA);
+#if 0
+ snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
+ snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
+ snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
+ snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
+#endif
+
+ //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
+ /* Analog or Digital output */
+ snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
+ snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000b0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers */
+ chip->spdif_enable = 0; /* Set digital SPDIF output off */
+ chip->capture_source = 3; /* Set CAPTURE_SOURCE */
+ //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
+ //snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
+
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
+ snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
+ snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
+ snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
+ snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
+ for(ch = 0; ch < 4; ch++) {
+ snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
+ snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
+ //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
+ //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
+ snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
+ snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
+ }
+ snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
+ chip->capture_source = 3; /* Set CAPTURE_SOURCE */
+
+ if ((chip->serial == 0x10061102) ||
+ (chip->serial == 0x10071102) ||
+ (chip->serial == 0x10091462)) { /* The SB0410 and SB0413 use GPIO differently. */
+ /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+ outl(0x0, chip->port+GPIO);
+ //outl(0x00f0e000, chip->port+GPIO); /* Analog */
+ outl(0x005f4300, chip->port+GPIO); /* Analog */
+ } else {
+ outl(0x0, chip->port+GPIO);
+ outl(0x005f03a3, chip->port+GPIO); /* Analog */
+ //outl(0x005f02a2, chip->port+GPIO); /* SPDIF */
+ }
+ snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
+
+ //outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+ //outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
+ //outl(0x00000009, chip->port+HCFG);
+ outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+ chip, &ops)) < 0) {
+ snd_ca0106_free(chip);
+ return err;
+ }
+ *rchip = chip;
+ return 0;
+}
+
+static int __devinit snd_ca0106_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ ca0106_t *chip;
+ ca0106_names_t *c;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_ca0106_create(card, pci, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((chip->serial != 0x10061102) &&
+ (chip->serial != 0x10071102) &&
+ (chip->serial != 0x10091462) ) { /* The SB0410 and SB0413 do not have an ac97 chip. */
+ if ((err = snd_ca0106_ac97(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ if ((err = snd_ca0106_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_ca0106_proc_init(chip);
+
+ strcpy(card->driver, "CA0106");
+ strcpy(card->shortname, "CA0106");
+
+ for (c=ca0106_chip_names; c->serial; c++) {
+ if (c->serial == chip->serial) break;
+ }
+ sprintf(card->longname, "%s at 0x%lx irq %i",
+ c->name, chip->port, chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_ca0106_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+// PCI IDs
+static struct pci_device_id snd_ca0106_ids[] = {
+ { 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Audigy LS or Live 24bit */
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
+
+// pci_driver definition
+static struct pci_driver driver = {
+ .name = "CA0106",
+ .id_table = snd_ca0106_ids,
+ .probe = snd_ca0106_probe,
+ .remove = __devexit_p(snd_ca0106_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_ca0106_init(void)
+{
+ int err;
+
+ if ((err = pci_module_init(&driver)) > 0)
+ return err;
+
+ return 0;
+}
+
+// clean up the module
+static void __exit alsa_card_ca0106_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ca0106_init)
+module_exit(alsa_card_ca0106_exit)
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
new file mode 100644
index 00000000000..97bed1b0899
--- /dev/null
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ * Version: 0.0.16
+ *
+ * FEATURES currently supported:
+ * See ca0106_main.c for features.
+ *
+ * Changelog:
+ * Support interrupts per period.
+ * Removed noise from Center/LFE channel when in Analog mode.
+ * Rename and remove mixer controls.
+ * 0.0.6
+ * Use separate card based DMA buffer for periods table list.
+ * 0.0.7
+ * Change remove and rename ctrls into lists.
+ * 0.0.8
+ * Try to fix capture sources.
+ * 0.0.9
+ * Fix AC3 output.
+ * Enable S32_LE format support.
+ * 0.0.10
+ * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ * 0.0.11
+ * Add Model name recognition.
+ * 0.0.12
+ * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ * Remove redundent "voice" handling.
+ * 0.0.13
+ * Single trigger call for multi channels.
+ * 0.0.14
+ * Set limits based on what the sound card hardware can do.
+ * playback periods_min=2, periods_max=8
+ * capture hw constraints require period_size = n * 64 bytes.
+ * playback hw constraints require period_size = n * 64 bytes.
+ * 0.0.15
+ * Separated ca0106.c into separate functional .c files.
+ * 0.0.16
+ * Modified Copyright message.
+ *
+ * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+
+#include "ca0106.h"
+
+static int snd_ca0106_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ca0106_shared_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->spdif_enable;
+ return 0;
+}
+
+static int snd_ca0106_shared_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+ u32 mask;
+
+ val = ucontrol->value.enumerated.item[0] ;
+ change = (emu->spdif_enable != val);
+ if (change) {
+ emu->spdif_enable = val;
+ if (val == 1) {
+ /* Digital */
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
+ snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
+ snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
+ mask = inl(emu->port + GPIO) & ~0x101;
+ outl(mask, emu->port + GPIO);
+
+ } else {
+ /* Analog */
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000b0000);
+ snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
+ snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
+ mask = inl(emu->port + GPIO) | 0x101;
+ outl(mask, emu->port + GPIO);
+ }
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ca0106_shared_spdif __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SPDIF Out",
+ .info = snd_ca0106_shared_spdif_info,
+ .get = snd_ca0106_shared_spdif_get,
+ .put = snd_ca0106_shared_spdif_put
+};
+
+static int snd_ca0106_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[6] = { "SPDIF out", "i2s mixer out", "SPDIF in", "i2s in", "AC97 in", "SRC out" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 6;
+ if (uinfo->value.enumerated.item > 5)
+ uinfo->value.enumerated.item = 5;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ca0106_capture_source_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->capture_source;
+ return 0;
+}
+
+static int snd_ca0106_capture_source_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+ u32 mask;
+ u32 source;
+
+ val = ucontrol->value.enumerated.item[0] ;
+ change = (emu->capture_source != val);
+ if (change) {
+ emu->capture_source = val;
+ source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+ mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
+ snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ca0106_capture_source __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_ca0106_capture_source_info,
+ .get = snd_ca0106_capture_source_get,
+ .put = snd_ca0106_capture_source_put
+};
+
+static int snd_ca0106_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ca0106_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+ return 0;
+}
+
+static int snd_ca0106_spdif_get_mask(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * 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 snd_ca0106_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int change;
+ unsigned int val;
+
+ val = (ucontrol->value.iec958.status[0] << 0) |
+ (ucontrol->value.iec958.status[1] << 8) |
+ (ucontrol->value.iec958.status[2] << 16) |
+ (ucontrol->value.iec958.status[3] << 24);
+ change = val != emu->spdif_bits[idx];
+ if (change) {
+ snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
+ emu->spdif_bits[idx] = val;
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ca0106_spdif_mask_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+ .count = 4,
+ .info = snd_ca0106_spdif_info,
+ .get = snd_ca0106_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_ca0106_spdif_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .count = 4,
+ .info = snd_ca0106_spdif_info,
+ .get = snd_ca0106_spdif_get,
+ .put = snd_ca0106_spdif_put
+};
+
+static int snd_ca0106_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_ca0106_volume_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol, int reg, int channel_id)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int value;
+
+ value = snd_ca0106_ptr_read(emu, reg, channel_id);
+ ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
+ ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
+ return 0;
+}
+
+static int snd_ca0106_volume_get_spdif_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_FRONT_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_get_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_spdif_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_UNKNOWN_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_spdif_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_REAR_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_analog_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_FRONT_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_get_analog_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_analog_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_UNKNOWN_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_analog_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_REAR_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_get_feedback(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = 1;
+ int reg = CAPTURE_CONTROL;
+ return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol, int reg, int channel_id)
+{
+ ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int value;
+ //value = snd_ca0106_ptr_read(emu, reg, channel_id);
+ //value = value & 0xffff;
+ value = ((0xff - ucontrol->value.integer.value[0]) << 24) | ((0xff - ucontrol->value.integer.value[1]) << 16);
+ value = value | ((0xff - ucontrol->value.integer.value[0]) << 8) | ((0xff - ucontrol->value.integer.value[1]) );
+ snd_ca0106_ptr_write(emu, reg, channel_id, value);
+ return 1;
+}
+static int snd_ca0106_volume_put_spdif_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_FRONT_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_spdif_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_UNKNOWN_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_spdif_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_REAR_CHANNEL;
+ int reg = PLAYBACK_VOLUME1;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_FRONT_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_UNKNOWN_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = CONTROL_REAR_CHANNEL;
+ int reg = PLAYBACK_VOLUME2;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_put_feedback(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int channel_id = 1;
+ int reg = CAPTURE_CONTROL;
+ return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_front =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Front Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_analog_front,
+ .put = snd_ca0106_volume_put_analog_front
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_center_lfe =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Center/LFE Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_analog_center_lfe,
+ .put = snd_ca0106_volume_put_analog_center_lfe
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Unknown Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_analog_unknown,
+ .put = snd_ca0106_volume_put_analog_unknown
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_rear =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Rear Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_analog_rear,
+ .put = snd_ca0106_volume_put_analog_rear
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_front =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SPDIF Front Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_spdif_front,
+ .put = snd_ca0106_volume_put_spdif_front
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_center_lfe =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SPDIF Center/LFE Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_spdif_center_lfe,
+ .put = snd_ca0106_volume_put_spdif_center_lfe
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_unknown =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SPDIF Unknown Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_spdif_unknown,
+ .put = snd_ca0106_volume_put_spdif_unknown
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_rear =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SPDIF Rear Volume",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_spdif_rear,
+ .put = snd_ca0106_volume_put_spdif_rear
+};
+
+static snd_kcontrol_new_t snd_ca0106_volume_control_feedback =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CAPTURE feedback into PLAYBACK",
+ .info = snd_ca0106_volume_info,
+ .get = snd_ca0106_volume_get_feedback,
+ .put = snd_ca0106_volume_put_feedback
+};
+
+
+static int remove_ctl(snd_card_t *card, const char *name)
+{
+ snd_ctl_elem_id_t id;
+ memset(&id, 0, sizeof(id));
+ strcpy(id.name, name);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_remove_id(card, &id);
+}
+
+static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name)
+{
+ snd_ctl_elem_id_t sid;
+ memset(&sid, 0, sizeof(sid));
+ /* FIXME: strcpy is bad. */
+ strcpy(sid.name, name);
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_find_id(card, &sid);
+}
+
+static int rename_ctl(snd_card_t *card, const char *src, const char *dst)
+{
+ snd_kcontrol_t *kctl = ctl_find(card, src);
+ if (kctl) {
+ strcpy(kctl->id.name, dst);
+ return 0;
+ }
+ return -ENOENT;
+}
+
+int __devinit snd_ca0106_mixer(ca0106_t *emu)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = emu->card;
+ char **c;
+ static char *ca0106_remove_ctls[] = {
+ "Master Mono Playback Switch",
+ "Master Mono Playback Volume",
+ "3D Control - Switch",
+ "3D Control Sigmatel - Depth",
+ "PCM Playback Switch",
+ "PCM Playback Volume",
+ "CD Playback Switch",
+ "CD Playback Volume",
+ "Phone Playback Switch",
+ "Phone Playback Volume",
+ "Video Playback Switch",
+ "Video Playback Volume",
+ "PC Speaker Playback Switch",
+ "PC Speaker Playback Volume",
+ "Mono Output Select",
+ "Capture Source",
+ "Capture Switch",
+ "Capture Volume",
+ "External Amplifier",
+ "Sigmatel 4-Speaker Stereo Playback Switch",
+ "Sigmatel Surround Phase Inversion Playback ",
+ NULL
+ };
+ static char *ca0106_rename_ctls[] = {
+ "Master Playback Switch", "Capture Switch",
+ "Master Playback Volume", "Capture Volume",
+ "Line Playback Switch", "AC97 Line Capture Switch",
+ "Line Playback Volume", "AC97 Line Capture Volume",
+ "Aux Playback Switch", "AC97 Aux Capture Switch",
+ "Aux Playback Volume", "AC97 Aux Capture Volume",
+ "Mic Playback Switch", "AC97 Mic Capture Switch",
+ "Mic Playback Volume", "AC97 Mic Capture Volume",
+ "Mic Select", "AC97 Mic Select",
+ "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
+ NULL
+ };
+#if 1
+ for (c=ca0106_remove_ctls; *c; c++)
+ remove_ctl(card, *c);
+ for (c=ca0106_rename_ctls; *c; c += 2)
+ rename_ctl(card, c[0], c[1]);
+#endif
+
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_front, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_rear, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_center_lfe, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_unknown, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_front, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_rear, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_center_lfe, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_unknown, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_feedback, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_mask_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_shared_spdif, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_ca0106_capture_source, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) {
+ /* already defined by ac97, remove it */
+ /* FIXME: or do we need both controls? */
+ remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT));
+ }
+ if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ return 0;
+}
+
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
new file mode 100644
index 00000000000..afb711421e4
--- /dev/null
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ * Version: 0.0.17
+ *
+ * FEATURES currently supported:
+ * See ca0106_main.c for features.
+ *
+ * Changelog:
+ * Support interrupts per period.
+ * Removed noise from Center/LFE channel when in Analog mode.
+ * Rename and remove mixer controls.
+ * 0.0.6
+ * Use separate card based DMA buffer for periods table list.
+ * 0.0.7
+ * Change remove and rename ctrls into lists.
+ * 0.0.8
+ * Try to fix capture sources.
+ * 0.0.9
+ * Fix AC3 output.
+ * Enable S32_LE format support.
+ * 0.0.10
+ * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ * 0.0.11
+ * Add Model name recognition.
+ * 0.0.12
+ * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ * Remove redundent "voice" handling.
+ * 0.0.13
+ * Single trigger call for multi channels.
+ * 0.0.14
+ * Set limits based on what the sound card hardware can do.
+ * playback periods_min=2, periods_max=8
+ * capture hw constraints require period_size = n * 64 bytes.
+ * playback hw constraints require period_size = n * 64 bytes.
+ * 0.0.15
+ * Separate ca0106.c into separate functional .c files.
+ * 0.0.16
+ * Modified Copyright message.
+ * 0.0.17
+ * Add iec958 file in proc file system to show status of SPDIF in.
+ *
+ * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+
+#include "ca0106.h"
+
+
+struct snd_ca0106_category_str {
+ int val;
+ const char *name;
+};
+
+static struct snd_ca0106_category_str snd_ca0106_con_category[] = {
+ { IEC958_AES1_CON_DAT, "DAT" },
+ { IEC958_AES1_CON_VCR, "VCR" },
+ { IEC958_AES1_CON_MICROPHONE, "microphone" },
+ { IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },
+ { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
+ { IEC958_AES1_CON_MIXER, "mixer" },
+ { IEC958_AES1_CON_SAMPLER, "sampler" },
+ { IEC958_AES1_CON_PCM_CODER, "PCM coder" },
+ { IEC958_AES1_CON_IEC908_CD, "CD" },
+ { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
+ { IEC958_AES1_CON_GENERAL, "general" },
+};
+
+
+void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value)
+{
+ int i;
+ u32 status[4];
+ status[0] = value & 0xff;
+ status[1] = (value >> 8) & 0xff;
+ status[2] = (value >> 16) & 0xff;
+ status[3] = (value >> 24) & 0xff;
+
+ if (! (status[0] & IEC958_AES0_PROFESSIONAL)) {
+ /* consumer */
+ snd_iprintf(buffer, "Mode: consumer\n");
+ snd_iprintf(buffer, "Data: ");
+ if (!(status[0] & IEC958_AES0_NONAUDIO)) {
+ snd_iprintf(buffer, "audio\n");
+ } else {
+ snd_iprintf(buffer, "non-audio\n");
+ }
+ snd_iprintf(buffer, "Rate: ");
+ switch (status[3] & IEC958_AES3_CON_FS) {
+ case IEC958_AES3_CON_FS_44100:
+ snd_iprintf(buffer, "44100 Hz\n");
+ break;
+ case IEC958_AES3_CON_FS_48000:
+ snd_iprintf(buffer, "48000 Hz\n");
+ break;
+ case IEC958_AES3_CON_FS_32000:
+ snd_iprintf(buffer, "32000 Hz\n");
+ break;
+ default:
+ snd_iprintf(buffer, "unknown\n");
+ break;
+ }
+ snd_iprintf(buffer, "Copyright: ");
+ if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) {
+ snd_iprintf(buffer, "permitted\n");
+ } else {
+ snd_iprintf(buffer, "protected\n");
+ }
+ snd_iprintf(buffer, "Emphasis: ");
+ if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) {
+ snd_iprintf(buffer, "none\n");
+ } else {
+ snd_iprintf(buffer, "50/15us\n");
+ }
+ snd_iprintf(buffer, "Category: ");
+ for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) {
+ if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) {
+ snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name);
+ break;
+ }
+ }
+ if (i >= ARRAY_SIZE(snd_ca0106_con_category)) {
+ snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY);
+ }
+ snd_iprintf(buffer, "Original: ");
+ if (status[1] & IEC958_AES1_CON_ORIGINAL) {
+ snd_iprintf(buffer, "original\n");
+ } else {
+ snd_iprintf(buffer, "1st generation\n");
+ }
+ snd_iprintf(buffer, "Clock: ");
+ switch (status[3] & IEC958_AES3_CON_CLOCK) {
+ case IEC958_AES3_CON_CLOCK_1000PPM:
+ snd_iprintf(buffer, "1000 ppm\n");
+ break;
+ case IEC958_AES3_CON_CLOCK_50PPM:
+ snd_iprintf(buffer, "50 ppm\n");
+ break;
+ case IEC958_AES3_CON_CLOCK_VARIABLE:
+ snd_iprintf(buffer, "variable pitch\n");
+ break;
+ default:
+ snd_iprintf(buffer, "unknown\n");
+ break;
+ }
+ } else {
+ snd_iprintf(buffer, "Mode: professional\n");
+ snd_iprintf(buffer, "Data: ");
+ if (!(status[0] & IEC958_AES0_NONAUDIO)) {
+ snd_iprintf(buffer, "audio\n");
+ } else {
+ snd_iprintf(buffer, "non-audio\n");
+ }
+ snd_iprintf(buffer, "Rate: ");
+ switch (status[0] & IEC958_AES0_PRO_FS) {
+ case IEC958_AES0_PRO_FS_44100:
+ snd_iprintf(buffer, "44100 Hz\n");
+ break;
+ case IEC958_AES0_PRO_FS_48000:
+ snd_iprintf(buffer, "48000 Hz\n");
+ break;
+ case IEC958_AES0_PRO_FS_32000:
+ snd_iprintf(buffer, "32000 Hz\n");
+ break;
+ default:
+ snd_iprintf(buffer, "unknown\n");
+ break;
+ }
+ snd_iprintf(buffer, "Rate Locked: ");
+ if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED)
+ snd_iprintf(buffer, "no\n");
+ else
+ snd_iprintf(buffer, "yes\n");
+ snd_iprintf(buffer, "Emphasis: ");
+ switch (status[0] & IEC958_AES0_PRO_EMPHASIS) {
+ case IEC958_AES0_PRO_EMPHASIS_CCITT:
+ snd_iprintf(buffer, "CCITT J.17\n");
+ break;
+ case IEC958_AES0_PRO_EMPHASIS_NONE:
+ snd_iprintf(buffer, "none\n");
+ break;
+ case IEC958_AES0_PRO_EMPHASIS_5015:
+ snd_iprintf(buffer, "50/15us\n");
+ break;
+ case IEC958_AES0_PRO_EMPHASIS_NOTID:
+ default:
+ snd_iprintf(buffer, "unknown\n");
+ break;
+ }
+ snd_iprintf(buffer, "Stereophonic: ");
+ if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) {
+ snd_iprintf(buffer, "stereo\n");
+ } else {
+ snd_iprintf(buffer, "not indicated\n");
+ }
+ snd_iprintf(buffer, "Userbits: ");
+ switch (status[1] & IEC958_AES1_PRO_USERBITS) {
+ case IEC958_AES1_PRO_USERBITS_192:
+ snd_iprintf(buffer, "192bit\n");
+ break;
+ case IEC958_AES1_PRO_USERBITS_UDEF:
+ snd_iprintf(buffer, "user-defined\n");
+ break;
+ default:
+ snd_iprintf(buffer, "unkown\n");
+ break;
+ }
+ snd_iprintf(buffer, "Sample Bits: ");
+ switch (status[2] & IEC958_AES2_PRO_SBITS) {
+ case IEC958_AES2_PRO_SBITS_20:
+ snd_iprintf(buffer, "20 bit\n");
+ break;
+ case IEC958_AES2_PRO_SBITS_24:
+ snd_iprintf(buffer, "24 bit\n");
+ break;
+ case IEC958_AES2_PRO_SBITS_UDEF:
+ snd_iprintf(buffer, "user defined\n");
+ break;
+ default:
+ snd_iprintf(buffer, "unknown\n");
+ break;
+ }
+ snd_iprintf(buffer, "Word Length: ");
+ switch (status[2] & IEC958_AES2_PRO_WORDLEN) {
+ case IEC958_AES2_PRO_WORDLEN_22_18:
+ snd_iprintf(buffer, "22 bit or 18 bit\n");
+ break;
+ case IEC958_AES2_PRO_WORDLEN_23_19:
+ snd_iprintf(buffer, "23 bit or 19 bit\n");
+ break;
+ case IEC958_AES2_PRO_WORDLEN_24_20:
+ snd_iprintf(buffer, "24 bit or 20 bit\n");
+ break;
+ case IEC958_AES2_PRO_WORDLEN_20_16:
+ snd_iprintf(buffer, "20 bit or 16 bit\n");
+ break;
+ default:
+ snd_iprintf(buffer, "unknown\n");
+ break;
+ }
+ }
+}
+
+static void snd_ca0106_proc_iec958(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ u32 value;
+
+ value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0);
+ snd_iprintf(buffer, "Status: %s, %s, %s\n",
+ (value & 0x100000) ? "Rate Locked" : "Not Rate Locked",
+ (value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock",
+ (value & 0x400000) ? "Audio Valid" : "No valid audio" );
+ snd_iprintf(buffer, "Estimated sample rate: %u\n",
+ ((value & 0xfffff) * 48000) / 0x8000 );
+ if (value & 0x200000) {
+ snd_iprintf(buffer, "IEC958/SPDIF input status:\n");
+ value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0);
+ snd_ca0106_proc_dump_iec958(buffer, value);
+ }
+
+ snd_iprintf(buffer, "\n");
+}
+
+static void snd_ca0106_proc_reg_write32(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ unsigned long flags;
+ char line[64];
+ u32 reg, val;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x", &reg, &val) != 2)
+ continue;
+ if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(val, emu->port + (reg & 0xfffffffc));
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ }
+ }
+}
+
+static void snd_ca0106_proc_reg_read32(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ unsigned long value;
+ unsigned long flags;
+ int i;
+ snd_iprintf(buffer, "Registers:\n\n");
+ for(i = 0; i < 0x20; i+=4) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ value = inl(emu->port + i);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
+ }
+}
+
+static void snd_ca0106_proc_reg_read16(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ unsigned int value;
+ unsigned long flags;
+ int i;
+ snd_iprintf(buffer, "Registers:\n\n");
+ for(i = 0; i < 0x20; i+=2) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ value = inw(emu->port + i);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_iprintf(buffer, "Register %02X: %04X\n", i, value);
+ }
+}
+
+static void snd_ca0106_proc_reg_read8(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ unsigned int value;
+ unsigned long flags;
+ int i;
+ snd_iprintf(buffer, "Registers:\n\n");
+ for(i = 0; i < 0x20; i+=1) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ value = inb(emu->port + i);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_iprintf(buffer, "Register %02X: %02X\n", i, value);
+ }
+}
+
+static void snd_ca0106_proc_reg_read1(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ unsigned long value;
+ int i,j;
+
+ snd_iprintf(buffer, "Registers\n");
+ for(i = 0; i < 0x40; i++) {
+ snd_iprintf(buffer, "%02X: ",i);
+ for (j = 0; j < 4; j++) {
+ value = snd_ca0106_ptr_read(emu, i, j);
+ snd_iprintf(buffer, "%08lX ", value);
+ }
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void snd_ca0106_proc_reg_read2(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ unsigned long value;
+ int i,j;
+
+ snd_iprintf(buffer, "Registers\n");
+ for(i = 0x40; i < 0x80; i++) {
+ snd_iprintf(buffer, "%02X: ",i);
+ for (j = 0; j < 4; j++) {
+ value = snd_ca0106_ptr_read(emu, i, j);
+ snd_iprintf(buffer, "%08lX ", value);
+ }
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void snd_ca0106_proc_reg_write(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ca0106_t *emu = entry->private_data;
+ char line[64];
+ unsigned int reg, channel_id , val;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+ continue;
+ if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+ snd_ca0106_ptr_write(emu, reg, channel_id, val);
+ }
+}
+
+
+int __devinit snd_ca0106_proc_init(ca0106_t * emu)
+{
+ snd_info_entry_t *entry;
+
+ if(! snd_card_proc_new(emu->card, "iec958", &entry))
+ snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_iec958);
+ if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
+ snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_ca0106_proc_reg_write32;
+ }
+ if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
+ snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16);
+ if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry))
+ snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read8);
+ if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
+ snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_ca0106_proc_reg_write;
+// entry->private_data = emu;
+ }
+ if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry))
+ snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read2);
+ return 0;
+}
+
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
new file mode 100644
index 00000000000..113208fbde1
--- /dev/null
+++ b/sound/pci/cmipci.c
@@ -0,0 +1,2956 @@
+/*
+ * Driver for C-Media CMI8338 and 8738 PCI soundcards.
+ * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Does not work. Warning may block system in capture mode */
+/* #define USE_VAR48KRATE */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("C-Media CMI8x38 PCI");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738},"
+ "{C-Media,CMI8738B},"
+ "{C-Media,CMI8338A},"
+ "{C-Media,CMI8338B}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static long mpu_port[SNDRV_CARDS];
+static long fm_port[SNDRV_CARDS];
+static int soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
+#ifdef SUPPORT_JOYSTICK
+static int joystick_port[SNDRV_CARDS];
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for C-Media PCI soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port.");
+module_param_array(soft_ac3, bool, NULL, 0444);
+MODULE_PARM_DESC(soft_ac3, "Sofware-conversion of raw SPDIF packets (model 033 only).");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(joystick_port, int, NULL, 0444);
+MODULE_PARM_DESC(joystick_port, "Joystick port address.");
+#endif
+
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
+#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
+#endif
+
+/*
+ * CM8x38 registers definition
+ */
+
+#define CM_REG_FUNCTRL0 0x00
+#define CM_RST_CH1 0x00080000
+#define CM_RST_CH0 0x00040000
+#define CM_CHEN1 0x00020000 /* ch1: enable */
+#define CM_CHEN0 0x00010000 /* ch0: enable */
+#define CM_PAUSE1 0x00000008 /* ch1: pause */
+#define CM_PAUSE0 0x00000004 /* ch0: pause */
+#define CM_CHADC1 0x00000002 /* ch1, 0:playback, 1:record */
+#define CM_CHADC0 0x00000001 /* ch0, 0:playback, 1:record */
+
+#define CM_REG_FUNCTRL1 0x04
+#define CM_ASFC_MASK 0x0000E000 /* ADC sampling frequency */
+#define CM_ASFC_SHIFT 13
+#define CM_DSFC_MASK 0x00001C00 /* DAC sampling frequency */
+#define CM_DSFC_SHIFT 10
+#define CM_SPDF_1 0x00000200 /* SPDIF IN/OUT at channel B */
+#define CM_SPDF_0 0x00000100 /* SPDIF OUT only channel A */
+#define CM_SPDFLOOP 0x00000080 /* ext. SPDIIF/OUT -> IN loopback */
+#define CM_SPDO2DAC 0x00000040 /* SPDIF/OUT can be heard from internal DAC */
+#define CM_INTRM 0x00000020 /* master control block (MCB) interrupt enabled */
+#define CM_BREQ 0x00000010 /* bus master enabled */
+#define CM_VOICE_EN 0x00000008 /* legacy voice (SB16,FM) */
+#define CM_UART_EN 0x00000004 /* UART */
+#define CM_JYSTK_EN 0x00000002 /* joy stick */
+
+#define CM_REG_CHFORMAT 0x08
+
+#define CM_CHB3D5C 0x80000000 /* 5,6 channels */
+#define CM_CHB3D 0x20000000 /* 4 channels */
+
+#define CM_CHIP_MASK1 0x1f000000
+#define CM_CHIP_037 0x01000000
+
+#define CM_SPDIF_SELECT1 0x00080000 /* for model <= 037 ? */
+#define CM_AC3EN1 0x00100000 /* enable AC3: model 037 */
+#define CM_SPD24SEL 0x00020000 /* 24bit spdif: model 037 */
+/* #define CM_SPDIF_INVERSE 0x00010000 */ /* ??? */
+
+#define CM_ADCBITLEN_MASK 0x0000C000
+#define CM_ADCBITLEN_16 0x00000000
+#define CM_ADCBITLEN_15 0x00004000
+#define CM_ADCBITLEN_14 0x00008000
+#define CM_ADCBITLEN_13 0x0000C000
+
+#define CM_ADCDACLEN_MASK 0x00003000
+#define CM_ADCDACLEN_060 0x00000000
+#define CM_ADCDACLEN_066 0x00001000
+#define CM_ADCDACLEN_130 0x00002000
+#define CM_ADCDACLEN_280 0x00003000
+
+#define CM_CH1_SRATE_176K 0x00000800
+#define CM_CH1_SRATE_88K 0x00000400
+#define CM_CH0_SRATE_176K 0x00000200
+#define CM_CH0_SRATE_88K 0x00000100
+
+#define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */
+
+#define CM_CH1FMT_MASK 0x0000000C
+#define CM_CH1FMT_SHIFT 2
+#define CM_CH0FMT_MASK 0x00000003
+#define CM_CH0FMT_SHIFT 0
+
+#define CM_REG_INT_HLDCLR 0x0C
+#define CM_CHIP_MASK2 0xff000000
+#define CM_CHIP_039 0x04000000
+#define CM_CHIP_039_6CH 0x01000000
+#define CM_CHIP_055 0x08000000
+#define CM_CHIP_8768 0x20000000
+#define CM_TDMA_INT_EN 0x00040000
+#define CM_CH1_INT_EN 0x00020000
+#define CM_CH0_INT_EN 0x00010000
+#define CM_INT_HOLD 0x00000002
+#define CM_INT_CLEAR 0x00000001
+
+#define CM_REG_INT_STATUS 0x10
+#define CM_INTR 0x80000000
+#define CM_VCO 0x08000000 /* Voice Control? CMI8738 */
+#define CM_MCBINT 0x04000000 /* Master Control Block abort cond.? */
+#define CM_UARTINT 0x00010000
+#define CM_LTDMAINT 0x00008000
+#define CM_HTDMAINT 0x00004000
+#define CM_XDO46 0x00000080 /* Modell 033? Direct programming EEPROM (read data register) */
+#define CM_LHBTOG 0x00000040 /* High/Low status from DMA ctrl register */
+#define CM_LEG_HDMA 0x00000020 /* Legacy is in High DMA channel */
+#define CM_LEG_STEREO 0x00000010 /* Legacy is in Stereo mode */
+#define CM_CH1BUSY 0x00000008
+#define CM_CH0BUSY 0x00000004
+#define CM_CHINT1 0x00000002
+#define CM_CHINT0 0x00000001
+
+#define CM_REG_LEGACY_CTRL 0x14
+#define CM_NXCHG 0x80000000 /* h/w multi channels? */
+#define CM_VMPU_MASK 0x60000000 /* MPU401 i/o port address */
+#define CM_VMPU_330 0x00000000
+#define CM_VMPU_320 0x20000000
+#define CM_VMPU_310 0x40000000
+#define CM_VMPU_300 0x60000000
+#define CM_VSBSEL_MASK 0x0C000000 /* SB16 base address */
+#define CM_VSBSEL_220 0x00000000
+#define CM_VSBSEL_240 0x04000000
+#define CM_VSBSEL_260 0x08000000
+#define CM_VSBSEL_280 0x0C000000
+#define CM_FMSEL_MASK 0x03000000 /* FM OPL3 base address */
+#define CM_FMSEL_388 0x00000000
+#define CM_FMSEL_3C8 0x01000000
+#define CM_FMSEL_3E0 0x02000000
+#define CM_FMSEL_3E8 0x03000000
+#define CM_ENSPDOUT 0x00800000 /* enable XPDIF/OUT to I/O interface */
+#define CM_SPDCOPYRHT 0x00400000 /* set copyright spdif in/out */
+#define CM_DAC2SPDO 0x00200000 /* enable wave+fm_midi -> SPDIF/OUT */
+#define CM_SETRETRY 0x00010000 /* 0: legacy i/o wait (default), 1: legacy i/o bus retry */
+#define CM_CHB3D6C 0x00008000 /* 5.1 channels support */
+#define CM_LINE_AS_BASS 0x00006000 /* use line-in as bass */
+
+#define CM_REG_MISC_CTRL 0x18
+#define CM_PWD 0x80000000
+#define CM_RESET 0x40000000
+#define CM_SFIL_MASK 0x30000000
+#define CM_TXVX 0x08000000
+#define CM_N4SPK3D 0x04000000 /* 4ch output */
+#define CM_SPDO5V 0x02000000 /* 5V spdif output (1 = 0.5v (coax)) */
+#define CM_SPDIF48K 0x01000000 /* write */
+#define CM_SPATUS48K 0x01000000 /* read */
+#define CM_ENDBDAC 0x00800000 /* enable dual dac */
+#define CM_XCHGDAC 0x00400000 /* 0: front=ch0, 1: front=ch1 */
+#define CM_SPD32SEL 0x00200000 /* 0: 16bit SPDIF, 1: 32bit */
+#define CM_SPDFLOOPI 0x00100000 /* int. SPDIF-IN -> int. OUT */
+#define CM_FM_EN 0x00080000 /* enalbe FM */
+#define CM_AC3EN2 0x00040000 /* enable AC3: model 039 */
+#define CM_VIDWPDSB 0x00010000
+#define CM_SPDF_AC97 0x00008000 /* 0: SPDIF/OUT 44.1K, 1: 48K */
+#define CM_MASK_EN 0x00004000
+#define CM_VIDWPPRT 0x00002000
+#define CM_SFILENB 0x00001000
+#define CM_MMODE_MASK 0x00000E00
+#define CM_SPDIF_SELECT2 0x00000100 /* for model > 039 ? */
+#define CM_ENCENTER 0x00000080
+#define CM_FLINKON 0x00000040
+#define CM_FLINKOFF 0x00000020
+#define CM_MIDSMP 0x00000010
+#define CM_UPDDMA_MASK 0x0000000C
+#define CM_TWAIT_MASK 0x00000003
+
+ /* byte */
+#define CM_REG_MIXER0 0x20
+
+#define CM_REG_SB16_DATA 0x22
+#define CM_REG_SB16_ADDR 0x23
+
+#define CM_REFFREQ_XIN (315*1000*1000)/22 /* 14.31818 Mhz reference clock frequency pin XIN */
+#define CM_ADCMULT_XIN 512 /* Guessed (487 best for 44.1kHz, not for 88/176kHz) */
+#define CM_TOLERANCE_RATE 0.001 /* Tolerance sample rate pitch (1000ppm) */
+#define CM_MAXIMUM_RATE 80000000 /* Note more than 80MHz */
+
+#define CM_REG_MIXER1 0x24
+#define CM_FMMUTE 0x80 /* mute FM */
+#define CM_FMMUTE_SHIFT 7
+#define CM_WSMUTE 0x40 /* mute PCM */
+#define CM_WSMUTE_SHIFT 6
+#define CM_SPK4 0x20 /* lin-in -> rear line out */
+#define CM_SPK4_SHIFT 5
+#define CM_REAR2FRONT 0x10 /* exchange rear/front */
+#define CM_REAR2FRONT_SHIFT 4
+#define CM_WAVEINL 0x08 /* digital wave rec. left chan */
+#define CM_WAVEINL_SHIFT 3
+#define CM_WAVEINR 0x04 /* digical wave rec. right */
+#define CM_WAVEINR_SHIFT 2
+#define CM_X3DEN 0x02 /* 3D surround enable */
+#define CM_X3DEN_SHIFT 1
+#define CM_CDPLAY 0x01 /* enable SPDIF/IN PCM -> DAC */
+#define CM_CDPLAY_SHIFT 0
+
+#define CM_REG_MIXER2 0x25
+#define CM_RAUXREN 0x80 /* AUX right capture */
+#define CM_RAUXREN_SHIFT 7
+#define CM_RAUXLEN 0x40 /* AUX left capture */
+#define CM_RAUXLEN_SHIFT 6
+#define CM_VAUXRM 0x20 /* AUX right mute */
+#define CM_VAUXRM_SHIFT 5
+#define CM_VAUXLM 0x10 /* AUX left mute */
+#define CM_VAUXLM_SHIFT 4
+#define CM_VADMIC_MASK 0x0e /* mic gain level (0-3) << 1 */
+#define CM_VADMIC_SHIFT 1
+#define CM_MICGAINZ 0x01 /* mic boost */
+#define CM_MICGAINZ_SHIFT 0
+
+#define CM_REG_AUX_VOL 0x26
+#define CM_VAUXL_MASK 0xf0
+#define CM_VAUXR_MASK 0x0f
+
+#define CM_REG_MISC 0x27
+#define CM_XGPO1 0x20
+// #define CM_XGPBIO 0x04
+#define CM_MIC_CENTER_LFE 0x04 /* mic as center/lfe out? (model 039 or later?) */
+#define CM_SPDIF_INVERSE 0x04 /* spdif input phase inverse (model 037) */
+#define CM_SPDVALID 0x02 /* spdif input valid check */
+#define CM_DMAUTO 0x01
+
+#define CM_REG_AC97 0x28 /* hmmm.. do we have ac97 link? */
+/*
+ * For CMI-8338 (0x28 - 0x2b) .. is this valid for CMI-8738
+ * or identical with AC97 codec?
+ */
+#define CM_REG_EXTERN_CODEC CM_REG_AC97
+
+/*
+ * MPU401 pci port index address 0x40 - 0x4f (CMI-8738 spec ver. 0.6)
+ */
+#define CM_REG_MPU_PCI 0x40
+
+/*
+ * FM pci port index address 0x50 - 0x5f (CMI-8738 spec ver. 0.6)
+ */
+#define CM_REG_FM_PCI 0x50
+
+/*
+ * for CMI-8338 .. this is not valid for CMI-8738.
+ */
+#define CM_REG_EXTENT_IND 0xf0
+#define CM_VPHONE_MASK 0xe0 /* Phone volume control (0-3) << 5 */
+#define CM_VPHONE_SHIFT 5
+#define CM_VPHOM 0x10 /* Phone mute control */
+#define CM_VSPKM 0x08 /* Speaker mute control, default high */
+#define CM_RLOOPREN 0x04 /* Rec. R-channel enable */
+#define CM_RLOOPLEN 0x02 /* Rec. L-channel enable */
+
+/*
+ * CMI-8338 spec ver 0.5 (this is not valid for CMI-8738):
+ * the 8 registers 0xf8 - 0xff are used for programming m/n counter by the PLL
+ * unit (readonly?).
+ */
+#define CM_REG_PLL 0xf8
+
+/*
+ * extended registers
+ */
+#define CM_REG_CH0_FRAME1 0x80 /* base address */
+#define CM_REG_CH0_FRAME2 0x84
+#define CM_REG_CH1_FRAME1 0x88 /* 0-15: count of samples at bus master; buffer size */
+#define CM_REG_CH1_FRAME2 0x8C /* 16-31: count of samples at codec; fragment size */
+#define CM_REG_MISC_CTRL_8768 0x92 /* reg. name the same as 0x18 */
+#define CM_CHB3D8C 0x20 /* 7.1 channels support */
+#define CM_SPD32FMT 0x10 /* SPDIF/IN 32k */
+#define CM_ADC2SPDIF 0x08 /* ADC output to SPDIF/OUT */
+#define CM_SHAREADC 0x04 /* DAC in ADC as Center/LFE */
+#define CM_REALTCMP 0x02 /* monitor the CMPL/CMPR of ADC */
+#define CM_INVLRCK 0x01 /* invert ZVPORT's LRCK */
+
+/*
+ * size of i/o region
+ */
+#define CM_EXTENT_CODEC 0x100
+#define CM_EXTENT_MIDI 0x2
+#define CM_EXTENT_SYNTH 0x4
+
+
+/*
+ * pci ids
+ */
+#ifndef PCI_VENDOR_ID_CMEDIA
+#define PCI_VENDOR_ID_CMEDIA 0x13F6
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A
+#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B
+#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
+#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
+#endif
+
+/*
+ * channels for playback / capture
+ */
+#define CM_CH_PLAY 0
+#define CM_CH_CAPT 1
+
+/*
+ * flags to check device open/close
+ */
+#define CM_OPEN_NONE 0
+#define CM_OPEN_CH_MASK 0x01
+#define CM_OPEN_DAC 0x10
+#define CM_OPEN_ADC 0x20
+#define CM_OPEN_SPDIF 0x40
+#define CM_OPEN_MCHAN 0x80
+#define CM_OPEN_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC)
+#define CM_OPEN_PLAYBACK2 (CM_CH_CAPT | CM_OPEN_DAC)
+#define CM_OPEN_PLAYBACK_MULTI (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_MCHAN)
+#define CM_OPEN_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC)
+#define CM_OPEN_SPDIF_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_SPDIF)
+#define CM_OPEN_SPDIF_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC | CM_OPEN_SPDIF)
+
+
+#if CM_CH_PLAY == 1
+#define CM_PLAYBACK_SRATE_176K CM_CH1_SRATE_176K
+#define CM_PLAYBACK_SPDF CM_SPDF_1
+#define CM_CAPTURE_SPDF CM_SPDF_0
+#else
+#define CM_PLAYBACK_SRATE_176K CM_CH0_SRATE_176K
+#define CM_PLAYBACK_SPDF CM_SPDF_0
+#define CM_CAPTURE_SPDF CM_SPDF_1
+#endif
+
+
+/*
+ * driver data
+ */
+
+typedef struct snd_stru_cmipci cmipci_t;
+typedef struct snd_stru_cmipci_pcm cmipci_pcm_t;
+
+struct snd_stru_cmipci_pcm {
+ snd_pcm_substream_t *substream;
+ int running; /* dac/adc running? */
+ unsigned int dma_size; /* in frames */
+ unsigned int period_size; /* in frames */
+ unsigned int offset; /* physical address of the buffer */
+ unsigned int fmt; /* format bits */
+ int ch; /* channel (0/1) */
+ unsigned int is_dac; /* is dac? */
+ int bytes_per_frame;
+ int shift;
+};
+
+/* mixer elements toggled/resumed during ac3 playback */
+struct cmipci_mixer_auto_switches {
+ const char *name; /* switch to toggle */
+ int toggle_on; /* value to change when ac3 mode */
+};
+static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = {
+ {"PCM Playback Switch", 0},
+ {"IEC958 Output Switch", 1},
+ {"IEC958 Mix Analog", 0},
+ // {"IEC958 Out To DAC", 1}, // no longer used
+ {"IEC958 Loop", 0},
+};
+#define CM_SAVED_MIXERS ARRAY_SIZE(cm_saved_mixer)
+
+struct snd_stru_cmipci {
+ snd_card_t *card;
+
+ struct pci_dev *pci;
+ unsigned int device; /* device ID */
+ int irq;
+
+ unsigned long iobase;
+ unsigned int ctrl; /* FUNCTRL0 current value */
+
+ snd_pcm_t *pcm; /* DAC/ADC PCM */
+ snd_pcm_t *pcm2; /* 2nd DAC */
+ snd_pcm_t *pcm_spdif; /* SPDIF */
+
+ int chip_version;
+ int max_channels;
+ unsigned int has_dual_dac: 1;
+ unsigned int can_ac3_sw: 1;
+ unsigned int can_ac3_hw: 1;
+ unsigned int can_multi_ch: 1;
+ unsigned int do_soft_ac3: 1;
+
+ unsigned int spdif_playback_avail: 1; /* spdif ready? */
+ unsigned int spdif_playback_enabled: 1; /* spdif switch enabled? */
+ int spdif_counter; /* for software AC3 */
+
+ unsigned int dig_status;
+ unsigned int dig_pcm_status;
+
+ snd_pcm_hardware_t *hw_info[3]; /* for playbacks */
+
+ int opened[2]; /* open mode */
+ struct semaphore open_mutex;
+
+ unsigned int mixer_insensitive: 1;
+ snd_kcontrol_t *mixer_res_ctl[CM_SAVED_MIXERS];
+ int mixer_res_status[CM_SAVED_MIXERS];
+
+ opl3_t *opl3;
+ snd_hwdep_t *opl3hwdep;
+
+ cmipci_pcm_t channel[2]; /* ch0 - DAC, ch1 - ADC or 2nd DAC */
+
+ /* external MIDI */
+ snd_rawmidi_t *rmidi;
+
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+
+ spinlock_t reg_lock;
+};
+
+
+/* read/write operations for dword register */
+inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data)
+{
+ outl(data, cm->iobase + cmd);
+}
+inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd)
+{
+ return inl(cm->iobase + cmd);
+}
+
+/* read/write operations for word register */
+inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data)
+{
+ outw(data, cm->iobase + cmd);
+}
+inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd)
+{
+ return inw(cm->iobase + cmd);
+}
+
+/* read/write operations for byte register */
+inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data)
+{
+ outb(data, cm->iobase + cmd);
+}
+
+inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd)
+{
+ return inb(cm->iobase + cmd);
+}
+
+/* bit operations for dword register */
+static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+{
+ unsigned int val;
+ val = inl(cm->iobase + cmd);
+ val |= flag;
+ outl(val, cm->iobase + cmd);
+}
+
+static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+{
+ unsigned int val;
+ val = inl(cm->iobase + cmd);
+ val &= ~flag;
+ outl(val, cm->iobase + cmd);
+}
+
+#if 0 // not used
+/* bit operations for byte register */
+static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+{
+ unsigned char val;
+ val = inb(cm->iobase + cmd);
+ val |= flag;
+ outb(val, cm->iobase + cmd);
+}
+
+static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+{
+ unsigned char val;
+ val = inb(cm->iobase + cmd);
+ val &= ~flag;
+ outb(val, cm->iobase + cmd);
+}
+#endif
+
+
+/*
+ * PCM interface
+ */
+
+/*
+ * calculate frequency
+ */
+
+static unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };
+
+static unsigned int snd_cmipci_rate_freq(unsigned int rate)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(rates); i++) {
+ if (rates[i] == rate)
+ return i;
+ }
+ snd_BUG();
+ return 0;
+}
+
+#ifdef USE_VAR48KRATE
+/*
+ * Determine PLL values for frequency setup, maybe the CMI8338 (CMI8738???)
+ * does it this way .. maybe not. Never get any information from C-Media about
+ * that <werner@suse.de>.
+ */
+static int snd_cmipci_pll_rmn(unsigned int rate, unsigned int adcmult, int *r, int *m, int *n)
+{
+ unsigned int delta, tolerance;
+ int xm, xn, xr;
+
+ for (*r = 0; rate < CM_MAXIMUM_RATE/adcmult; *r += (1<<5))
+ rate <<= 1;
+ *n = -1;
+ if (*r > 0xff)
+ goto out;
+ tolerance = rate*CM_TOLERANCE_RATE;
+
+ for (xn = (1+2); xn < (0x1f+2); xn++) {
+ for (xm = (1+2); xm < (0xff+2); xm++) {
+ xr = ((CM_REFFREQ_XIN/adcmult) * xm) / xn;
+
+ if (xr < rate)
+ delta = rate - xr;
+ else
+ delta = xr - rate;
+
+ /*
+ * If we found one, remember this,
+ * and try to find a closer one
+ */
+ if (delta < tolerance) {
+ tolerance = delta;
+ *m = xm - 2;
+ *n = xn - 2;
+ }
+ }
+ }
+out:
+ return (*n > -1);
+}
+
+/*
+ * Program pll register bits, I assume that the 8 registers 0xf8 upto 0xff
+ * are mapped onto the 8 ADC/DAC sampling frequency which can be choosen
+ * at the register CM_REG_FUNCTRL1 (0x04).
+ * Problem: other ways are also possible (any information about that?)
+ */
+static void snd_cmipci_set_pll(cmipci_t *cm, unsigned int rate, unsigned int slot)
+{
+ unsigned int reg = CM_REG_PLL + slot;
+ /*
+ * Guess that this programs at reg. 0x04 the pos 15:13/12:10
+ * for DSFC/ASFC (000 upto 111).
+ */
+
+ /* FIXME: Init (Do we've to set an other register first before programming?) */
+
+ /* FIXME: Is this correct? Or shouldn't the m/n/r values be used for that? */
+ snd_cmipci_write_b(cm, reg, rate>>8);
+ snd_cmipci_write_b(cm, reg, rate&0xff);
+
+ /* FIXME: Setup (Do we've to set an other register first to enable this?) */
+}
+#endif /* USE_VAR48KRATE */
+
+static int snd_cmipci_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_cmipci_playback2_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ if (params_channels(hw_params) > 2) {
+ down(&cm->open_mutex);
+ if (cm->opened[CM_CH_PLAY]) {
+ up(&cm->open_mutex);
+ return -EBUSY;
+ }
+ /* reserve the channel A */
+ cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI;
+ up(&cm->open_mutex);
+ }
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static void snd_cmipci_ch_reset(cmipci_t *cm, int ch)
+{
+ int reset = CM_RST_CH0 << (cm->channel[ch].ch);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset);
+ udelay(10);
+}
+
+static int snd_cmipci_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+
+/*
+ */
+
+static unsigned int hw_channels[] = {1, 2, 4, 5, 6, 8};
+static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = {
+ .count = 3,
+ .list = hw_channels,
+ .mask = 0,
+};
+static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = {
+ .count = 5,
+ .list = hw_channels,
+ .mask = 0,
+};
+static snd_pcm_hw_constraint_list_t hw_constraints_channels_8 = {
+ .count = 6,
+ .list = hw_channels,
+ .mask = 0,
+};
+
+static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels)
+{
+ if (channels > 2) {
+ if (! cm->can_multi_ch)
+ return -EINVAL;
+ if (rec->fmt != 0x03) /* stereo 16bit only */
+ return -EINVAL;
+
+ spin_lock_irq(&cm->reg_lock);
+ snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+ if (channels > 4) {
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+ } else {
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+ }
+ if (channels >= 6) {
+ snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+ } else {
+ snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+ }
+ if (cm->chip_version == 68) {
+ if (channels == 8) {
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL_8768, CM_CHB3D8C);
+ } else {
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL_8768, CM_CHB3D8C);
+ }
+ }
+ spin_unlock_irq(&cm->reg_lock);
+
+ } else {
+ if (cm->can_multi_ch) {
+ spin_lock_irq(&cm->reg_lock);
+ snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+ snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+ spin_unlock_irq(&cm->reg_lock);
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * prepare playback/capture channel
+ * channel to be used must have been set in rec->ch.
+ */
+static int snd_cmipci_pcm_prepare(cmipci_t *cm, cmipci_pcm_t *rec,
+ snd_pcm_substream_t *substream)
+{
+ unsigned int reg, freq, val;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ rec->fmt = 0;
+ rec->shift = 0;
+ if (snd_pcm_format_width(runtime->format) >= 16) {
+ rec->fmt |= 0x02;
+ if (snd_pcm_format_width(runtime->format) > 16)
+ rec->shift++; /* 24/32bit */
+ }
+ if (runtime->channels > 1)
+ rec->fmt |= 0x01;
+ if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) {
+ snd_printd("cannot set dac channels\n");
+ return -EINVAL;
+ }
+
+ rec->offset = runtime->dma_addr;
+ /* buffer and period sizes in frame */
+ rec->dma_size = runtime->buffer_size << rec->shift;
+ rec->period_size = runtime->period_size << rec->shift;
+ if (runtime->channels > 2) {
+ /* multi-channels */
+ rec->dma_size = (rec->dma_size * runtime->channels) / 2;
+ rec->period_size = (rec->period_size * runtime->channels) / 2;
+ }
+
+ spin_lock_irq(&cm->reg_lock);
+
+ /* set buffer address */
+ reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
+ snd_cmipci_write(cm, reg, rec->offset);
+ /* program sample counts */
+ reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
+ snd_cmipci_write_w(cm, reg, rec->dma_size - 1);
+ snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1);
+
+ /* set adc/dac flag */
+ val = rec->ch ? CM_CHADC1 : CM_CHADC0;
+ if (rec->is_dac)
+ cm->ctrl &= ~val;
+ else
+ cm->ctrl |= val;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+ //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+
+ /* set sample rate */
+ freq = snd_cmipci_rate_freq(runtime->rate);
+ val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
+ if (rec->ch) {
+ val &= ~CM_ASFC_MASK;
+ val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK;
+ } else {
+ val &= ~CM_DSFC_MASK;
+ val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK;
+ }
+ snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
+ //snd_printd("cmipci: functrl1 = %08x\n", val);
+
+ /* set format */
+ val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
+ if (rec->ch) {
+ val &= ~CM_CH1FMT_MASK;
+ val |= rec->fmt << CM_CH1FMT_SHIFT;
+ } else {
+ val &= ~CM_CH0FMT_MASK;
+ val |= rec->fmt << CM_CH0FMT_SHIFT;
+ }
+ snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
+ //snd_printd("cmipci: chformat = %08x\n", val);
+
+ rec->running = 0;
+ spin_unlock_irq(&cm->reg_lock);
+
+ return 0;
+}
+
+/*
+ * PCM trigger/stop
+ */
+static int snd_cmipci_pcm_trigger(cmipci_t *cm, cmipci_pcm_t *rec,
+ snd_pcm_substream_t *substream, int cmd)
+{
+ unsigned int inthld, chen, reset, pause;
+ int result = 0;
+
+ inthld = CM_CH0_INT_EN << rec->ch;
+ chen = CM_CHEN0 << rec->ch;
+ reset = CM_RST_CH0 << rec->ch;
+ pause = CM_PAUSE0 << rec->ch;
+
+ spin_lock(&cm->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ rec->running = 1;
+ /* set interrupt */
+ snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld);
+ cm->ctrl |= chen;
+ /* enable channel */
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+ //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ rec->running = 0;
+ /* disable interrupt */
+ snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld);
+ /* reset */
+ cm->ctrl &= ~chen;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ cm->ctrl |= pause;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cm->ctrl &= ~pause;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ spin_unlock(&cm->reg_lock);
+ return result;
+}
+
+/*
+ * return the current pointer
+ */
+static snd_pcm_uframes_t snd_cmipci_pcm_pointer(cmipci_t *cm, cmipci_pcm_t *rec,
+ snd_pcm_substream_t *substream)
+{
+ size_t ptr;
+ unsigned int reg;
+ if (!rec->running)
+ return 0;
+#if 1 // this seems better..
+ reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
+ ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1);
+ ptr >>= rec->shift;
+#else
+ reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
+ ptr = snd_cmipci_read(cm, reg) - rec->offset;
+ ptr = bytes_to_frames(substream->runtime, ptr);
+#endif
+ if (substream->runtime->channels > 2)
+ ptr = (ptr * 2) / substream->runtime->channels;
+ return ptr;
+}
+
+/*
+ * playback
+ */
+
+static int snd_cmipci_playback_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_cmipci_playback_pointer(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+
+
+/*
+ * capture
+ */
+
+static int snd_cmipci_capture_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_cmipci_capture_pointer(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+
+/*
+ * hw preparation for spdif
+ */
+
+static int snd_cmipci_spdif_default_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_cmipci_spdif_default_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < 4; i++)
+ ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cmipci_spdif_default_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+ int i, change;
+ unsigned int val;
+
+ val = 0;
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < 4; i++)
+ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
+ change = val != chip->dig_status;
+ chip->dig_status = val;
+ spin_unlock_irq(&chip->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_cmipci_spdif_default __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_cmipci_spdif_default_info,
+ .get = snd_cmipci_spdif_default_get,
+ .put = snd_cmipci_spdif_default_put
+};
+
+static int snd_cmipci_spdif_mask_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_cmipci_spdif_mask_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t *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 snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_cmipci_spdif_mask_info,
+ .get = snd_cmipci_spdif_mask_get,
+};
+
+static int snd_cmipci_spdif_stream_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_cmipci_spdif_stream_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < 4; i++)
+ ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cmipci_spdif_stream_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+ int i, change;
+ unsigned int val;
+
+ val = 0;
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < 4; i++)
+ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
+ change = val != chip->dig_pcm_status;
+ chip->dig_pcm_status = val;
+ spin_unlock_irq(&chip->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_cmipci_spdif_stream __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_cmipci_spdif_stream_info,
+ .get = snd_cmipci_spdif_stream_get,
+ .put = snd_cmipci_spdif_stream_put
+};
+
+/*
+ */
+
+/* save mixer setting and mute for AC3 playback */
+static int save_mixer_state(cmipci_t *cm)
+{
+ if (! cm->mixer_insensitive) {
+ snd_ctl_elem_value_t *val;
+ unsigned int i;
+
+ val = kmalloc(sizeof(*val), GFP_ATOMIC);
+ if (!val)
+ return -ENOMEM;
+ for (i = 0; i < CM_SAVED_MIXERS; i++) {
+ snd_kcontrol_t *ctl = cm->mixer_res_ctl[i];
+ if (ctl) {
+ int event;
+ memset(val, 0, sizeof(*val));
+ ctl->get(ctl, val);
+ cm->mixer_res_status[i] = val->value.integer.value[0];
+ val->value.integer.value[0] = cm_saved_mixer[i].toggle_on;
+ event = SNDRV_CTL_EVENT_MASK_INFO;
+ if (cm->mixer_res_status[i] != val->value.integer.value[0]) {
+ ctl->put(ctl, val); /* toggle */
+ event |= SNDRV_CTL_EVENT_MASK_VALUE;
+ }
+ ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(cm->card, event, &ctl->id);
+ }
+ }
+ kfree(val);
+ cm->mixer_insensitive = 1;
+ }
+ return 0;
+}
+
+
+/* restore the previously saved mixer status */
+static void restore_mixer_state(cmipci_t *cm)
+{
+ if (cm->mixer_insensitive) {
+ snd_ctl_elem_value_t *val;
+ unsigned int i;
+
+ val = kmalloc(sizeof(*val), GFP_KERNEL);
+ if (!val)
+ return;
+ cm->mixer_insensitive = 0; /* at first clear this;
+ otherwise the changes will be ignored */
+ for (i = 0; i < CM_SAVED_MIXERS; i++) {
+ snd_kcontrol_t *ctl = cm->mixer_res_ctl[i];
+ if (ctl) {
+ int event;
+
+ memset(val, 0, sizeof(*val));
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ ctl->get(ctl, val);
+ event = SNDRV_CTL_EVENT_MASK_INFO;
+ if (val->value.integer.value[0] != cm->mixer_res_status[i]) {
+ val->value.integer.value[0] = cm->mixer_res_status[i];
+ ctl->put(ctl, val);
+ event |= SNDRV_CTL_EVENT_MASK_VALUE;
+ }
+ snd_ctl_notify(cm->card, event, &ctl->id);
+ }
+ }
+ kfree(val);
+ }
+}
+
+/* spinlock held! */
+static void setup_ac3(cmipci_t *cm, snd_pcm_substream_t *subs, int do_ac3, int rate)
+{
+ if (do_ac3) {
+ /* AC3EN for 037 */
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1);
+ /* AC3EN for 039 */
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2);
+
+ if (cm->can_ac3_hw) {
+ /* SPD24SEL for 037, 0x02 */
+ /* SPD24SEL for 039, 0x20, but cannot be set */
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+ } else { /* can_ac3_sw */
+ /* SPD32SEL for 037 & 039, 0x20 */
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+ /* set 176K sample rate to fix 033 HW bug */
+ if (cm->chip_version == 33) {
+ if (rate >= 48000) {
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+ } else {
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+ }
+ }
+ }
+
+ } else {
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2);
+
+ if (cm->can_ac3_hw) {
+ /* chip model >= 37 */
+ if (snd_pcm_format_width(subs->runtime->format) > 16) {
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+ } else {
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+ }
+ } else {
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+ }
+ }
+}
+
+static int setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3)
+{
+ int rate, err;
+
+ rate = subs->runtime->rate;
+
+ if (up && do_ac3)
+ if ((err = save_mixer_state(cm)) < 0)
+ return err;
+
+ spin_lock_irq(&cm->reg_lock);
+ cm->spdif_playback_avail = up;
+ if (up) {
+ /* they are controlled via "IEC958 Output Switch" */
+ /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
+ /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
+ if (cm->spdif_playback_enabled)
+ snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+ setup_ac3(cm, subs, do_ac3, rate);
+
+ if (rate == 48000)
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
+ else
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
+
+ } else {
+ /* they are controlled via "IEC958 Output Switch" */
+ /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
+ /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
+ snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+ setup_ac3(cm, subs, 0, 0);
+ }
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+}
+
+
+/*
+ * preparation
+ */
+
+/* playback - enable spdif only on the certain condition */
+static int snd_cmipci_playback_prepare(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ int rate = substream->runtime->rate;
+ int err, do_spdif, do_ac3 = 0;
+
+ do_spdif = ((rate == 44100 || rate == 48000) &&
+ substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE &&
+ substream->runtime->channels == 2);
+ if (do_spdif && cm->can_ac3_hw)
+ do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
+ if ((err = setup_spdif_playback(cm, substream, do_spdif, do_ac3)) < 0)
+ return err;
+ return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+/* playback (via device #2) - enable spdif always */
+static int snd_cmipci_playback_spdif_prepare(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ int err, do_ac3;
+
+ if (cm->can_ac3_hw)
+ do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
+ else
+ do_ac3 = 1; /* doesn't matter */
+ if ((err = setup_spdif_playback(cm, substream, 1, do_ac3)) < 0)
+ return err;
+ return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+static int snd_cmipci_playback_hw_free(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ setup_spdif_playback(cm, substream, 0, 0);
+ restore_mixer_state(cm);
+ return snd_cmipci_hw_free(substream);
+}
+
+/* capture */
+static int snd_cmipci_capture_prepare(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+/* capture with spdif (via device #2) */
+static int snd_cmipci_capture_spdif_prepare(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&cm->reg_lock);
+ snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+ spin_unlock_irq(&cm->reg_lock);
+
+ return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+static int snd_cmipci_capture_spdif_hw_free(snd_pcm_substream_t *subs)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(subs);
+
+ spin_lock_irq(&cm->reg_lock);
+ snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+ spin_unlock_irq(&cm->reg_lock);
+
+ return snd_cmipci_hw_free(subs);
+}
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ cmipci_t *cm = dev_id;
+ unsigned int status, mask = 0;
+
+ /* fastpath out, to ease interrupt sharing */
+ status = snd_cmipci_read(cm, CM_REG_INT_STATUS);
+ if (!(status & CM_INTR))
+ return IRQ_NONE;
+
+ /* acknowledge interrupt */
+ spin_lock(&cm->reg_lock);
+ if (status & CM_CHINT0)
+ mask |= CM_CH0_INT_EN;
+ if (status & CM_CHINT1)
+ mask |= CM_CH1_INT_EN;
+ snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask);
+ snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask);
+ spin_unlock(&cm->reg_lock);
+
+ if (cm->rmidi && (status & CM_UARTINT))
+ snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data, regs);
+
+ if (cm->pcm) {
+ if ((status & CM_CHINT0) && cm->channel[0].running)
+ snd_pcm_period_elapsed(cm->channel[0].substream);
+ if ((status & CM_CHINT1) && cm->channel[1].running)
+ snd_pcm_period_elapsed(cm->channel[1].substream);
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * h/w infos
+ */
+
+/* playback on channel A */
+static snd_pcm_hardware_t snd_cmipci_playback =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5512,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 2,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/* capture on channel B */
+static snd_pcm_hardware_t snd_cmipci_capture =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5512,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 2,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/* playback on channel B - stereo 16bit only? */
+static snd_pcm_hardware_t snd_cmipci_playback2 =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5512,
+ .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 = 2,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/* spdif playback on channel A */
+static snd_pcm_hardware_t snd_cmipci_playback_spdif =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .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 = 2,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/* spdif playback on channel A (32bit, IEC958 subframes) */
+static snd_pcm_hardware_t snd_cmipci_playback_iec958_subframe =
+{
+ .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_IEC958_SUBFRAME_LE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .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 = 2,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/* spdif capture on channel B */
+static snd_pcm_hardware_t snd_cmipci_capture_spdif =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .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 = 2,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/*
+ * check device open/close
+ */
+static int open_device_check(cmipci_t *cm, int mode, snd_pcm_substream_t *subs)
+{
+ int ch = mode & CM_OPEN_CH_MASK;
+
+ /* FIXME: a file should wait until the device becomes free
+ * when it's opened on blocking mode. however, since the current
+ * pcm framework doesn't pass file pointer before actually opened,
+ * we can't know whether blocking mode or not in open callback..
+ */
+ down(&cm->open_mutex);
+ if (cm->opened[ch]) {
+ up(&cm->open_mutex);
+ return -EBUSY;
+ }
+ cm->opened[ch] = mode;
+ cm->channel[ch].substream = subs;
+ if (! (mode & CM_OPEN_DAC)) {
+ /* disable dual DAC mode */
+ cm->channel[ch].is_dac = 0;
+ spin_lock_irq(&cm->reg_lock);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
+ spin_unlock_irq(&cm->reg_lock);
+ }
+ up(&cm->open_mutex);
+ return 0;
+}
+
+static void close_device_check(cmipci_t *cm, int mode)
+{
+ int ch = mode & CM_OPEN_CH_MASK;
+
+ down(&cm->open_mutex);
+ if (cm->opened[ch] == mode) {
+ if (cm->channel[ch].substream) {
+ snd_cmipci_ch_reset(cm, ch);
+ cm->channel[ch].running = 0;
+ cm->channel[ch].substream = NULL;
+ }
+ cm->opened[ch] = 0;
+ if (! cm->channel[ch].is_dac) {
+ /* enable dual DAC mode again */
+ cm->channel[ch].is_dac = 1;
+ spin_lock_irq(&cm->reg_lock);
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
+ spin_unlock_irq(&cm->reg_lock);
+ }
+ }
+ up(&cm->open_mutex);
+}
+
+/*
+ */
+
+static int snd_cmipci_playback_open(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0)
+ return err;
+ runtime->hw = snd_cmipci_playback;
+ runtime->hw.channels_max = cm->max_channels;
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+ cm->dig_pcm_status = cm->dig_status;
+ return 0;
+}
+
+static int snd_cmipci_capture_open(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0)
+ return err;
+ runtime->hw = snd_cmipci_capture;
+ if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording
+ runtime->hw.rate_min = 41000;
+ runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
+ }
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+ return 0;
+}
+
+static int snd_cmipci_playback2_open(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */
+ return err;
+ runtime->hw = snd_cmipci_playback2;
+ down(&cm->open_mutex);
+ if (! cm->opened[CM_CH_PLAY]) {
+ if (cm->can_multi_ch) {
+ runtime->hw.channels_max = cm->max_channels;
+ if (cm->max_channels == 4)
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4);
+ else if (cm->max_channels == 6)
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6);
+ else if (cm->max_channels == 8)
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_8);
+ }
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+ }
+ up(&cm->open_mutex);
+ return 0;
+}
+
+static int snd_cmipci_playback_spdif_open(snd_pcm_substream_t *substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */
+ return err;
+ if (cm->can_ac3_hw) {
+ runtime->hw = snd_cmipci_playback_spdif;
+ if (cm->chip_version >= 37)
+ runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ } else {
+ runtime->hw = snd_cmipci_playback_iec958_subframe;
+ }
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
+ cm->dig_pcm_status = cm->dig_status;
+ return 0;
+}
+
+static int snd_cmipci_capture_spdif_open(snd_pcm_substream_t * substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
+ return err;
+ runtime->hw = snd_cmipci_capture_spdif;
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
+ return 0;
+}
+
+
+/*
+ */
+
+static int snd_cmipci_playback_close(snd_pcm_substream_t * substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ close_device_check(cm, CM_OPEN_PLAYBACK);
+ return 0;
+}
+
+static int snd_cmipci_capture_close(snd_pcm_substream_t * substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ close_device_check(cm, CM_OPEN_CAPTURE);
+ return 0;
+}
+
+static int snd_cmipci_playback2_close(snd_pcm_substream_t * substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ close_device_check(cm, CM_OPEN_PLAYBACK2);
+ close_device_check(cm, CM_OPEN_PLAYBACK_MULTI);
+ return 0;
+}
+
+static int snd_cmipci_playback_spdif_close(snd_pcm_substream_t * substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ close_device_check(cm, CM_OPEN_SPDIF_PLAYBACK);
+ return 0;
+}
+
+static int snd_cmipci_capture_spdif_close(snd_pcm_substream_t * substream)
+{
+ cmipci_t *cm = snd_pcm_substream_chip(substream);
+ close_device_check(cm, CM_OPEN_SPDIF_CAPTURE);
+ return 0;
+}
+
+
+/*
+ */
+
+static snd_pcm_ops_t snd_cmipci_playback_ops = {
+ .open = snd_cmipci_playback_open,
+ .close = snd_cmipci_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cmipci_hw_params,
+ .hw_free = snd_cmipci_playback_hw_free,
+ .prepare = snd_cmipci_playback_prepare,
+ .trigger = snd_cmipci_playback_trigger,
+ .pointer = snd_cmipci_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_cmipci_capture_ops = {
+ .open = snd_cmipci_capture_open,
+ .close = snd_cmipci_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cmipci_hw_params,
+ .hw_free = snd_cmipci_hw_free,
+ .prepare = snd_cmipci_capture_prepare,
+ .trigger = snd_cmipci_capture_trigger,
+ .pointer = snd_cmipci_capture_pointer,
+};
+
+static snd_pcm_ops_t snd_cmipci_playback2_ops = {
+ .open = snd_cmipci_playback2_open,
+ .close = snd_cmipci_playback2_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cmipci_playback2_hw_params,
+ .hw_free = snd_cmipci_hw_free,
+ .prepare = snd_cmipci_capture_prepare, /* channel B */
+ .trigger = snd_cmipci_capture_trigger, /* channel B */
+ .pointer = snd_cmipci_capture_pointer, /* channel B */
+};
+
+static snd_pcm_ops_t snd_cmipci_playback_spdif_ops = {
+ .open = snd_cmipci_playback_spdif_open,
+ .close = snd_cmipci_playback_spdif_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cmipci_hw_params,
+ .hw_free = snd_cmipci_playback_hw_free,
+ .prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */
+ .trigger = snd_cmipci_playback_trigger,
+ .pointer = snd_cmipci_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_cmipci_capture_spdif_ops = {
+ .open = snd_cmipci_capture_spdif_open,
+ .close = snd_cmipci_capture_spdif_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cmipci_hw_params,
+ .hw_free = snd_cmipci_capture_spdif_hw_free,
+ .prepare = snd_cmipci_capture_spdif_prepare,
+ .trigger = snd_cmipci_capture_trigger,
+ .pointer = snd_cmipci_capture_pointer,
+};
+
+
+/*
+ */
+
+static void snd_cmipci_pcm_free(snd_pcm_t *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_cmipci_pcm_new(cmipci_t *cm, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_ops);
+
+ pcm->private_data = cm;
+ pcm->private_free = snd_cmipci_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "C-Media PCI DAC/ADC");
+ cm->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+
+ return 0;
+}
+
+static int __devinit snd_cmipci_pcm2_new(cmipci_t *cm, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 0, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback2_ops);
+
+ pcm->private_data = cm;
+ pcm->private_free = snd_cmipci_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "C-Media PCI 2nd DAC");
+ cm->pcm2 = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+
+ return 0;
+}
+
+static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops);
+
+ pcm->private_data = cm;
+ pcm->private_free = snd_cmipci_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "C-Media PCI IEC958");
+ cm->pcm_spdif = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+
+ return 0;
+}
+
+/*
+ * mixer interface:
+ * - CM8338/8738 has a compatible mixer interface with SB16, but
+ * lack of some elements like tone control, i/o gain and AGC.
+ * - Access to native registers:
+ * - A 3D switch
+ * - Output mute switches
+ */
+
+static void snd_cmipci_mixer_write(cmipci_t *s, unsigned char idx, unsigned char data)
+{
+ outb(idx, s->iobase + CM_REG_SB16_ADDR);
+ outb(data, s->iobase + CM_REG_SB16_DATA);
+}
+
+static unsigned char snd_cmipci_mixer_read(cmipci_t *s, unsigned char idx)
+{
+ unsigned char v;
+
+ outb(idx, s->iobase + CM_REG_SB16_ADDR);
+ v = inb(s->iobase + CM_REG_SB16_DATA);
+ return v;
+}
+
+/*
+ * general mixer element
+ */
+typedef struct cmipci_sb_reg {
+ unsigned int left_reg, right_reg;
+ unsigned int left_shift, right_shift;
+ unsigned int mask;
+ unsigned int invert: 1;
+ unsigned int stereo: 1;
+} cmipci_sb_reg_t;
+
+#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \
+ ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23))
+
+#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_cmipci_info_volume, \
+ .get = snd_cmipci_get_volume, .put = snd_cmipci_put_volume, \
+ .private_value = COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \
+}
+
+#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1)
+#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0)
+#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1)
+#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0)
+
+static void cmipci_sb_reg_decode(cmipci_sb_reg_t *r, unsigned long val)
+{
+ r->left_reg = val & 0xff;
+ r->right_reg = (val >> 8) & 0xff;
+ r->left_shift = (val >> 16) & 0x07;
+ r->right_shift = (val >> 19) & 0x07;
+ r->invert = (val >> 22) & 1;
+ r->stereo = (val >> 23) & 1;
+ r->mask = (val >> 24) & 0xff;
+}
+
+static int snd_cmipci_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ cmipci_sb_reg_t reg;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = reg.stereo + 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = reg.mask;
+ return 0;
+}
+
+static int snd_cmipci_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ cmipci_sb_reg_t reg;
+ int val;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ spin_lock_irq(&cm->reg_lock);
+ val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ ucontrol->value.integer.value[0] = val;
+ if (reg.stereo) {
+ val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ ucontrol->value.integer.value[1] = val;
+ }
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+}
+
+static int snd_cmipci_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ cmipci_sb_reg_t reg;
+ int change;
+ int left, right, oleft, oright;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ left = ucontrol->value.integer.value[0] & reg.mask;
+ if (reg.invert)
+ left = reg.mask - left;
+ left <<= reg.left_shift;
+ if (reg.stereo) {
+ right = ucontrol->value.integer.value[1] & reg.mask;
+ if (reg.invert)
+ right = reg.mask - right;
+ right <<= reg.right_shift;
+ } else
+ right = 0;
+ spin_lock_irq(&cm->reg_lock);
+ oleft = snd_cmipci_mixer_read(cm, reg.left_reg);
+ left |= oleft & ~(reg.mask << reg.left_shift);
+ change = left != oleft;
+ if (reg.stereo) {
+ if (reg.left_reg != reg.right_reg) {
+ snd_cmipci_mixer_write(cm, reg.left_reg, left);
+ oright = snd_cmipci_mixer_read(cm, reg.right_reg);
+ } else
+ oright = left;
+ right |= oright & ~(reg.mask << reg.right_shift);
+ change |= right != oright;
+ snd_cmipci_mixer_write(cm, reg.right_reg, right);
+ } else
+ snd_cmipci_mixer_write(cm, reg.left_reg, left);
+ spin_unlock_irq(&cm->reg_lock);
+ return change;
+}
+
+/*
+ * input route (left,right) -> (left,right)
+ */
+#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_cmipci_info_input_sw, \
+ .get = snd_cmipci_get_input_sw, .put = snd_cmipci_put_input_sw, \
+ .private_value = COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \
+}
+
+static int snd_cmipci_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 4;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_cmipci_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ cmipci_sb_reg_t reg;
+ int val1, val2;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ spin_lock_irq(&cm->reg_lock);
+ val1 = snd_cmipci_mixer_read(cm, reg.left_reg);
+ val2 = snd_cmipci_mixer_read(cm, reg.right_reg);
+ spin_unlock_irq(&cm->reg_lock);
+ ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1;
+ ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1;
+ ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1;
+ ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1;
+ return 0;
+}
+
+static int snd_cmipci_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ cmipci_sb_reg_t reg;
+ int change;
+ int val1, val2, oval1, oval2;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ spin_lock_irq(&cm->reg_lock);
+ oval1 = snd_cmipci_mixer_read(cm, reg.left_reg);
+ oval2 = snd_cmipci_mixer_read(cm, reg.right_reg);
+ val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
+ val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
+ val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift;
+ val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift;
+ val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift;
+ val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift;
+ change = val1 != oval1 || val2 != oval2;
+ snd_cmipci_mixer_write(cm, reg.left_reg, val1);
+ snd_cmipci_mixer_write(cm, reg.right_reg, val2);
+ spin_unlock_irq(&cm->reg_lock);
+ return change;
+}
+
+/*
+ * native mixer switches/volumes
+ */
+
+#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_cmipci_info_native_mixer, \
+ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+ .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \
+}
+
+#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_cmipci_info_native_mixer, \
+ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+ .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \
+}
+
+#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_cmipci_info_native_mixer, \
+ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+ .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \
+}
+
+#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_cmipci_info_native_mixer, \
+ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+ .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \
+}
+
+static int snd_cmipci_info_native_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ cmipci_sb_reg_t reg;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = reg.stereo + 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = reg.mask;
+ return 0;
+
+}
+
+static int snd_cmipci_get_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ cmipci_sb_reg_t reg;
+ unsigned char oreg, val;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ spin_lock_irq(&cm->reg_lock);
+ oreg = inb(cm->iobase + reg.left_reg);
+ val = (oreg >> reg.left_shift) & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ ucontrol->value.integer.value[0] = val;
+ if (reg.stereo) {
+ val = (oreg >> reg.right_shift) & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ ucontrol->value.integer.value[1] = val;
+ }
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+}
+
+static int snd_cmipci_put_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ cmipci_sb_reg_t reg;
+ unsigned char oreg, nreg, val;
+
+ cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+ spin_lock_irq(&cm->reg_lock);
+ oreg = inb(cm->iobase + reg.left_reg);
+ val = ucontrol->value.integer.value[0] & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ nreg = oreg & ~(reg.mask << reg.left_shift);
+ nreg |= (val << reg.left_shift);
+ if (reg.stereo) {
+ val = ucontrol->value.integer.value[1] & reg.mask;
+ if (reg.invert)
+ val = reg.mask - val;
+ nreg &= ~(reg.mask << reg.right_shift);
+ nreg |= (val << reg.right_shift);
+ }
+ outb(nreg, cm->iobase + reg.left_reg);
+ spin_unlock_irq(&cm->reg_lock);
+ return (nreg != oreg);
+}
+
+/*
+ * special case - check mixer sensitivity
+ */
+static int snd_cmipci_get_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ //cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ return snd_cmipci_get_native_mixer(kcontrol, ucontrol);
+}
+
+static int snd_cmipci_put_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ if (cm->mixer_insensitive) {
+ /* ignored */
+ return 0;
+ }
+ return snd_cmipci_put_native_mixer(kcontrol, ucontrol);
+}
+
+
+static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = {
+ CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31),
+ CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0),
+ CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31),
+ //CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1),
+ { /* switch with sensitivity */
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .info = snd_cmipci_info_native_mixer,
+ .get = snd_cmipci_get_native_mixer_sensitive,
+ .put = snd_cmipci_put_native_mixer_sensitive,
+ .private_value = COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0),
+ },
+ CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0),
+ CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31),
+ CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1),
+ CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5),
+ CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31),
+ CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1),
+ CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1),
+ CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31),
+ CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3),
+ CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3),
+ CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
+ CMIPCI_SB_SW_MONO("Mic Playback Switch", 0),
+ CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0),
+ CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+ CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
+ CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
+ CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
+ CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1),
+ CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
+};
+
+/*
+ * other switches
+ */
+
+typedef struct snd_cmipci_switch_args {
+ int reg; /* register index */
+ unsigned int mask; /* mask bits */
+ unsigned int mask_on; /* mask bits to turn on */
+ unsigned int is_byte: 1; /* byte access? */
+ unsigned int ac3_sensitive: 1; /* access forbidden during non-audio operation? */
+} snd_cmipci_switch_args_t;
+
+static int snd_cmipci_uswitch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int _snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args)
+{
+ unsigned int val;
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&cm->reg_lock);
+ if (args->ac3_sensitive && cm->mixer_insensitive) {
+ ucontrol->value.integer.value[0] = 0;
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+ }
+ if (args->is_byte)
+ val = inb(cm->iobase + args->reg);
+ else
+ val = snd_cmipci_read(cm, args->reg);
+ ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0;
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+}
+
+static int snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value;
+ snd_assert(args != NULL, return -EINVAL);
+ return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args);
+}
+
+static int _snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args)
+{
+ unsigned int val;
+ int change;
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&cm->reg_lock);
+ if (args->ac3_sensitive && cm->mixer_insensitive) {
+ /* ignored */
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+ }
+ if (args->is_byte)
+ val = inb(cm->iobase + args->reg);
+ else
+ val = snd_cmipci_read(cm, args->reg);
+ change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0);
+ if (change) {
+ val &= ~args->mask;
+ if (ucontrol->value.integer.value[0])
+ val |= args->mask_on;
+ else
+ val |= (args->mask & ~args->mask_on);
+ if (args->is_byte)
+ outb((unsigned char)val, cm->iobase + args->reg);
+ else
+ snd_cmipci_write(cm, args->reg, val);
+ }
+ spin_unlock_irq(&cm->reg_lock);
+ return change;
+}
+
+static int snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value;
+ snd_assert(args != NULL, return -EINVAL);
+ return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args);
+}
+
+#define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \
+static snd_cmipci_switch_args_t cmipci_switch_arg_##sname = { \
+ .reg = xreg, \
+ .mask = xmask, \
+ .mask_on = xmask_on, \
+ .is_byte = xis_byte, \
+ .ac3_sensitive = xac3, \
+}
+
+#define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \
+ DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask, xis_byte, xac3)
+
+#if 0 /* these will be controlled in pcm device */
+DEFINE_BIT_SWITCH_ARG(spdif_in, CM_REG_FUNCTRL1, CM_SPDF_1, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_out, CM_REG_FUNCTRL1, CM_SPDF_0, 0, 0);
+#endif
+DEFINE_BIT_SWITCH_ARG(spdif_in_sel1, CM_REG_CHFORMAT, CM_SPDIF_SELECT1, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_in_sel2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT2, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_copyright, CM_REG_LEGACY_CTRL, CM_SPDCOPYRHT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_dac_out, CM_REG_LEGACY_CTRL, CM_DAC2SPDO, 0, 1);
+DEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */
+// DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0);
+/* DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_CHFORMAT, CM_SPDIF_INVERSE, 0, 0); */
+DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_MISC, CM_SPDIF_INVERSE, 1, 0);
+DEFINE_BIT_SWITCH_ARG(spdi_phase2, CM_REG_CHFORMAT, CM_SPDIF_INVERSE2, 0, 0);
+#if CM_CH_PLAY == 1
+DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */
+#else
+DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0);
+#endif
+DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0);
+DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
+DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
+// DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); /* now module option */
+DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0);
+
+#define DEFINE_SWITCH(sname, stype, sarg) \
+{ .name = sname, \
+ .iface = stype, \
+ .info = snd_cmipci_uswitch_info, \
+ .get = snd_cmipci_uswitch_get, \
+ .put = snd_cmipci_uswitch_put, \
+ .private_value = (unsigned long)&cmipci_switch_arg_##sarg,\
+}
+
+#define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg)
+#define DEFINE_MIXER_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_MIXER, sarg)
+
+
+/*
+ * callbacks for spdif output switch
+ * needs toggle two registers..
+ */
+static int snd_cmipci_spdout_enable_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ int changed;
+ changed = _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable);
+ changed |= _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac);
+ return changed;
+}
+
+static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+ int changed;
+ changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable);
+ changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac);
+ if (changed) {
+ if (ucontrol->value.integer.value[0]) {
+ if (chip->spdif_playback_avail)
+ snd_cmipci_set_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+ } else {
+ if (chip->spdif_playback_avail)
+ snd_cmipci_clear_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+ }
+ }
+ chip->spdif_playback_enabled = ucontrol->value.integer.value[0];
+ return changed;
+}
+
+
+/* both for CM8338/8738 */
+static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = {
+ DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
+ DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear),
+};
+
+/* for non-multichannel chips */
+static snd_kcontrol_new_t snd_cmipci_nomulti_switch __devinitdata =
+DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac);
+
+/* only for CM8738 */
+static snd_kcontrol_new_t snd_cmipci_8738_mixer_switches[] __devinitdata = {
+#if 0 /* controlled in pcm device */
+ DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in),
+ DEFINE_MIXER_SWITCH("IEC958 Out", spdif_out),
+ DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac),
+#endif
+ // DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable),
+ { .name = "IEC958 Output Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_cmipci_uswitch_info,
+ .get = snd_cmipci_spdout_enable_get,
+ .put = snd_cmipci_spdout_enable_put,
+ },
+ DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid),
+ DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright),
+ DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v),
+// DEFINE_MIXER_SWITCH("IEC958 In/Out 48KHz", spdo_48k),
+ DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop),
+ DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor),
+};
+
+/* only for model 033/037 */
+static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = {
+ DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out),
+ DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase),
+ DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel1),
+};
+
+/* only for model 039 or later */
+static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = {
+ DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
+ DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
+ DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
+ DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */
+};
+
+/* card control switches */
+static snd_kcontrol_new_t snd_cmipci_control_switches[] __devinitdata = {
+ // DEFINE_CARD_SWITCH("Joystick", joystick), /* now module option */
+ DEFINE_CARD_SWITCH("Modem", modem),
+};
+
+
+static int __devinit snd_cmipci_mixer_new(cmipci_t *cm, int pcm_spdif_device)
+{
+ snd_card_t *card;
+ snd_kcontrol_new_t *sw;
+ snd_kcontrol_t *kctl;
+ unsigned int idx;
+ int err;
+
+ snd_assert(cm != NULL && cm->card != NULL, return -EINVAL);
+
+ card = cm->card;
+
+ strcpy(card->mixername, "CMedia PCI");
+
+ spin_lock_irq(&cm->reg_lock);
+ snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */
+ spin_unlock_irq(&cm->reg_lock);
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixers); idx++) {
+ if (cm->chip_version == 68) { // 8768 has no PCM volume
+ if (!strcmp(snd_cmipci_mixers[idx].name,
+ "PCM Playback Volume"))
+ continue;
+ }
+ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0)
+ return err;
+ }
+
+ /* mixer switches */
+ sw = snd_cmipci_mixer_switches;
+ for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixer_switches); idx++, sw++) {
+ err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+ if (err < 0)
+ return err;
+ }
+ if (! cm->can_multi_ch) {
+ err = snd_ctl_add(cm->card, snd_ctl_new1(&snd_cmipci_nomulti_switch, cm));
+ if (err < 0)
+ return err;
+ }
+ if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 ||
+ cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) {
+ sw = snd_cmipci_8738_mixer_switches;
+ for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_8738_mixer_switches); idx++, sw++) {
+ err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+ if (err < 0)
+ return err;
+ }
+ if (cm->can_ac3_hw) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0)
+ return err;
+ kctl->id.device = pcm_spdif_device;
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0)
+ return err;
+ kctl->id.device = pcm_spdif_device;
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0)
+ return err;
+ kctl->id.device = pcm_spdif_device;
+ }
+ if (cm->chip_version <= 37) {
+ sw = snd_cmipci_old_mixer_switches;
+ for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_old_mixer_switches); idx++, sw++) {
+ err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+ if (cm->chip_version >= 39) {
+ sw = snd_cmipci_extra_mixer_switches;
+ for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_extra_mixer_switches); idx++, sw++) {
+ err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* card switches */
+ sw = snd_cmipci_control_switches;
+ for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) {
+ err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+ if (err < 0)
+ return err;
+ }
+
+ for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
+ snd_ctl_elem_id_t id;
+ snd_kcontrol_t *ctl;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, cm_saved_mixer[idx].name);
+ if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL)
+ cm->mixer_res_ctl[idx] = ctl;
+ }
+
+ return 0;
+}
+
+
+/*
+ * proc interface
+ */
+
+#ifdef CONFIG_PROC_FS
+static void snd_cmipci_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t *buffer)
+{
+ cmipci_t *cm = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "%s\n\n", cm->card->longname);
+ for (i = 0; i < 0x40; i++) {
+ int v = inb(cm->iobase + i);
+ if (i % 4 == 0)
+ snd_iprintf(buffer, "%02x: ", i);
+ snd_iprintf(buffer, "%02x", v);
+ if (i % 4 == 3)
+ snd_iprintf(buffer, "\n");
+ else
+ snd_iprintf(buffer, " ");
+ }
+}
+
+static void __devinit snd_cmipci_proc_init(cmipci_t *cm)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(cm->card, "cmipci", &entry))
+ snd_info_set_text_ops(entry, cm, 1024, snd_cmipci_proc_read);
+}
+#else /* !CONFIG_PROC_FS */
+static inline void snd_cmipci_proc_init(cmipci_t *cm) {}
+#endif
+
+
+static struct pci_device_id snd_cmipci_ids[] = {
+ {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AL, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,},
+};
+
+
+/*
+ * check chip version and capabilities
+ * driver name is modified according to the chip model
+ */
+static void __devinit query_chip(cmipci_t *cm)
+{
+ unsigned int detect;
+
+ /* check reg 0Ch, bit 24-31 */
+ detect = snd_cmipci_read(cm, CM_REG_INT_HLDCLR) & CM_CHIP_MASK2;
+ if (! detect) {
+ /* check reg 08h, bit 24-28 */
+ detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1;
+ if (! detect) {
+ cm->chip_version = 33;
+ cm->max_channels = 2;
+ if (cm->do_soft_ac3)
+ cm->can_ac3_sw = 1;
+ else
+ cm->can_ac3_hw = 1;
+ cm->has_dual_dac = 1;
+ } else {
+ cm->chip_version = 37;
+ cm->max_channels = 2;
+ cm->can_ac3_hw = 1;
+ cm->has_dual_dac = 1;
+ }
+ } else {
+ /* check reg 0Ch, bit 26 */
+ if (detect & CM_CHIP_8768) {
+ cm->chip_version = 68;
+ cm->max_channels = 8;
+ cm->can_ac3_hw = 1;
+ cm->has_dual_dac = 1;
+ cm->can_multi_ch = 1;
+ } else if (detect & CM_CHIP_055) {
+ cm->chip_version = 55;
+ cm->max_channels = 6;
+ cm->can_ac3_hw = 1;
+ cm->has_dual_dac = 1;
+ cm->can_multi_ch = 1;
+ } else if (detect & CM_CHIP_039) {
+ cm->chip_version = 39;
+ if (detect & CM_CHIP_039_6CH) /* 4 or 6 channels */
+ cm->max_channels = 6;
+ else
+ cm->max_channels = 4;
+ cm->can_ac3_hw = 1;
+ cm->has_dual_dac = 1;
+ cm->can_multi_ch = 1;
+ } else {
+ printk(KERN_ERR "chip %x version not supported\n", detect);
+ }
+ }
+}
+
+#ifdef SUPPORT_JOYSTICK
+static int __devinit snd_cmipci_create_gameport(cmipci_t *cm, int dev)
+{
+ static int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */
+ struct gameport *gp;
+ struct resource *r = NULL;
+ int i, io_port = 0;
+
+ if (joystick_port[dev] == 0)
+ return -ENODEV;
+
+ if (joystick_port[dev] == 1) { /* auto-detect */
+ for (i = 0; ports[i]; i++) {
+ io_port = ports[i];
+ r = request_region(io_port, 1, "CMIPCI gameport");
+ if (r)
+ break;
+ }
+ } else {
+ io_port = joystick_port[dev];
+ r = request_region(io_port, 1, "CMIPCI gameport");
+ }
+
+ if (!r) {
+ printk(KERN_WARNING "cmipci: cannot reserve joystick ports\n");
+ return -EBUSY;
+ }
+
+ cm->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "cmipci: cannot allocate memory for gameport\n");
+ release_resource(r);
+ kfree_nocheck(r);
+ return -ENOMEM;
+ }
+ gameport_set_name(gp, "C-Media Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(cm->pci));
+ gameport_set_dev_parent(gp, &cm->pci->dev);
+ gp->io = io_port;
+ gameport_set_port_data(gp, r);
+
+ snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
+
+ gameport_register_port(cm->gameport);
+
+ return 0;
+}
+
+static void snd_cmipci_free_gameport(cmipci_t *cm)
+{
+ if (cm->gameport) {
+ struct resource *r = gameport_get_port_data(cm->gameport);
+
+ gameport_unregister_port(cm->gameport);
+ cm->gameport = NULL;
+
+ snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
+ release_resource(r);
+ kfree_nocheck(r);
+ }
+}
+#else
+static inline int snd_cmipci_create_gameport(cmipci_t *cm, int dev) { return -ENOSYS; }
+static inline void snd_cmipci_free_gameport(cmipci_t *cm) { }
+#endif
+
+static int snd_cmipci_free(cmipci_t *cm)
+{
+ if (cm->irq >= 0) {
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+ snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT);
+ snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */
+ snd_cmipci_ch_reset(cm, CM_CH_PLAY);
+ snd_cmipci_ch_reset(cm, CM_CH_CAPT);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
+ snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
+
+ /* reset mixer */
+ snd_cmipci_mixer_write(cm, 0, 0);
+
+ synchronize_irq(cm->irq);
+
+ free_irq(cm->irq, (void *)cm);
+ }
+
+ snd_cmipci_free_gameport(cm);
+ pci_release_regions(cm->pci);
+ pci_disable_device(cm->pci);
+ kfree(cm);
+ return 0;
+}
+
+static int snd_cmipci_dev_free(snd_device_t *device)
+{
+ cmipci_t *cm = device->device_data;
+ return snd_cmipci_free(cm);
+}
+
+static int __devinit snd_cmipci_create(snd_card_t *card, struct pci_dev *pci,
+ int dev, cmipci_t **rcmipci)
+{
+ cmipci_t *cm;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_cmipci_dev_free,
+ };
+ unsigned int val = 0;
+ long iomidi = mpu_port[dev];
+ long iosynth = fm_port[dev];
+ int pcm_index, pcm_spdif_index;
+ static struct pci_device_id intel_82437vx[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) },
+ { },
+ };
+
+ *rcmipci = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ cm = kcalloc(1, sizeof(*cm), GFP_KERNEL);
+ if (cm == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&cm->reg_lock);
+ init_MUTEX(&cm->open_mutex);
+ cm->device = pci->device;
+ cm->card = card;
+ cm->pci = pci;
+ cm->irq = -1;
+ cm->channel[0].ch = 0;
+ cm->channel[1].ch = 1;
+ cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */
+
+ if ((err = pci_request_regions(pci, card->driver)) < 0) {
+ kfree(cm);
+ pci_disable_device(pci);
+ return err;
+ }
+ cm->iobase = pci_resource_start(pci, 0);
+
+ if (request_irq(pci->irq, snd_cmipci_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)cm)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_cmipci_free(cm);
+ return -EBUSY;
+ }
+ cm->irq = pci->irq;
+
+ pci_set_master(cm->pci);
+
+ /*
+ * check chip version, max channels and capabilities
+ */
+
+ cm->chip_version = 0;
+ cm->max_channels = 2;
+ cm->do_soft_ac3 = soft_ac3[dev];
+
+ if (pci->device != PCI_DEVICE_ID_CMEDIA_CM8338A &&
+ pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B)
+ query_chip(cm);
+ /* added -MCx suffix for chip supporting multi-channels */
+ if (cm->can_multi_ch)
+ sprintf(cm->card->driver + strlen(cm->card->driver),
+ "-MC%d", cm->max_channels);
+ else if (cm->can_ac3_sw)
+ strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC");
+
+ cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+ cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+#if CM_CH_PLAY == 1
+ cm->ctrl = CM_CHADC0; /* default FUNCNTRL0 */
+#else
+ cm->ctrl = CM_CHADC1; /* default FUNCNTRL0 */
+#endif
+
+ /* initialize codec registers */
+ snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */
+ snd_cmipci_ch_reset(cm, CM_CH_PLAY);
+ snd_cmipci_ch_reset(cm, CM_CH_CAPT);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
+ snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
+
+ snd_cmipci_write(cm, CM_REG_CHFORMAT, 0);
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC|CM_N4SPK3D);
+#if CM_CH_PLAY == 1
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+#else
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+#endif
+ /* Set Bus Master Request */
+ snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_BREQ);
+
+ /* Assume TX and compatible chip set (Autodetection required for VX chip sets) */
+ switch (pci->device) {
+ case PCI_DEVICE_ID_CMEDIA_CM8738:
+ case PCI_DEVICE_ID_CMEDIA_CM8738B:
+ if (!pci_dev_present(intel_82437vx))
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_TXVX);
+ break;
+ default:
+ break;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) {
+ snd_cmipci_free(cm);
+ return err;
+ }
+
+ /* set MPU address */
+ switch (iomidi) {
+ case 0x320: val = CM_VMPU_320; break;
+ case 0x310: val = CM_VMPU_310; break;
+ case 0x300: val = CM_VMPU_300; break;
+ case 0x330: val = CM_VMPU_330; break;
+ default:
+ iomidi = 0; break;
+ }
+ if (iomidi > 0) {
+ snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
+ /* enable UART */
+ snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN);
+ }
+
+ /* set FM address */
+ val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK;
+ switch (iosynth) {
+ case 0x3E8: val |= CM_FMSEL_3E8; break;
+ case 0x3E0: val |= CM_FMSEL_3E0; break;
+ case 0x3C8: val |= CM_FMSEL_3C8; break;
+ case 0x388: val |= CM_FMSEL_388; break;
+ default:
+ iosynth = 0; break;
+ }
+ if (iosynth > 0) {
+ snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
+ /* enable FM */
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+
+ if (snd_opl3_create(card, iosynth, iosynth + 2,
+ OPL3_HW_OPL3, 0, &cm->opl3) < 0) {
+ printk(KERN_ERR "cmipci: no OPL device at 0x%lx, skipping...\n", iosynth);
+ iosynth = 0;
+ } else {
+ if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0) {
+ printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n");
+ return err;
+ }
+ }
+ }
+ if (! iosynth) {
+ /* disable FM */
+ snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val & ~CM_FMSEL_MASK);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+ }
+
+ /* reset mixer */
+ snd_cmipci_mixer_write(cm, 0, 0);
+
+ snd_cmipci_proc_init(cm);
+
+ /* create pcm devices */
+ pcm_index = pcm_spdif_index = 0;
+ if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
+ return err;
+ pcm_index++;
+ if (cm->has_dual_dac) {
+ if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+ return err;
+ pcm_index++;
+ }
+ if (cm->can_ac3_hw || cm->can_ac3_sw) {
+ pcm_spdif_index = pcm_index;
+ if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0)
+ return err;
+ }
+
+ /* create mixer interface & switches */
+ if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0)
+ return err;
+
+ if (iomidi > 0) {
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+ iomidi, 0,
+ cm->irq, 0, &cm->rmidi)) < 0) {
+ printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
+ }
+ }
+
+#ifdef USE_VAR48KRATE
+ for (val = 0; val < ARRAY_SIZE(rates); val++)
+ snd_cmipci_set_pll(cm, rates[val], val);
+
+ /*
+ * (Re-)Enable external switch spdo_48k
+ */
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K|CM_SPDF_AC97);
+#endif /* USE_VAR48KRATE */
+
+ if (snd_cmipci_create_gameport(cm, dev) < 0)
+ snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rcmipci = cm;
+ return 0;
+}
+
+/*
+ */
+
+MODULE_DEVICE_TABLE(pci, snd_cmipci_ids);
+
+static int __devinit snd_cmipci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ cmipci_t *cm;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (! enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ switch (pci->device) {
+ case PCI_DEVICE_ID_CMEDIA_CM8738:
+ case PCI_DEVICE_ID_CMEDIA_CM8738B:
+ strcpy(card->driver, "CMI8738");
+ break;
+ case PCI_DEVICE_ID_CMEDIA_CM8338A:
+ case PCI_DEVICE_ID_CMEDIA_CM8338B:
+ strcpy(card->driver, "CMI8338");
+ break;
+ default:
+ strcpy(card->driver, "CMIPCI");
+ break;
+ }
+
+ if ((err = snd_cmipci_create(card, pci, dev, &cm)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ sprintf(card->shortname, "C-Media PCI %s", card->driver);
+ sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i",
+ card->shortname,
+ cm->chip_version,
+ cm->iobase,
+ cm->irq);
+
+ //snd_printd("%s is detected\n", card->longname);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+
+}
+
+static void __devexit snd_cmipci_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+
+static struct pci_driver driver = {
+ .name = "C-Media PCI",
+ .id_table = snd_cmipci_ids,
+ .probe = snd_cmipci_probe,
+ .remove = __devexit_p(snd_cmipci_remove),
+};
+
+static int __init alsa_card_cmipci_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_cmipci_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cmipci_init)
+module_exit(alsa_card_cmipci_exit)
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
new file mode 100644
index 00000000000..d7e06b3caf9
--- /dev/null
+++ b/sound/pci/cs4281.c
@@ -0,0 +1,2136 @@
+/*
+ * Driver for Cirrus Logic CS4281 based PCI soundcard
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/ac97_codec.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Cirrus Logic CS4281");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,CS4281}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static int dual_codec[SNDRV_CARDS]; /* dual codec */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for CS4281 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for CS4281 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable CS4281 soundcard.");
+module_param_array(dual_codec, bool, NULL, 0444);
+MODULE_PARM_DESC(dual_codec, "Secondary Codec ID (0 = disabled).");
+
+/*
+ *
+ */
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4281
+#define PCI_DEVICE_ID_CIRRUS_4281 0x6005
+#endif
+
+/*
+ * Direct registers
+ */
+
+#define CS4281_BA0_SIZE 0x1000
+#define CS4281_BA1_SIZE 0x10000
+
+/*
+ * BA0 registers
+ */
+#define BA0_HISR 0x0000 /* Host Interrupt Status Register */
+#define BA0_HISR_INTENA (1<<31) /* Internal Interrupt Enable Bit */
+#define BA0_HISR_MIDI (1<<22) /* MIDI port interrupt */
+#define BA0_HISR_FIFOI (1<<20) /* FIFO polled interrupt */
+#define BA0_HISR_DMAI (1<<18) /* DMA interrupt (half or end) */
+#define BA0_HISR_FIFO(c) (1<<(12+(c))) /* FIFO channel interrupt */
+#define BA0_HISR_DMA(c) (1<<(8+(c))) /* DMA channel interrupt */
+#define BA0_HISR_GPPI (1<<5) /* General Purpose Input (Primary chip) */
+#define BA0_HISR_GPSI (1<<4) /* General Purpose Input (Secondary chip) */
+#define BA0_HISR_GP3I (1<<3) /* GPIO3 pin Interrupt */
+#define BA0_HISR_GP1I (1<<2) /* GPIO1 pin Interrupt */
+#define BA0_HISR_VUPI (1<<1) /* VOLUP pin Interrupt */
+#define BA0_HISR_VDNI (1<<0) /* VOLDN pin Interrupt */
+
+#define BA0_HICR 0x0008 /* Host Interrupt Control Register */
+#define BA0_HICR_CHGM (1<<1) /* INTENA Change Mask */
+#define BA0_HICR_IEV (1<<0) /* INTENA Value */
+#define BA0_HICR_EOI (3<<0) /* End of Interrupt command */
+
+#define BA0_HIMR 0x000c /* Host Interrupt Mask Register */
+ /* Use same contants as for BA0_HISR */
+
+#define BA0_IIER 0x0010 /* ISA Interrupt Enable Register */
+
+#define BA0_HDSR0 0x00f0 /* Host DMA Engine 0 Status Register */
+#define BA0_HDSR1 0x00f4 /* Host DMA Engine 1 Status Register */
+#define BA0_HDSR2 0x00f8 /* Host DMA Engine 2 Status Register */
+#define BA0_HDSR3 0x00fc /* Host DMA Engine 3 Status Register */
+
+#define BA0_HDSR_CH1P (1<<25) /* Channel 1 Pending */
+#define BA0_HDSR_CH2P (1<<24) /* Channel 2 Pending */
+#define BA0_HDSR_DHTC (1<<17) /* DMA Half Terminal Count */
+#define BA0_HDSR_DTC (1<<16) /* DMA Terminal Count */
+#define BA0_HDSR_DRUN (1<<15) /* DMA Running */
+#define BA0_HDSR_RQ (1<<7) /* Pending Request */
+
+#define BA0_DCA0 0x0110 /* Host DMA Engine 0 Current Address */
+#define BA0_DCC0 0x0114 /* Host DMA Engine 0 Current Count */
+#define BA0_DBA0 0x0118 /* Host DMA Engine 0 Base Address */
+#define BA0_DBC0 0x011c /* Host DMA Engine 0 Base Count */
+#define BA0_DCA1 0x0120 /* Host DMA Engine 1 Current Address */
+#define BA0_DCC1 0x0124 /* Host DMA Engine 1 Current Count */
+#define BA0_DBA1 0x0128 /* Host DMA Engine 1 Base Address */
+#define BA0_DBC1 0x012c /* Host DMA Engine 1 Base Count */
+#define BA0_DCA2 0x0130 /* Host DMA Engine 2 Current Address */
+#define BA0_DCC2 0x0134 /* Host DMA Engine 2 Current Count */
+#define BA0_DBA2 0x0138 /* Host DMA Engine 2 Base Address */
+#define BA0_DBC2 0x013c /* Host DMA Engine 2 Base Count */
+#define BA0_DCA3 0x0140 /* Host DMA Engine 3 Current Address */
+#define BA0_DCC3 0x0144 /* Host DMA Engine 3 Current Count */
+#define BA0_DBA3 0x0148 /* Host DMA Engine 3 Base Address */
+#define BA0_DBC3 0x014c /* Host DMA Engine 3 Base Count */
+#define BA0_DMR0 0x0150 /* Host DMA Engine 0 Mode */
+#define BA0_DCR0 0x0154 /* Host DMA Engine 0 Command */
+#define BA0_DMR1 0x0158 /* Host DMA Engine 1 Mode */
+#define BA0_DCR1 0x015c /* Host DMA Engine 1 Command */
+#define BA0_DMR2 0x0160 /* Host DMA Engine 2 Mode */
+#define BA0_DCR2 0x0164 /* Host DMA Engine 2 Command */
+#define BA0_DMR3 0x0168 /* Host DMA Engine 3 Mode */
+#define BA0_DCR3 0x016c /* Host DMA Engine 3 Command */
+
+#define BA0_DMR_DMA (1<<29) /* Enable DMA mode */
+#define BA0_DMR_POLL (1<<28) /* Enable poll mode */
+#define BA0_DMR_TBC (1<<25) /* Transfer By Channel */
+#define BA0_DMR_CBC (1<<24) /* Count By Channel (0 = frame resolution) */
+#define BA0_DMR_SWAPC (1<<22) /* Swap Left/Right Channels */
+#define BA0_DMR_SIZE20 (1<<20) /* Sample is 20-bit */
+#define BA0_DMR_USIGN (1<<19) /* Unsigned */
+#define BA0_DMR_BEND (1<<18) /* Big Endian */
+#define BA0_DMR_MONO (1<<17) /* Mono */
+#define BA0_DMR_SIZE8 (1<<16) /* Sample is 8-bit */
+#define BA0_DMR_TYPE_DEMAND (0<<6)
+#define BA0_DMR_TYPE_SINGLE (1<<6)
+#define BA0_DMR_TYPE_BLOCK (2<<6)
+#define BA0_DMR_TYPE_CASCADE (3<<6) /* Not supported */
+#define BA0_DMR_DEC (1<<5) /* Access Increment (0) or Decrement (1) */
+#define BA0_DMR_AUTO (1<<4) /* Auto-Initialize */
+#define BA0_DMR_TR_VERIFY (0<<2) /* Verify Transfer */
+#define BA0_DMR_TR_WRITE (1<<2) /* Write Transfer */
+#define BA0_DMR_TR_READ (2<<2) /* Read Transfer */
+
+#define BA0_DCR_HTCIE (1<<17) /* Half Terminal Count Interrupt */
+#define BA0_DCR_TCIE (1<<16) /* Terminal Count Interrupt */
+#define BA0_DCR_MSK (1<<0) /* DMA Mask bit */
+
+#define BA0_FCR0 0x0180 /* FIFO Control 0 */
+#define BA0_FCR1 0x0184 /* FIFO Control 1 */
+#define BA0_FCR2 0x0188 /* FIFO Control 2 */
+#define BA0_FCR3 0x018c /* FIFO Control 3 */
+
+#define BA0_FCR_FEN (1<<31) /* FIFO Enable bit */
+#define BA0_FCR_DACZ (1<<30) /* DAC Zero */
+#define BA0_FCR_PSH (1<<29) /* Previous Sample Hold */
+#define BA0_FCR_RS(x) (((x)&0x1f)<<24) /* Right Slot Mapping */
+#define BA0_FCR_LS(x) (((x)&0x1f)<<16) /* Left Slot Mapping */
+#define BA0_FCR_SZ(x) (((x)&0x7f)<<8) /* FIFO buffer size (in samples) */
+#define BA0_FCR_OF(x) (((x)&0x7f)<<0) /* FIFO starting offset (in samples) */
+
+#define BA0_FPDR0 0x0190 /* FIFO Polled Data 0 */
+#define BA0_FPDR1 0x0194 /* FIFO Polled Data 1 */
+#define BA0_FPDR2 0x0198 /* FIFO Polled Data 2 */
+#define BA0_FPDR3 0x019c /* FIFO Polled Data 3 */
+
+#define BA0_FCHS 0x020c /* FIFO Channel Status */
+#define BA0_FCHS_RCO(x) (1<<(7+(((x)&3)<<3))) /* Right Channel Out */
+#define BA0_FCHS_LCO(x) (1<<(6+(((x)&3)<<3))) /* Left Channel Out */
+#define BA0_FCHS_MRP(x) (1<<(5+(((x)&3)<<3))) /* Move Read Pointer */
+#define BA0_FCHS_FE(x) (1<<(4+(((x)&3)<<3))) /* FIFO Empty */
+#define BA0_FCHS_FF(x) (1<<(3+(((x)&3)<<3))) /* FIFO Full */
+#define BA0_FCHS_IOR(x) (1<<(2+(((x)&3)<<3))) /* Internal Overrun Flag */
+#define BA0_FCHS_RCI(x) (1<<(1+(((x)&3)<<3))) /* Right Channel In */
+#define BA0_FCHS_LCI(x) (1<<(0+(((x)&3)<<3))) /* Left Channel In */
+
+#define BA0_FSIC0 0x0210 /* FIFO Status and Interrupt Control 0 */
+#define BA0_FSIC1 0x0214 /* FIFO Status and Interrupt Control 1 */
+#define BA0_FSIC2 0x0218 /* FIFO Status and Interrupt Control 2 */
+#define BA0_FSIC3 0x021c /* FIFO Status and Interrupt Control 3 */
+
+#define BA0_FSIC_FIC(x) (((x)&0x7f)<<24) /* FIFO Interrupt Count */
+#define BA0_FSIC_FORIE (1<<23) /* FIFO OverRun Interrupt Enable */
+#define BA0_FSIC_FURIE (1<<22) /* FIFO UnderRun Interrupt Enable */
+#define BA0_FSIC_FSCIE (1<<16) /* FIFO Sample Count Interrupt Enable */
+#define BA0_FSIC_FSC(x) (((x)&0x7f)<<8) /* FIFO Sample Count */
+#define BA0_FSIC_FOR (1<<7) /* FIFO OverRun */
+#define BA0_FSIC_FUR (1<<6) /* FIFO UnderRun */
+#define BA0_FSIC_FSCR (1<<0) /* FIFO Sample Count Reached */
+
+#define BA0_PMCS 0x0344 /* Power Management Control/Status */
+#define BA0_CWPR 0x03e0 /* Configuration Write Protect */
+#define BA0_EPPMC 0x03e4 /* Extended PCI Power Management Control */
+#define BA0_GPIOR 0x03e8 /* GPIO Pin Interface Register */
+
+#define BA0_SPMC 0x03ec /* Serial Port Power Management Control (& ASDIN2 enable) */
+#define BA0_SPMC_GIPPEN (1<<15) /* GP INT Primary PME# Enable */
+#define BA0_SPMC_GISPEN (1<<14) /* GP INT Secondary PME# Enable */
+#define BA0_SPMC_EESPD (1<<9) /* EEPROM Serial Port Disable */
+#define BA0_SPMC_ASDI2E (1<<8) /* ASDIN2 Enable */
+#define BA0_SPMC_ASDO (1<<7) /* Asynchronous ASDOUT Assertion */
+#define BA0_SPMC_WUP2 (1<<3) /* Wakeup for Secondary Input */
+#define BA0_SPMC_WUP1 (1<<2) /* Wakeup for Primary Input */
+#define BA0_SPMC_ASYNC (1<<1) /* Asynchronous ASYNC Assertion */
+#define BA0_SPMC_RSTN (1<<0) /* Reset Not! */
+
+#define BA0_CFLR 0x03f0 /* Configuration Load Register (EEPROM or BIOS) */
+#define BA0_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */
+#define BA0_IISR 0x03f4 /* ISA Interrupt Select */
+#define BA0_TMS 0x03f8 /* Test Register */
+#define BA0_SSVID 0x03fc /* Subsystem ID register */
+
+#define BA0_CLKCR1 0x0400 /* Clock Control Register 1 */
+#define BA0_CLKCR1_CLKON (1<<25) /* Read Only */
+#define BA0_CLKCR1_DLLRDY (1<<24) /* DLL Ready */
+#define BA0_CLKCR1_DLLOS (1<<6) /* DLL Output Select */
+#define BA0_CLKCR1_SWCE (1<<5) /* Clock Enable */
+#define BA0_CLKCR1_DLLP (1<<4) /* DLL PowerUp */
+#define BA0_CLKCR1_DLLSS (((x)&3)<<3) /* DLL Source Select */
+
+#define BA0_FRR 0x0410 /* Feature Reporting Register */
+#define BA0_SLT12O 0x041c /* Slot 12 GPIO Output Register for AC-Link */
+
+#define BA0_SERMC 0x0420 /* Serial Port Master Control */
+#define BA0_SERMC_FCRN (1<<27) /* Force Codec Ready Not */
+#define BA0_SERMC_ODSEN2 (1<<25) /* On-Demand Support Enable ASDIN2 */
+#define BA0_SERMC_ODSEN1 (1<<24) /* On-Demand Support Enable ASDIN1 */
+#define BA0_SERMC_SXLB (1<<21) /* ASDIN2 to ASDOUT Loopback */
+#define BA0_SERMC_SLB (1<<20) /* ASDOUT to ASDIN2 Loopback */
+#define BA0_SERMC_LOVF (1<<19) /* Loopback Output Valid Frame bit */
+#define BA0_SERMC_TCID(x) (((x)&3)<<16) /* Target Secondary Codec ID */
+#define BA0_SERMC_PXLB (5<<1) /* Primary Port External Loopback */
+#define BA0_SERMC_PLB (4<<1) /* Primary Port Internal Loopback */
+#define BA0_SERMC_PTC (7<<1) /* Port Timing Configuration */
+#define BA0_SERMC_PTC_AC97 (1<<1) /* AC97 mode */
+#define BA0_SERMC_MSPE (1<<0) /* Master Serial Port Enable */
+
+#define BA0_SERC1 0x0428 /* Serial Port Configuration 1 */
+#define BA0_SERC1_SO1F(x) (((x)&7)>>1) /* Primary Output Port Format */
+#define BA0_SERC1_AC97 (1<<1)
+#define BA0_SERC1_SO1EN (1<<0) /* Primary Output Port Enable */
+
+#define BA0_SERC2 0x042c /* Serial Port Configuration 2 */
+#define BA0_SERC2_SI1F(x) (((x)&7)>>1) /* Primary Input Port Format */
+#define BA0_SERC2_AC97 (1<<1)
+#define BA0_SERC2_SI1EN (1<<0) /* Primary Input Port Enable */
+
+#define BA0_SLT12M 0x045c /* Slot 12 Monitor Register for Primary AC-Link */
+
+#define BA0_ACCTL 0x0460 /* AC'97 Control */
+#define BA0_ACCTL_TC (1<<6) /* Target Codec */
+#define BA0_ACCTL_CRW (1<<4) /* 0=Write, 1=Read Command */
+#define BA0_ACCTL_DCV (1<<3) /* Dynamic Command Valid */
+#define BA0_ACCTL_VFRM (1<<2) /* Valid Frame */
+#define BA0_ACCTL_ESYN (1<<1) /* Enable Sync */
+
+#define BA0_ACSTS 0x0464 /* AC'97 Status */
+#define BA0_ACSTS_VSTS (1<<1) /* Valid Status */
+#define BA0_ACSTS_CRDY (1<<0) /* Codec Ready */
+
+#define BA0_ACOSV 0x0468 /* AC'97 Output Slot Valid */
+#define BA0_ACOSV_SLV(x) (1<<((x)-3))
+
+#define BA0_ACCAD 0x046c /* AC'97 Command Address */
+#define BA0_ACCDA 0x0470 /* AC'97 Command Data */
+
+#define BA0_ACISV 0x0474 /* AC'97 Input Slot Valid */
+#define BA0_ACISV_SLV(x) (1<<((x)-3))
+
+#define BA0_ACSAD 0x0478 /* AC'97 Status Address */
+#define BA0_ACSDA 0x047c /* AC'97 Status Data */
+#define BA0_JSPT 0x0480 /* Joystick poll/trigger */
+#define BA0_JSCTL 0x0484 /* Joystick control */
+#define BA0_JSC1 0x0488 /* Joystick control */
+#define BA0_JSC2 0x048c /* Joystick control */
+#define BA0_JSIO 0x04a0
+
+#define BA0_MIDCR 0x0490 /* MIDI Control */
+#define BA0_MIDCR_MRST (1<<5) /* Reset MIDI Interface */
+#define BA0_MIDCR_MLB (1<<4) /* MIDI Loop Back Enable */
+#define BA0_MIDCR_TIE (1<<3) /* MIDI Transmuit Interrupt Enable */
+#define BA0_MIDCR_RIE (1<<2) /* MIDI Receive Interrupt Enable */
+#define BA0_MIDCR_RXE (1<<1) /* MIDI Receive Enable */
+#define BA0_MIDCR_TXE (1<<0) /* MIDI Transmit Enable */
+
+#define BA0_MIDCMD 0x0494 /* MIDI Command (wo) */
+
+#define BA0_MIDSR 0x0494 /* MIDI Status (ro) */
+#define BA0_MIDSR_RDA (1<<15) /* Sticky bit (RBE 1->0) */
+#define BA0_MIDSR_TBE (1<<14) /* Sticky bit (TBF 0->1) */
+#define BA0_MIDSR_RBE (1<<7) /* Receive Buffer Empty */
+#define BA0_MIDSR_TBF (1<<6) /* Transmit Buffer Full */
+
+#define BA0_MIDWP 0x0498 /* MIDI Write */
+#define BA0_MIDRP 0x049c /* MIDI Read (ro) */
+
+#define BA0_AODSD1 0x04a8 /* AC'97 On-Demand Slot Disable for primary link (ro) */
+#define BA0_AODSD1_NDS(x) (1<<((x)-3))
+
+#define BA0_AODSD2 0x04ac /* AC'97 On-Demand Slot Disable for secondary link (ro) */
+#define BA0_AODSD2_NDS(x) (1<<((x)-3))
+
+#define BA0_CFGI 0x04b0 /* Configure Interface (EEPROM interface) */
+#define BA0_SLT12M2 0x04dc /* Slot 12 Monitor Register 2 for secondary AC-link */
+#define BA0_ACSTS2 0x04e4 /* AC'97 Status Register 2 */
+#define BA0_ACISV2 0x04f4 /* AC'97 Input Slot Valid Register 2 */
+#define BA0_ACSAD2 0x04f8 /* AC'97 Status Address Register 2 */
+#define BA0_ACSDA2 0x04fc /* AC'97 Status Data Register 2 */
+#define BA0_FMSR 0x0730 /* FM Synthesis Status (ro) */
+#define BA0_B0AP 0x0730 /* FM Bank 0 Address Port (wo) */
+#define BA0_FMDP 0x0734 /* FM Data Port */
+#define BA0_B1AP 0x0738 /* FM Bank 1 Address Port */
+#define BA0_B1DP 0x073c /* FM Bank 1 Data Port */
+
+#define BA0_SSPM 0x0740 /* Sound System Power Management */
+#define BA0_SSPM_MIXEN (1<<6) /* Playback SRC + FM/Wavetable MIX */
+#define BA0_SSPM_CSRCEN (1<<5) /* Capture Sample Rate Converter Enable */
+#define BA0_SSPM_PSRCEN (1<<4) /* Playback Sample Rate Converter Enable */
+#define BA0_SSPM_JSEN (1<<3) /* Joystick Enable */
+#define BA0_SSPM_ACLEN (1<<2) /* Serial Port Engine and AC-Link Enable */
+#define BA0_SSPM_FMEN (1<<1) /* FM Synthesis Block Enable */
+
+#define BA0_DACSR 0x0744 /* DAC Sample Rate - Playback SRC */
+#define BA0_ADCSR 0x0748 /* ADC Sample Rate - Capture SRC */
+
+#define BA0_SSCR 0x074c /* Sound System Control Register */
+#define BA0_SSCR_HVS1 (1<<23) /* Hardwave Volume Step (0=1,1=2) */
+#define BA0_SSCR_MVCS (1<<19) /* Master Volume Codec Select */
+#define BA0_SSCR_MVLD (1<<18) /* Master Volume Line Out Disable */
+#define BA0_SSCR_MVAD (1<<17) /* Master Volume Alternate Out Disable */
+#define BA0_SSCR_MVMD (1<<16) /* Master Volume Mono Out Disable */
+#define BA0_SSCR_XLPSRC (1<<8) /* External SRC Loopback Mode */
+#define BA0_SSCR_LPSRC (1<<7) /* SRC Loopback Mode */
+#define BA0_SSCR_CDTX (1<<5) /* CD Transfer Data */
+#define BA0_SSCR_HVC (1<<3) /* Harware Volume Control Enable */
+
+#define BA0_FMLVC 0x0754 /* FM Synthesis Left Volume Control */
+#define BA0_FMRVC 0x0758 /* FM Synthesis Right Volume Control */
+#define BA0_SRCSA 0x075c /* SRC Slot Assignments */
+#define BA0_PPLVC 0x0760 /* PCM Playback Left Volume Control */
+#define BA0_PPRVC 0x0764 /* PCM Playback Right Volume Control */
+#define BA0_PASR 0x0768 /* playback sample rate */
+#define BA0_CASR 0x076C /* capture sample rate */
+
+/* Source Slot Numbers - Playback */
+#define SRCSLOT_LEFT_PCM_PLAYBACK 0
+#define SRCSLOT_RIGHT_PCM_PLAYBACK 1
+#define SRCSLOT_PHONE_LINE_1_DAC 2
+#define SRCSLOT_CENTER_PCM_PLAYBACK 3
+#define SRCSLOT_LEFT_SURROUND_PCM_PLAYBACK 4
+#define SRCSLOT_RIGHT_SURROUND_PCM_PLAYBACK 5
+#define SRCSLOT_LFE_PCM_PLAYBACK 6
+#define SRCSLOT_PHONE_LINE_2_DAC 7
+#define SRCSLOT_HEADSET_DAC 8
+#define SRCSLOT_LEFT_WT 29 /* invalid for BA0_SRCSA */
+#define SRCSLOT_RIGHT_WT 30 /* invalid for BA0_SRCSA */
+
+/* Source Slot Numbers - Capture */
+#define SRCSLOT_LEFT_PCM_RECORD 10
+#define SRCSLOT_RIGHT_PCM_RECORD 11
+#define SRCSLOT_PHONE_LINE_1_ADC 12
+#define SRCSLOT_MIC_ADC 13
+#define SRCSLOT_PHONE_LINE_2_ADC 17
+#define SRCSLOT_HEADSET_ADC 18
+#define SRCSLOT_SECONDARY_LEFT_PCM_RECORD 20
+#define SRCSLOT_SECONDARY_RIGHT_PCM_RECORD 21
+#define SRCSLOT_SECONDARY_PHONE_LINE_1_ADC 22
+#define SRCSLOT_SECONDARY_MIC_ADC 23
+#define SRCSLOT_SECONDARY_PHONE_LINE_2_ADC 27
+#define SRCSLOT_SECONDARY_HEADSET_ADC 28
+
+/* Source Slot Numbers - Others */
+#define SRCSLOT_POWER_DOWN 31
+
+/* MIDI modes */
+#define CS4281_MODE_OUTPUT (1<<0)
+#define CS4281_MODE_INPUT (1<<1)
+
+/* joystick bits */
+/* Bits for JSPT */
+#define JSPT_CAX 0x00000001
+#define JSPT_CAY 0x00000002
+#define JSPT_CBX 0x00000004
+#define JSPT_CBY 0x00000008
+#define JSPT_BA1 0x00000010
+#define JSPT_BA2 0x00000020
+#define JSPT_BB1 0x00000040
+#define JSPT_BB2 0x00000080
+
+/* Bits for JSCTL */
+#define JSCTL_SP_MASK 0x00000003
+#define JSCTL_SP_SLOW 0x00000000
+#define JSCTL_SP_MEDIUM_SLOW 0x00000001
+#define JSCTL_SP_MEDIUM_FAST 0x00000002
+#define JSCTL_SP_FAST 0x00000003
+#define JSCTL_ARE 0x00000004
+
+/* Data register pairs masks */
+#define JSC1_Y1V_MASK 0x0000FFFF
+#define JSC1_X1V_MASK 0xFFFF0000
+#define JSC1_Y1V_SHIFT 0
+#define JSC1_X1V_SHIFT 16
+#define JSC2_Y2V_MASK 0x0000FFFF
+#define JSC2_X2V_MASK 0xFFFF0000
+#define JSC2_Y2V_SHIFT 0
+#define JSC2_X2V_SHIFT 16
+
+/* JS GPIO */
+#define JSIO_DAX 0x00000001
+#define JSIO_DAY 0x00000002
+#define JSIO_DBX 0x00000004
+#define JSIO_DBY 0x00000008
+#define JSIO_AXOE 0x00000010
+#define JSIO_AYOE 0x00000020
+#define JSIO_BXOE 0x00000040
+#define JSIO_BYOE 0x00000080
+
+/*
+ *
+ */
+
+typedef struct snd_cs4281 cs4281_t;
+typedef struct snd_cs4281_dma cs4281_dma_t;
+
+struct snd_cs4281_dma {
+ snd_pcm_substream_t *substream;
+ unsigned int regDBA; /* offset to DBA register */
+ unsigned int regDCA; /* offset to DCA register */
+ unsigned int regDBC; /* offset to DBC register */
+ unsigned int regDCC; /* offset to DCC register */
+ unsigned int regDMR; /* offset to DMR register */
+ unsigned int regDCR; /* offset to DCR register */
+ unsigned int regHDSR; /* offset to HDSR register */
+ unsigned int regFCR; /* offset to FCR register */
+ unsigned int regFSIC; /* offset to FSIC register */
+ unsigned int valDMR; /* DMA mode */
+ unsigned int valDCR; /* DMA command */
+ unsigned int valFCR; /* FIFO control */
+ unsigned int fifo_offset; /* FIFO offset within BA1 */
+ unsigned char left_slot; /* FIFO left slot */
+ unsigned char right_slot; /* FIFO right slot */
+ int frag; /* period number */
+};
+
+#define SUSPEND_REGISTERS 20
+
+struct snd_cs4281 {
+ int irq;
+
+ void __iomem *ba0; /* virtual (accessible) address */
+ void __iomem *ba1; /* virtual (accessible) address */
+ unsigned long ba0_addr;
+ unsigned long ba1_addr;
+
+ int dual_codec;
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97;
+ ac97_t *ac97_secondary;
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ snd_rawmidi_t *rmidi;
+ snd_rawmidi_substream_t *midi_input;
+ snd_rawmidi_substream_t *midi_output;
+
+ cs4281_dma_t dma[4];
+
+ unsigned char src_left_play_slot;
+ unsigned char src_right_play_slot;
+ unsigned char src_left_rec_slot;
+ unsigned char src_right_rec_slot;
+
+ unsigned int spurious_dhtc_irq;
+ unsigned int spurious_dtc_irq;
+
+ spinlock_t reg_lock;
+ unsigned int midcr;
+ unsigned int uartm;
+
+ struct gameport *gameport;
+
+#ifdef CONFIG_PM
+ u32 suspend_regs[SUSPEND_REGISTERS];
+#endif
+
+};
+
+static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_cs4281_ids[] = {
+ { 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4281 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs4281_ids);
+
+/*
+ * constants
+ */
+
+#define CS4281_FIFO_SIZE 32
+
+/*
+ * common I/O routines
+ */
+
+static void snd_cs4281_delay(unsigned int delay)
+{
+ if (delay > 999) {
+ unsigned long end_time;
+ delay = (delay * HZ) / 1000000;
+ if (delay < 1)
+ delay = 1;
+ end_time = jiffies + delay;
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after_eq(end_time, jiffies));
+ } else {
+ udelay(delay);
+ }
+}
+
+inline static void snd_cs4281_delay_long(void)
+{
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+}
+
+static inline void snd_cs4281_pokeBA0(cs4281_t *chip, unsigned long offset, unsigned int val)
+{
+ writel(val, chip->ba0 + offset);
+}
+
+static inline unsigned int snd_cs4281_peekBA0(cs4281_t *chip, unsigned long offset)
+{
+ return readl(chip->ba0 + offset);
+}
+
+static void snd_cs4281_ac97_write(ac97_t *ac97,
+ unsigned short reg, unsigned short val)
+{
+ /*
+ * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+ * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97
+ * 3. Write ACCTL = Control Register = 460h for initiating the write
+ * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+ * 5. if DCV not cleared, break and return error
+ */
+ cs4281_t *chip = ac97->private_data;
+ int count;
+
+ /*
+ * Setup the AC97 control registers on the CS461x to send the
+ * appropriate command to the AC97 to perform the read.
+ * ACCAD = Command Address Register = 46Ch
+ * ACCDA = Command Data Register = 470h
+ * ACCTL = Control Register = 460h
+ * set DCV - will clear when process completed
+ * reset CRW - Write command
+ * set VFRM - valid frame enabled
+ * set ESYN - ASYNC generation enabled
+ * set RSTN - ARST# inactive, AC97 codec not reset
+ */
+ snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg);
+ snd_cs4281_pokeBA0(chip, BA0_ACCDA, val);
+ snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM |
+ BA0_ACCTL_ESYN | (ac97->num ? BA0_ACCTL_TC : 0));
+ for (count = 0; count < 2000; count++) {
+ /*
+ * First, we want to wait for a short time.
+ */
+ udelay(10);
+ /*
+ * Now, check to see if the write has completed.
+ * ACCTL = 460h, DCV should be reset by now and 460h = 07h
+ */
+ if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) {
+ return;
+ }
+ }
+ snd_printk(KERN_ERR "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
+}
+
+static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ cs4281_t *chip = ac97->private_data;
+ int count;
+ unsigned short result;
+ // FIXME: volatile is necessary in the following due to a bug of
+ // some gcc versions
+ volatile int ac97_num = ((volatile ac97_t *)ac97)->num;
+
+ /*
+ * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+ * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97
+ * 3. Write ACCTL = Control Register = 460h for initiating the write
+ * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+ * 5. if DCV not cleared, break and return error
+ * 6. Read ACSTS = Status Register = 464h, check VSTS bit
+ */
+
+ snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
+
+ /*
+ * Setup the AC97 control registers on the CS461x to send the
+ * appropriate command to the AC97 to perform the read.
+ * ACCAD = Command Address Register = 46Ch
+ * ACCDA = Command Data Register = 470h
+ * ACCTL = Control Register = 460h
+ * set DCV - will clear when process completed
+ * set CRW - Read command
+ * set VFRM - valid frame enabled
+ * set ESYN - ASYNC generation enabled
+ * set RSTN - ARST# inactive, AC97 codec not reset
+ */
+
+ snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg);
+ snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0);
+ snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW |
+ BA0_ACCTL_VFRM | BA0_ACCTL_ESYN |
+ (ac97_num ? BA0_ACCTL_TC : 0));
+
+
+ /*
+ * Wait for the read to occur.
+ */
+ for (count = 0; count < 500; count++) {
+ /*
+ * First, we want to wait for a short time.
+ */
+ udelay(10);
+ /*
+ * Now, check to see if the read has completed.
+ * ACCTL = 460h, DCV should be reset by now and 460h = 17h
+ */
+ if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV))
+ goto __ok1;
+ }
+
+ snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+ result = 0xffff;
+ goto __end;
+
+ __ok1:
+ /*
+ * Wait for the valid status bit to go active.
+ */
+ for (count = 0; count < 100; count++) {
+ /*
+ * Read the AC97 status register.
+ * ACSTS = Status Register = 464h
+ * VSTS - Valid Status
+ */
+ if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS)
+ goto __ok2;
+ udelay(10);
+ }
+
+ snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
+ result = 0xffff;
+ goto __end;
+
+ __ok2:
+ /*
+ * Read the data returned from the AC97 register.
+ * ACSDA = Status Data Register = 474h
+ */
+ result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
+
+ __end:
+ return result;
+}
+
+/*
+ * PCM part
+ */
+
+static int snd_cs4281_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+ cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dma->valDCR |= BA0_DCR_MSK;
+ dma->valFCR |= BA0_FCR_FEN;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dma->valDCR &= ~BA0_DCR_MSK;
+ dma->valFCR &= ~BA0_FCR_FEN;
+ break;
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA);
+ dma->valDMR |= BA0_DMR_DMA;
+ dma->valDCR &= ~BA0_DCR_MSK;
+ dma->valFCR |= BA0_FCR_FEN;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL);
+ dma->valDCR |= BA0_DCR_MSK;
+ dma->valFCR &= ~BA0_FCR_FEN;
+ /* Leave wave playback FIFO enabled for FM */
+ if (dma->regFCR != BA0_FCR0)
+ dma->valFCR &= ~BA0_FCR_FEN;
+ break;
+ default:
+ spin_unlock(&chip->reg_lock);
+ return -EINVAL;
+ }
+ snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR);
+ snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR);
+ snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR);
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
+{
+ unsigned int val = ~0;
+
+ if (real_rate)
+ *real_rate = rate;
+ /* special "hardcoded" rates */
+ switch (rate) {
+ case 8000: return 5;
+ case 11025: return 4;
+ case 16000: return 3;
+ case 22050: return 2;
+ case 44100: return 1;
+ case 48000: return 0;
+ default:
+ goto __variable;
+ }
+ __variable:
+ val = 1536000 / rate;
+ if (real_rate)
+ *real_rate = 1536000 / val;
+ return val;
+}
+
+static void snd_cs4281_mode(cs4281_t *chip, cs4281_dma_t *dma, snd_pcm_runtime_t *runtime, int capture, int src)
+{
+ int rec_mono;
+
+ dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO |
+ (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ);
+ if (runtime->channels == 1)
+ dma->valDMR |= BA0_DMR_MONO;
+ if (snd_pcm_format_unsigned(runtime->format) > 0)
+ dma->valDMR |= BA0_DMR_USIGN;
+ if (snd_pcm_format_big_endian(runtime->format) > 0)
+ dma->valDMR |= BA0_DMR_BEND;
+ switch (snd_pcm_format_width(runtime->format)) {
+ case 8: dma->valDMR |= BA0_DMR_SIZE8;
+ if (runtime->channels == 1)
+ dma->valDMR |= BA0_DMR_SWAPC;
+ break;
+ case 32: dma->valDMR |= BA0_DMR_SIZE20; break;
+ }
+ dma->frag = 0; /* for workaround */
+ dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK;
+ if (runtime->buffer_size != runtime->period_size)
+ dma->valDCR |= BA0_DCR_HTCIE;
+ /* Initialize DMA */
+ snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr);
+ snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1);
+ rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO;
+ snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) |
+ (chip->src_right_play_slot << 8) |
+ (chip->src_left_rec_slot << 16) |
+ ((rec_mono ? 31 : chip->src_right_rec_slot) << 24));
+ if (!src)
+ goto __skip_src;
+ if (!capture) {
+ if (dma->left_slot == chip->src_left_play_slot) {
+ unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
+ snd_assert(dma->right_slot == chip->src_right_play_slot, );
+ snd_cs4281_pokeBA0(chip, BA0_DACSR, val);
+ }
+ } else {
+ if (dma->left_slot == chip->src_left_rec_slot) {
+ unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
+ snd_assert(dma->right_slot == chip->src_right_rec_slot, );
+ snd_cs4281_pokeBA0(chip, BA0_ADCSR, val);
+ }
+ }
+ __skip_src:
+ /* Deactivate wave playback FIFO before changing slot assignments */
+ if (dma->regFCR == BA0_FCR0)
+ snd_cs4281_pokeBA0(chip, dma->regFCR, snd_cs4281_peekBA0(chip, dma->regFCR) & ~BA0_FCR_FEN);
+ /* Initialize FIFO */
+ dma->valFCR = BA0_FCR_LS(dma->left_slot) |
+ BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) |
+ BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+ BA0_FCR_OF(dma->fifo_offset);
+ snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0));
+ /* Activate FIFO again for FM playback */
+ if (dma->regFCR == BA0_FCR0)
+ snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | BA0_FCR_FEN);
+ /* Clear FIFO Status and Interrupt Control Register */
+ snd_cs4281_pokeBA0(chip, dma->regFSIC, 0);
+}
+
+static int snd_cs4281_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_cs4281_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4281_playback_prepare(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+ cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ snd_cs4281_mode(chip, dma, runtime, 0, 1);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cs4281_capture_prepare(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+ cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ snd_cs4281_mode(chip, dma, runtime, 1, 1);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_cs4281_pointer(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+ cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+ // printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies);
+ return runtime->buffer_size -
+ snd_cs4281_peekBA0(chip, dma->regDCC) - 1;
+}
+
+static snd_pcm_hardware_t snd_cs4281_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (512*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (512*1024),
+ .periods_min = 1,
+ .periods_max = 2,
+ .fifo_size = CS4281_FIFO_SIZE,
+};
+
+static snd_pcm_hardware_t snd_cs4281_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (512*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (512*1024),
+ .periods_min = 1,
+ .periods_max = 2,
+ .fifo_size = CS4281_FIFO_SIZE,
+};
+
+static int snd_cs4281_playback_open(snd_pcm_substream_t * substream)
+{
+ cs4281_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs4281_dma_t *dma;
+
+ dma = &chip->dma[0];
+ dma->substream = substream;
+ dma->left_slot = 0;
+ dma->right_slot = 1;
+ runtime->private_data = dma;
+ runtime->hw = snd_cs4281_playback;
+ snd_pcm_set_sync(substream);
+ /* should be detected from the AC'97 layer, but it seems
+ that although CS4297A rev B reports 18-bit ADC resolution,
+ samples are 20-bit */
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
+ return 0;
+}
+
+static int snd_cs4281_capture_open(snd_pcm_substream_t * substream)
+{
+ cs4281_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs4281_dma_t *dma;
+
+ dma = &chip->dma[1];
+ dma->substream = substream;
+ dma->left_slot = 10;
+ dma->right_slot = 11;
+ runtime->private_data = dma;
+ runtime->hw = snd_cs4281_capture;
+ snd_pcm_set_sync(substream);
+ /* should be detected from the AC'97 layer, but it seems
+ that although CS4297A rev B reports 18-bit ADC resolution,
+ samples are 20-bit */
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
+ return 0;
+}
+
+static int snd_cs4281_playback_close(snd_pcm_substream_t * substream)
+{
+ cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+
+ dma->substream = NULL;
+ return 0;
+}
+
+static int snd_cs4281_capture_close(snd_pcm_substream_t * substream)
+{
+ cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+
+ dma->substream = NULL;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_cs4281_playback_ops = {
+ .open = snd_cs4281_playback_open,
+ .close = snd_cs4281_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs4281_hw_params,
+ .hw_free = snd_cs4281_hw_free,
+ .prepare = snd_cs4281_playback_prepare,
+ .trigger = snd_cs4281_trigger,
+ .pointer = snd_cs4281_pointer,
+};
+
+static snd_pcm_ops_t snd_cs4281_capture_ops = {
+ .open = snd_cs4281_capture_open,
+ .close = snd_cs4281_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs4281_hw_params,
+ .hw_free = snd_cs4281_hw_free,
+ .prepare = snd_cs4281_capture_prepare,
+ .trigger = snd_cs4281_trigger,
+ .pointer = snd_cs4281_pointer,
+};
+
+static void snd_cs4281_pcm_free(snd_pcm_t *pcm)
+{
+ cs4281_t *chip = pcm->private_data;
+ chip->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_cs4281_pcm(cs4281_t * chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4281_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4281_capture_ops);
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_cs4281_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "CS4281");
+ chip->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 512*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+/*
+ * Mixer section
+ */
+
+#define CS_VOL_MASK 0x1f
+
+static int snd_cs4281_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = CS_VOL_MASK;
+ return 0;
+}
+
+static int snd_cs4281_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs4281_t *chip = snd_kcontrol_chip(kcontrol);
+ int regL = (kcontrol->private_value >> 16) & 0xffff;
+ int regR = kcontrol->private_value & 0xffff;
+ int volL, volR;
+
+ volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK);
+ volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK);
+
+ ucontrol->value.integer.value[0] = volL;
+ ucontrol->value.integer.value[1] = volR;
+ return 0;
+}
+
+static int snd_cs4281_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs4281_t *chip = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+ int regL = (kcontrol->private_value >> 16) & 0xffff;
+ int regR = kcontrol->private_value & 0xffff;
+ int volL, volR;
+
+ volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK);
+ volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK);
+
+ if (ucontrol->value.integer.value[0] != volL) {
+ volL = CS_VOL_MASK - (ucontrol->value.integer.value[0] & CS_VOL_MASK);
+ snd_cs4281_pokeBA0(chip, regL, volL);
+ change = 1;
+ }
+ if (ucontrol->value.integer.value[0] != volL) {
+ volR = CS_VOL_MASK - (ucontrol->value.integer.value[1] & CS_VOL_MASK);
+ snd_cs4281_pokeBA0(chip, regR, volR);
+ change = 1;
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_cs4281_fm_vol =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Synth Playback Volume",
+ .info = snd_cs4281_info_volume,
+ .get = snd_cs4281_get_volume,
+ .put = snd_cs4281_put_volume,
+ .private_value = ((BA0_FMLVC << 16) | BA0_FMRVC),
+};
+
+static snd_kcontrol_new_t snd_cs4281_pcm_vol =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Stream Playback Volume",
+ .info = snd_cs4281_info_volume,
+ .get = snd_cs4281_get_volume,
+ .put = snd_cs4281_put_volume,
+ .private_value = ((BA0_PPLVC << 16) | BA0_PPRVC),
+};
+
+static void snd_cs4281_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ cs4281_t *chip = bus->private_data;
+ chip->ac97_bus = NULL;
+}
+
+static void snd_cs4281_mixer_free_ac97(ac97_t *ac97)
+{
+ cs4281_t *chip = ac97->private_data;
+ if (ac97->num)
+ chip->ac97_secondary = NULL;
+ else
+ chip->ac97 = NULL;
+}
+
+static int __devinit snd_cs4281_mixer(cs4281_t * chip)
+{
+ snd_card_t *card = chip->card;
+ ac97_template_t ac97;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_cs4281_ac97_write,
+ .read = snd_cs4281_ac97_read,
+ };
+
+ if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ return err;
+ chip->ac97_bus->private_free = snd_cs4281_mixer_free_ac97_bus;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_cs4281_mixer_free_ac97;
+ if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ return err;
+ if (chip->dual_codec) {
+ ac97.num = 1;
+ if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_secondary)) < 0)
+ return err;
+ }
+ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip))) < 0)
+ return err;
+ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip))) < 0)
+ return err;
+ return 0;
+}
+
+
+/*
+ * proc interface
+ */
+
+static void snd_cs4281_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ cs4281_t *chip = entry->private_data;
+
+ snd_iprintf(buffer, "Cirrus Logic CS4281\n\n");
+ snd_iprintf(buffer, "Spurious half IRQs : %u\n", chip->spurious_dhtc_irq);
+ snd_iprintf(buffer, "Spurious end IRQs : %u\n", chip->spurious_dtc_irq);
+}
+
+static long snd_cs4281_BA0_read(snd_info_entry_t *entry, void *file_private_data,
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
+{
+ long size;
+ cs4281_t *chip = entry->private_data;
+
+ size = count;
+ if (pos + size > CS4281_BA0_SIZE)
+ size = (long)CS4281_BA0_SIZE - pos;
+ if (size > 0) {
+ if (copy_to_user_fromio(buf, chip->ba0 + pos, size))
+ return -EFAULT;
+ }
+ return size;
+}
+
+static long snd_cs4281_BA1_read(snd_info_entry_t *entry, void *file_private_data,
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
+{
+ long size;
+ cs4281_t *chip = entry->private_data;
+
+ size = count;
+ if (pos + size > CS4281_BA1_SIZE)
+ size = (long)CS4281_BA1_SIZE - pos;
+ if (size > 0) {
+ if (copy_to_user_fromio(buf, chip->ba1 + pos, size))
+ return -EFAULT;
+ }
+ return size;
+}
+
+static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
+ .read = snd_cs4281_BA0_read,
+};
+
+static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
+ .read = snd_cs4281_BA1_read,
+};
+
+static void __devinit snd_cs4281_proc_init(cs4281_t * chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(chip->card, "cs4281", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_cs4281_proc_read);
+ if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = chip;
+ entry->c.ops = &snd_cs4281_proc_ops_BA0;
+ entry->size = CS4281_BA0_SIZE;
+ }
+ if (! snd_card_proc_new(chip->card, "cs4281_BA1", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = chip;
+ entry->c.ops = &snd_cs4281_proc_ops_BA1;
+ entry->size = CS4281_BA1_SIZE;
+ }
+}
+
+/*
+ * joystick support
+ */
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+static void snd_cs4281_gameport_trigger(struct gameport *gameport)
+{
+ cs4281_t *chip = gameport_get_port_data(gameport);
+
+ snd_assert(chip, return);
+ snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff);
+}
+
+static unsigned char snd_cs4281_gameport_read(struct gameport *gameport)
+{
+ cs4281_t *chip = gameport_get_port_data(gameport);
+
+ snd_assert(chip, return 0);
+ return snd_cs4281_peekBA0(chip, BA0_JSPT);
+}
+
+#ifdef COOKED_MODE
+static int snd_cs4281_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ cs4281_t *chip = gameport_get_port_data(gameport);
+ unsigned js1, js2, jst;
+
+ snd_assert(chip, return 0);
+
+ js1 = snd_cs4281_peekBA0(chip, BA0_JSC1);
+ js2 = snd_cs4281_peekBA0(chip, BA0_JSC2);
+ jst = snd_cs4281_peekBA0(chip, BA0_JSPT);
+
+ *buttons = (~jst >> 4) & 0x0F;
+
+ axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
+ axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
+ axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
+ axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
+
+ for (jst = 0; jst < 4; ++jst)
+ if (axes[jst] == 0xFFFF) axes[jst] = -1;
+ return 0;
+}
+#else
+#define snd_cs4281_gameport_cooked_read NULL
+#endif
+
+static int snd_cs4281_gameport_open(struct gameport *gameport, int mode)
+{
+ switch (mode) {
+#ifdef COOKED_MODE
+ case GAMEPORT_MODE_COOKED:
+ return 0;
+#endif
+ case GAMEPORT_MODE_RAW:
+ return 0;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static int __devinit snd_cs4281_create_gameport(cs4281_t *chip)
+{
+ struct gameport *gp;
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "cs4281: cannot allocate memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "CS4281 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gp->open = snd_cs4281_gameport_open;
+ gp->read = snd_cs4281_gameport_read;
+ gp->trigger = snd_cs4281_gameport_trigger;
+ gp->cooked_read = snd_cs4281_gameport_cooked_read;
+ gameport_set_port_data(gp, chip);
+
+ snd_cs4281_pokeBA0(chip, BA0_JSIO, 0xFF); // ?
+ snd_cs4281_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static void snd_cs4281_free_gameport(cs4281_t *chip)
+{
+ if (chip->gameport) {
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+ }
+}
+#else
+static inline int snd_cs4281_create_gameport(cs4281_t *chip) { return -ENOSYS; }
+static inline void snd_cs4281_free_gameport(cs4281_t *chip) { }
+#endif /* CONFIG_GAMEPORT || (MODULE && CONFIG_GAMEPORT_MODULE) */
+
+
+/*
+
+ */
+
+static int snd_cs4281_free(cs4281_t *chip)
+{
+ snd_cs4281_free_gameport(chip);
+
+ if (chip->irq >= 0)
+ synchronize_irq(chip->irq);
+
+ /* Mask interrupts */
+ snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff);
+ /* Stop the DLL Clock logic. */
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+ /* Sound System Power Management - Turn Everything OFF */
+ snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
+ /* PCI interface - D3 state */
+ pci_set_power_state(chip->pci, 3);
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->ba0)
+ iounmap(chip->ba0);
+ if (chip->ba1)
+ iounmap(chip->ba1);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+
+ kfree(chip);
+ return 0;
+}
+
+static int snd_cs4281_dev_free(snd_device_t *device)
+{
+ cs4281_t *chip = device->device_data;
+ return snd_cs4281_free(chip);
+}
+
+static int snd_cs4281_chip_init(cs4281_t *chip); /* defined below */
+#ifdef CONFIG_PM
+static int cs4281_suspend(snd_card_t *card, pm_message_t state);
+static int cs4281_resume(snd_card_t *card);
+#endif
+
+static int __devinit snd_cs4281_create(snd_card_t * card,
+ struct pci_dev *pci,
+ cs4281_t ** rchip,
+ int dual_codec)
+{
+ cs4281_t *chip;
+ unsigned int tmp;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_cs4281_dev_free,
+ };
+
+ *rchip = NULL;
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&chip->reg_lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ pci_set_master(pci);
+ if (dual_codec < 0 || dual_codec > 3) {
+ snd_printk(KERN_ERR "invalid dual_codec option %d\n", dual_codec);
+ dual_codec = 0;
+ }
+ chip->dual_codec = dual_codec;
+
+ if ((err = pci_request_regions(pci, "CS4281")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->ba0_addr = pci_resource_start(pci, 0);
+ chip->ba1_addr = pci_resource_start(pci, 1);
+
+ if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", (void *)chip)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ snd_cs4281_free(chip);
+ return -ENOMEM;
+ }
+ chip->irq = pci->irq;
+
+ chip->ba0 = ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0));
+ chip->ba1 = ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1));
+ if (!chip->ba0 || !chip->ba1) {
+ snd_cs4281_free(chip);
+ return -ENOMEM;
+ }
+
+ tmp = snd_cs4281_chip_init(chip);
+ if (tmp) {
+ snd_cs4281_free(chip);
+ return tmp;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_cs4281_free(chip);
+ return err;
+ }
+
+ snd_cs4281_proc_init(chip);
+
+ snd_card_set_pm_callback(card, cs4281_suspend, cs4281_resume, chip);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = chip;
+ return 0;
+}
+
+static int snd_cs4281_chip_init(cs4281_t *chip)
+{
+ unsigned int tmp;
+ int timeout;
+ int retry_count = 2;
+
+ __retry:
+ tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
+ if (tmp != BA0_CFLR_DEFAULT) {
+ snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT);
+ tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
+ if (tmp != BA0_CFLR_DEFAULT) {
+ snd_printk(KERN_ERR "CFLR setup failed (0x%x)\n", tmp);
+ return -EIO;
+ }
+ }
+
+ /* Set the 'Configuration Write Protect' register
+ * to 4281h. Allows vendor-defined configuration
+ * space between 0e4h and 0ffh to be written. */
+ snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281);
+
+ if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
+ snd_printk(KERN_ERR "SERC1 AC'97 check failed (0x%x)\n", tmp);
+ return -EIO;
+ }
+ if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
+ snd_printk(KERN_ERR "SERC2 AC'97 check failed (0x%x)\n", tmp);
+ return -EIO;
+ }
+
+ /* Sound System Power Management */
+ snd_cs4281_pokeBA0(chip, BA0_SSPM, BA0_SSPM_MIXEN | BA0_SSPM_CSRCEN |
+ BA0_SSPM_PSRCEN | BA0_SSPM_JSEN |
+ BA0_SSPM_ACLEN | BA0_SSPM_FMEN);
+
+ /* Serial Port Power Management */
+ /* Blast the clock control register to zero so that the
+ * PLL starts out in a known state, and blast the master serial
+ * port control register to zero so that the serial ports also
+ * start out in a known state. */
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+ snd_cs4281_pokeBA0(chip, BA0_SERMC, 0);
+
+ /* Make ESYN go to zero to turn off
+ * the Sync pulse on the AC97 link. */
+ snd_cs4281_pokeBA0(chip, BA0_ACCTL, 0);
+ udelay(50);
+
+ /* Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
+ * spec) and then drive it high. This is done for non AC97 modes since
+ * there might be logic external to the CS4281 that uses the ARST# line
+ * for a reset. */
+ snd_cs4281_pokeBA0(chip, BA0_SPMC, 0);
+ udelay(50);
+ snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN);
+ snd_cs4281_delay(50000);
+
+ if (chip->dual_codec)
+ snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN | BA0_SPMC_ASDI2E);
+
+ /*
+ * Set the serial port timing configuration.
+ */
+ snd_cs4281_pokeBA0(chip, BA0_SERMC,
+ (chip->dual_codec ? BA0_SERMC_TCID(chip->dual_codec) : BA0_SERMC_TCID(1)) |
+ BA0_SERMC_PTC_AC97 | BA0_SERMC_MSPE);
+
+ /*
+ * Start the DLL Clock logic.
+ */
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP);
+ snd_cs4281_delay(50000);
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP);
+
+ /*
+ * Wait for the DLL ready signal from the clock logic.
+ */
+ timeout = HZ;
+ do {
+ /*
+ * Read the AC97 status register to see if we've seen a CODEC
+ * signal from the AC97 codec.
+ */
+ if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY)
+ goto __ok0;
+ snd_cs4281_delay_long();
+ } while (timeout-- > 0);
+
+ snd_printk(KERN_ERR "DLLRDY not seen\n");
+ return -EIO;
+
+ __ok0:
+
+ /*
+ * The first thing we do here is to enable sync generation. As soon
+ * as we start receiving bit clock, we'll start producing the SYNC
+ * signal.
+ */
+ snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_ESYN);
+
+ /*
+ * Wait for the codec ready signal from the AC97 codec.
+ */
+ timeout = HZ;
+ do {
+ /*
+ * Read the AC97 status register to see if we've seen a CODEC
+ * signal from the AC97 codec.
+ */
+ if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY)
+ goto __ok1;
+ snd_cs4281_delay_long();
+ } while (timeout-- > 0);
+
+ snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
+ return -EIO;
+
+ __ok1:
+ if (chip->dual_codec) {
+ timeout = HZ;
+ do {
+ if (snd_cs4281_peekBA0(chip, BA0_ACSTS2) & BA0_ACSTS_CRDY)
+ goto __codec2_ok;
+ snd_cs4281_delay_long();
+ } while (timeout-- > 0);
+ snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n");
+ chip->dual_codec = 0;
+ __codec2_ok: ;
+ }
+
+ /*
+ * Assert the valid frame signal so that we can start sending commands
+ * to the AC97 codec.
+ */
+
+ snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_VFRM | BA0_ACCTL_ESYN);
+
+ /*
+ * Wait until we've sampled input slots 3 and 4 as valid, meaning that
+ * the codec is pumping ADC data across the AC-link.
+ */
+
+ timeout = HZ;
+ do {
+ /*
+ * Read the input slot valid register and see if input slots 3
+ * 4 are valid yet.
+ */
+ if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4)))
+ goto __ok2;
+ snd_cs4281_delay_long();
+ } while (timeout-- > 0);
+
+ if (--retry_count > 0)
+ goto __retry;
+ snd_printk(KERN_ERR "never read ISV3 and ISV4 from AC'97\n");
+ return -EIO;
+
+ __ok2:
+
+ /*
+ * Now, assert valid frame and the slot 3 and 4 valid bits. This will
+ * commense the transfer of digital audio data to the AC97 codec.
+ */
+ snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4));
+
+ /*
+ * Initialize DMA structures
+ */
+ for (tmp = 0; tmp < 4; tmp++) {
+ cs4281_dma_t *dma = &chip->dma[tmp];
+ dma->regDBA = BA0_DBA0 + (tmp * 0x10);
+ dma->regDCA = BA0_DCA0 + (tmp * 0x10);
+ dma->regDBC = BA0_DBC0 + (tmp * 0x10);
+ dma->regDCC = BA0_DCC0 + (tmp * 0x10);
+ dma->regDMR = BA0_DMR0 + (tmp * 8);
+ dma->regDCR = BA0_DCR0 + (tmp * 8);
+ dma->regHDSR = BA0_HDSR0 + (tmp * 4);
+ dma->regFCR = BA0_FCR0 + (tmp * 4);
+ dma->regFSIC = BA0_FSIC0 + (tmp * 4);
+ dma->fifo_offset = tmp * CS4281_FIFO_SIZE;
+ snd_cs4281_pokeBA0(chip, dma->regFCR,
+ BA0_FCR_LS(31) |
+ BA0_FCR_RS(31) |
+ BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+ BA0_FCR_OF(dma->fifo_offset));
+ }
+
+ chip->src_left_play_slot = 0; /* AC'97 left PCM playback (3) */
+ chip->src_right_play_slot = 1; /* AC'97 right PCM playback (4) */
+ chip->src_left_rec_slot = 10; /* AC'97 left PCM record (3) */
+ chip->src_right_rec_slot = 11; /* AC'97 right PCM record (4) */
+
+ /* Activate wave playback FIFO for FM playback */
+ chip->dma[0].valFCR = BA0_FCR_FEN | BA0_FCR_LS(0) |
+ BA0_FCR_RS(1) |
+ BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+ BA0_FCR_OF(chip->dma[0].fifo_offset);
+ snd_cs4281_pokeBA0(chip, chip->dma[0].regFCR, chip->dma[0].valFCR);
+ snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) |
+ (chip->src_right_play_slot << 8) |
+ (chip->src_left_rec_slot << 16) |
+ (chip->src_right_rec_slot << 24));
+
+ /* Initialize digital volume */
+ snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0);
+ snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0);
+
+ /* Enable IRQs */
+ snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+ /* Unmask interrupts */
+ snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff & ~(
+ BA0_HISR_MIDI |
+ BA0_HISR_DMAI |
+ BA0_HISR_DMA(0) |
+ BA0_HISR_DMA(1) |
+ BA0_HISR_DMA(2) |
+ BA0_HISR_DMA(3)));
+ synchronize_irq(chip->irq);
+
+ return 0;
+}
+
+/*
+ * MIDI section
+ */
+
+static void snd_cs4281_midi_reset(cs4281_t *chip)
+{
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr | BA0_MIDCR_MRST);
+ udelay(100);
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+}
+
+static int snd_cs4281_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+ cs4281_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->midcr |= BA0_MIDCR_RXE;
+ chip->midi_input = substream;
+ if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
+ snd_cs4281_midi_reset(chip);
+ } else {
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cs4281_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+ cs4281_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE);
+ chip->midi_input = NULL;
+ if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
+ snd_cs4281_midi_reset(chip);
+ } else {
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS4281_MODE_INPUT;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cs4281_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+ cs4281_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->uartm |= CS4281_MODE_OUTPUT;
+ chip->midcr |= BA0_MIDCR_TXE;
+ chip->midi_output = substream;
+ if (!(chip->uartm & CS4281_MODE_INPUT)) {
+ snd_cs4281_midi_reset(chip);
+ } else {
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cs4281_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+ cs4281_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE);
+ chip->midi_output = NULL;
+ if (!(chip->uartm & CS4281_MODE_INPUT)) {
+ snd_cs4281_midi_reset(chip);
+ } else {
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS4281_MODE_OUTPUT;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static void snd_cs4281_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ cs4281_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (up) {
+ if ((chip->midcr & BA0_MIDCR_RIE) == 0) {
+ chip->midcr |= BA0_MIDCR_RIE;
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ } else {
+ if (chip->midcr & BA0_MIDCR_RIE) {
+ chip->midcr &= ~BA0_MIDCR_RIE;
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4281_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ cs4281_t *chip = substream->rmidi->private_data;
+ unsigned char byte;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (up) {
+ if ((chip->midcr & BA0_MIDCR_TIE) == 0) {
+ chip->midcr |= BA0_MIDCR_TIE;
+ /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+ while ((chip->midcr & BA0_MIDCR_TIE) &&
+ (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) {
+ if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ chip->midcr &= ~BA0_MIDCR_TIE;
+ } else {
+ snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte);
+ }
+ }
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ } else {
+ if (chip->midcr & BA0_MIDCR_TIE) {
+ chip->midcr &= ~BA0_MIDCR_TIE;
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static snd_rawmidi_ops_t snd_cs4281_midi_output =
+{
+ .open = snd_cs4281_midi_output_open,
+ .close = snd_cs4281_midi_output_close,
+ .trigger = snd_cs4281_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_cs4281_midi_input =
+{
+ .open = snd_cs4281_midi_input_open,
+ .close = snd_cs4281_midi_input_close,
+ .trigger = snd_cs4281_midi_input_trigger,
+};
+
+static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t **rrawmidi)
+{
+ snd_rawmidi_t *rmidi;
+ int err;
+
+ if (rrawmidi)
+ *rrawmidi = NULL;
+ if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0)
+ return err;
+ strcpy(rmidi->name, "CS4281");
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = chip;
+ chip->rmidi = rmidi;
+ if (rrawmidi)
+ *rrawmidi = rmidi;
+ return 0;
+}
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ cs4281_t *chip = dev_id;
+ unsigned int status, dma, val;
+ cs4281_dma_t *cdma;
+
+ if (chip == NULL)
+ return IRQ_NONE;
+ status = snd_cs4281_peekBA0(chip, BA0_HISR);
+ if ((status & 0x7fffffff) == 0) {
+ snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+ return IRQ_NONE;
+ }
+
+ if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) {
+ for (dma = 0; dma < 4; dma++)
+ if (status & BA0_HISR_DMA(dma)) {
+ cdma = &chip->dma[dma];
+ spin_lock(&chip->reg_lock);
+ /* ack DMA IRQ */
+ val = snd_cs4281_peekBA0(chip, cdma->regHDSR);
+ /* workaround, sometimes CS4281 acknowledges */
+ /* end or middle transfer position twice */
+ cdma->frag++;
+ if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) {
+ cdma->frag--;
+ chip->spurious_dhtc_irq++;
+ spin_unlock(&chip->reg_lock);
+ continue;
+ }
+ if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) {
+ cdma->frag--;
+ chip->spurious_dtc_irq++;
+ spin_unlock(&chip->reg_lock);
+ continue;
+ }
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(cdma->substream);
+ }
+ }
+
+ if ((status & BA0_HISR_MIDI) && chip->rmidi) {
+ unsigned char c;
+
+ spin_lock(&chip->reg_lock);
+ while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) {
+ c = snd_cs4281_peekBA0(chip, BA0_MIDRP);
+ if ((chip->midcr & BA0_MIDCR_RIE) == 0)
+ continue;
+ snd_rawmidi_receive(chip->midi_input, &c, 1);
+ }
+ while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) {
+ if ((chip->midcr & BA0_MIDCR_TIE) == 0)
+ break;
+ if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) {
+ chip->midcr &= ~BA0_MIDCR_TIE;
+ snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ break;
+ }
+ snd_cs4281_pokeBA0(chip, BA0_MIDWP, c);
+ }
+ spin_unlock(&chip->reg_lock);
+ }
+
+ /* EOI to the PCI part... reenables interrupts */
+ snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * OPL3 command
+ */
+static void snd_cs4281_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val)
+{
+ unsigned long flags;
+ cs4281_t *chip = opl3->private_data;
+ void __iomem *port;
+
+ if (cmd & OPL3_RIGHT)
+ port = chip->ba0 + BA0_B1AP; /* right port */
+ else
+ port = chip->ba0 + BA0_B0AP; /* left port */
+
+ spin_lock_irqsave(&opl3->reg_lock, flags);
+
+ writel((unsigned int)cmd, port);
+ udelay(10);
+
+ writel((unsigned int)val, port + 4);
+ udelay(30);
+
+ spin_unlock_irqrestore(&opl3->reg_lock, flags);
+}
+
+static int __devinit snd_cs4281_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ cs4281_t *chip;
+ opl3_t *opl3;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_cs4281_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ opl3->private_data = chip;
+ opl3->command = snd_cs4281_opl3_command;
+ snd_opl3_init(opl3);
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_cs4281_create_gameport(chip);
+ strcpy(card->driver, "CS4281");
+ strcpy(card->shortname, "Cirrus Logic CS4281");
+ sprintf(card->longname, "%s at 0x%lx, irq %d",
+ card->shortname,
+ chip->ba0_addr,
+ chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_cs4281_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/*
+ * Power Management
+ */
+#ifdef CONFIG_PM
+
+static int saved_regs[SUSPEND_REGISTERS] = {
+ BA0_JSCTL,
+ BA0_GPIOR,
+ BA0_SSCR,
+ BA0_MIDCR,
+ BA0_SRCSA,
+ BA0_PASR,
+ BA0_CASR,
+ BA0_DACSR,
+ BA0_ADCSR,
+ BA0_FMLVC,
+ BA0_FMRVC,
+ BA0_PPLVC,
+ BA0_PPRVC,
+};
+
+#define CLKCR1_CKRA 0x00010000L
+
+static int cs4281_suspend(snd_card_t *card, pm_message_t state)
+{
+ cs4281_t *chip = card->pm_private_data;
+ u32 ulCLK;
+ unsigned int i;
+
+ snd_pcm_suspend_all(chip->pcm);
+
+ if (chip->ac97)
+ snd_ac97_suspend(chip->ac97);
+ if (chip->ac97_secondary)
+ snd_ac97_suspend(chip->ac97_secondary);
+
+ ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+ ulCLK |= CLKCR1_CKRA;
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+ /* Disable interrupts. */
+ snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_CHGM);
+
+ /* remember the status registers */
+ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+ if (saved_regs[i])
+ chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]);
+
+ /* Turn off the serial ports. */
+ snd_cs4281_pokeBA0(chip, BA0_SERMC, 0);
+
+ /* Power off FM, Joystick, AC link, */
+ snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
+
+ /* DLL off. */
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+
+ /* AC link off. */
+ snd_cs4281_pokeBA0(chip, BA0_SPMC, 0);
+
+ ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+ ulCLK &= ~CLKCR1_CKRA;
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int cs4281_resume(snd_card_t *card)
+{
+ cs4281_t *chip = card->pm_private_data;
+ unsigned int i;
+ u32 ulCLK;
+
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+
+ ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+ ulCLK |= CLKCR1_CKRA;
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+ snd_cs4281_chip_init(chip);
+
+ /* restore the status registers */
+ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+ if (saved_regs[i])
+ snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]);
+
+ if (chip->ac97)
+ snd_ac97_resume(chip->ac97);
+ if (chip->ac97_secondary)
+ snd_ac97_resume(chip->ac97_secondary);
+
+ ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+ ulCLK &= ~CLKCR1_CKRA;
+ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_driver driver = {
+ .name = "CS4281",
+ .id_table = snd_cs4281_ids,
+ .probe = snd_cs4281_probe,
+ .remove = __devexit_p(snd_cs4281_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_cs4281_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_cs4281_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs4281_init)
+module_exit(alsa_card_cs4281_exit)
diff --git a/sound/pci/cs46xx/Makefile b/sound/pci/cs46xx/Makefile
new file mode 100644
index 00000000000..d8b77b89aec
--- /dev/null
+++ b/sound/pci/cs46xx/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-cs46xx-objs := cs46xx.o cs46xx_lib.o
+ifeq ($(CONFIG_SND_CS46XX_NEW_DSP),y)
+ snd-cs46xx-objs += dsp_spos.o dsp_spos_scb_lib.o
+endif
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_CS46XX) += snd-cs46xx.o
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
new file mode 100644
index 00000000000..25d6466a867
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -0,0 +1,183 @@
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ NOTES:
+ - sometimes the sound is metallic and sibilant, unloading and
+ reloading the module may solve this.
+*/
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/cs46xx.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,Sound Fusion (CS4280)},"
+ "{Cirrus Logic,Sound Fusion (CS4610)},"
+ "{Cirrus Logic,Sound Fusion (CS4612)},"
+ "{Cirrus Logic,Sound Fusion (CS4615)},"
+ "{Cirrus Logic,Sound Fusion (CS4622)},"
+ "{Cirrus Logic,Sound Fusion (CS4624)},"
+ "{Cirrus Logic,Sound Fusion (CS4630)}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable CS46xx soundcard.");
+module_param_array(external_amp, bool, NULL, 0444);
+MODULE_PARM_DESC(external_amp, "Force to enable external amplifer.");
+module_param_array(thinkpad, bool, NULL, 0444);
+MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
+module_param_array(mmap_valid, bool, NULL, 0444);
+MODULE_PARM_DESC(mmap_valid, "Support OSS mmap.");
+
+static struct pci_device_id snd_cs46xx_ids[] = {
+ { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4280 */
+ { 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4612 */
+ { 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4615 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids);
+
+static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ cs46xx_t *chip;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+ if ((err = snd_cs46xx_create(card, pci,
+ external_amp[dev], thinkpad[dev],
+ &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ chip->accept_valid = mmap_valid[dev];
+ if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if ((err = snd_cs46xx_pcm_rear(chip,1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_cs46xx_pcm_iec958(chip,2,NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#endif
+ if ((err = snd_cs46xx_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if (chip->nr_ac97_codecs ==2) {
+ if ((err = snd_cs46xx_pcm_center_lfe(chip,3,NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+#endif
+ if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_cs46xx_start_dsp(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+
+ snd_cs46xx_gameport(chip);
+
+ strcpy(card->driver, "CS46xx");
+ strcpy(card->shortname, "Sound Fusion CS46xx");
+ sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i",
+ card->shortname,
+ chip->ba0_addr,
+ chip->ba1_addr,
+ chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Sound Fusion CS46xx",
+ .id_table = snd_cs46xx_ids,
+ .probe = snd_card_cs46xx_probe,
+ .remove = __devexit_p(snd_card_cs46xx_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_cs46xx_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_cs46xx_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs46xx_init)
+module_exit(alsa_card_cs46xx_exit)
diff --git a/sound/pci/cs46xx/cs46xx_image.h b/sound/pci/cs46xx/cs46xx_image.h
new file mode 100644
index 00000000000..dc93f62db2c
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_image.h
@@ -0,0 +1,3468 @@
+struct BA1struct {
+ struct {
+ unsigned long offset;
+ unsigned long size;
+ } memory[BA1_MEMORY_COUNT];
+ u32 map[BA1_DWORD_SIZE];
+};
+
+
+static struct BA1struct BA1Struct = {
+{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }},
+{0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00200040,0x00008010,0x00000000,
+0x00000000,0x80000001,0x00000001,0x00060000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00900080,0x00000173,0x00000000,
+0x00000000,0x00000010,0x00800000,0x00900000,
+0xf2c0000f,0x00000200,0x00000000,0x00010600,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x330300c2,
+0x06000000,0x00000000,0x80008000,0x80008000,
+0x3fc0000f,0x00000301,0x00010400,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00b00000,0x00d0806d,0x330480c3,
+0x04800000,0x00000001,0x00800001,0x0000ffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x066a0600,0x06350070,0x0000929d,0x929d929d,
+0x00000000,0x0000735a,0x00000600,0x00000000,
+0x929d735a,0x8734abfe,0x00010000,0x735a735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000804f,0x000000c3,
+0x05000000,0x00a00010,0x00000000,0x80008000,
+0x00000000,0x00000000,0x00000700,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000080,0x00a00000,0x0000809a,0x000000c2,
+0x07400000,0x00000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x000107a0,
+0x00c80028,0x000000c2,0x06800000,0x00000000,
+0x06e00080,0x00300000,0x000080bb,0x000000c9,
+0x07a00000,0x04000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x00000780,
+0x00c80028,0x000000c5,0xff800000,0x00000000,
+0x00640080,0x00c00000,0x00008197,0x000000c9,
+0x07800000,0x04000000,0x80008000,0xffffffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000805e,0x000000c1,
+0x00000000,0x00800000,0x80008000,0x80008000,
+0x00020000,0x0000ffff,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x929d0600,0x929d929d,0x929d929d,0x929d0000,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x00100635,0x060b013f,0x00000004,
+0x00000001,0x007a0002,0x00000000,0x066e0610,
+0x0105929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
+0x00000000,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x00000000,0x06400136,
+0x0000270f,0x00010000,0x007a0000,0x00000000,
+0x068e0645,0x0105929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x735a0100,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00010004,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00001705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00009705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00011705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00019705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00021705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00029705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00031705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00039705,0x00001400,0x000a411e,0x00001003,
+0x000fe19e,0x00001003,0x0009c730,0x00001003,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00009705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00011705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00019705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00021705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00029705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00031705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00039705,0x00001400,0x000a211e,0x00001003,
+0x0000a730,0x00001008,0x000e2730,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x00000000,0x00000000,0x000f619c,0x00001003,
+0x0007f801,0x000c0000,0x00000037,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0000373c,0x00001000,0x00000000,0x00000000,
+0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000273c,0x00001000,
+0x00000033,0x00001000,0x000e679e,0x00001003,
+0x00007705,0x00001400,0x000ac71e,0x00001003,
+0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000a730,0x00001003,
+0x00000033,0x00001000,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x000c0000,
+0x00000032,0x00001000,0x0000273d,0x00001000,
+0x0004a730,0x00001003,0x00000f41,0x00097140,
+0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+0x00002725,0x000aa400,0x00013705,0x00093a00,
+0x0000002e,0x0009d6c0,0x00038630,0x00001004,
+0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
+0x00000000,0x000c70e0,0x0007d182,0x0002c640,
+0x00000630,0x00001004,0x000799b8,0x0002c6c0,
+0x00031705,0x00092240,0x00039f05,0x000932c0,
+0x0003520a,0x00000000,0x00040731,0x0000100b,
+0x00010705,0x000b20c0,0x00000000,0x000eba44,
+0x00032108,0x000c60c4,0x00065208,0x000c2917,
+0x000406b0,0x00001007,0x00012f05,0x00036880,
+0x0002818e,0x000c0000,0x0004410a,0x00000000,
+0x00040630,0x00001007,0x00029705,0x000c0000,
+0x00000000,0x00000000,0x00003fc1,0x0003fc40,
+0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
+0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
+0x000037c1,0x00000000,0x00003fc1,0x000991c0,
+0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
+0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
+0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
+0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
+0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
+0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
+0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
+0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
+0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
+0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
+0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
+0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
+0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
+0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
+0x000683ad,0x00095241,0x00020f05,0x000991c1,
+0x00000000,0x00000000,0x00086f88,0x00001000,
+0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
+0x0009de81,0x000bd300,0x0009d601,0x000b1700,
+0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
+0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
+0x000a1681,0x000b97c0,0x00021601,0x00002500,
+0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
+0x00021681,0x00002d00,0x00020f81,0x000bd800,
+0x000a0701,0x000b5bc0,0x00021601,0x00003500,
+0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
+0x00021681,0x00003d00,0x00020f81,0x000b1d00,
+0x000a0701,0x000b1fc0,0x00021601,0x00020500,
+0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
+0x00021681,0x00020d00,0x00020f81,0x000bde80,
+0x000a0701,0x000bdfc0,0x00021601,0x00021500,
+0x00020f81,0x000b9341,0x00020701,0x000b53c1,
+0x00021681,0x00021d00,0x000a0f81,0x000d0380,
+0x0000b601,0x000b15c0,0x00007b01,0x00000000,
+0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
+0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
+0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
+0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
+0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
+0x0007e48a,0x00000000,0x00011f05,0x00084080,
+0x00000000,0x00000000,0x00001705,0x000b3540,
+0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
+0x00055488,0x00000000,0x0000d482,0x0003fc40,
+0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
+0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
+0x000c86b0,0x00001007,0x00008281,0x000bb240,
+0x0000b801,0x000b7140,0x00007888,0x00000000,
+0x0000073c,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x00055288,0x000c555c,
+0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+0x0000fa88,0x00000000,0x00000032,0x00001000,
+0x0000073d,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x0008c01c,0x00001003,
+0x00002705,0x00001008,0x0008b201,0x000c1392,
+0x0000ba01,0x00000000,0x00008731,0x00001400,
+0x0004c108,0x000fe0c4,0x00057488,0x00000000,
+0x000a6388,0x00001001,0x0008b334,0x000bc141,
+0x0003020e,0x00000000,0x000886b0,0x00001008,
+0x00003625,0x000c5dfa,0x000a638a,0x00001001,
+0x0008020e,0x00001002,0x0008a6b0,0x00001008,
+0x0007f301,0x00000000,0x00000000,0x00000000,
+0x00002725,0x000a8c40,0x000000ae,0x00000000,
+0x000d8630,0x00001008,0x00000000,0x000c74e0,
+0x0007d182,0x0002d640,0x000a8630,0x00001008,
+0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5,
+0x0007420a,0x000c0000,0x00062208,0x000c4117,
+0x00070630,0x00001009,0x00000000,0x000c0000,
+0x0001022e,0x00000000,0x0003a630,0x00001009,
+0x00000000,0x000c0000,0x00000036,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x0002a730,0x00001008,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0002a730,0x00001008,
+0x00000033,0x00001000,0x0002a705,0x00001008,
+0x00007a01,0x000c0000,0x000e6288,0x000d550a,
+0x0006428a,0x00000000,0x00060730,0x0000100a,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
+0x00057488,0x00000000,0x00033b94,0x00081140,
+0x000183ae,0x00000000,0x000786b0,0x0000100b,
+0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+0x00042731,0x00001003,0x0007aab0,0x00034880,
+0x00048fb0,0x0000100a,0x00057488,0x00000000,
+0x00033b94,0x00081140,0x000183ae,0x00000000,
+0x000806b0,0x0000100b,0x00022f05,0x00000000,
+0x00007401,0x00091140,0x00048f05,0x000951c0,
+0x00042731,0x00001003,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x000fe19e,0x00001003,0x00000000,0x00000000,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00000f41,0x00097140,0x0000a841,0x0009b240,
+0x0000a0c1,0x0009f040,0x0001c641,0x00093540,
+0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44,
+0x00055208,0x00000000,0x00010705,0x000a2880,
+0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5,
+0x0004ef0a,0x000c0000,0x00012f05,0x00036880,
+0x00065308,0x000c2997,0x000d86b0,0x0000100a,
+0x0004410a,0x000d40c7,0x00000000,0x00000000,
+0x00080730,0x00001004,0x00056f0a,0x000ea105,
+0x00000000,0x00000000,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x0000273d,0x00001000,0x00000000,0x000eba44,
+0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x00000000,0x000e5084,
+0x00000000,0x000eba44,0x00087401,0x000e4782,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x0007c108,0x000c0000,
+0x0007e721,0x000bed40,0x00005f25,0x000badc0,
+0x0003ba97,0x000beb80,0x00065590,0x000b2e00,
+0x00033217,0x00003ec0,0x00065590,0x000b8e40,
+0x0003ed80,0x000491c0,0x00073fb0,0x00074c80,
+0x000283a0,0x0000100c,0x000ee388,0x00042970,
+0x00008301,0x00021ef2,0x000b8f14,0x0000000f,
+0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6,
+0x000032ac,0x000c3916,0x0004edc2,0x00074c80,
+0x00078898,0x00001000,0x00038894,0x00000032,
+0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6,
+0x0004edc2,0x000c1956,0x0000722c,0x00034a00,
+0x00041705,0x0009ed40,0x00058730,0x00001400,
+0x000d7488,0x000c3a00,0x00048f05,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000}
+ };
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
new file mode 100644
index 00000000000..5f2ffb7efa0
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -0,0 +1,3922 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Abramo Bagnara <abramo@alsa-project.org>
+ * Cirrus Logic, Inc.
+ * Routines for control of Cirrus Logic CS461x chips
+ *
+ * KNOWN BUGS:
+ * - Sometimes the SPDIF input DSP tasks get's unsynchronized
+ * and the SPDIF get somewhat "distorcionated", or/and left right channel
+ * are swapped. To get around this problem when it happens, mute and unmute
+ * the SPDIF input mixer controll.
+ * - On the Hercules Game Theater XP the amplifier are sometimes turned
+ * off on inadecuate moments which causes distorcions on sound.
+ *
+ * TODO:
+ * - Secondary CODEC on some soundcards
+ * - SPDIF input support for other sample rates then 48khz
+ * - Posibility to mix the SPDIF output with analog sources.
+ * - PCM channels for Center and LFE on secondary codec
+ *
+ * NOTE: with CONFIG_SND_CS46XX_NEW_DSP unset uses old DSP image (which
+ * is default configuration), no SPDIF, no secondary codec, no
+ * multi channel PCM. But known to work.
+ *
+ * FINALLY: A credit to the developers Tom and Jordan
+ * at Cirrus for have helping me out with the DSP, however we
+ * still don't have sufficient documentation and technical
+ * references to be able to implement all fancy feutures
+ * supported by the cs46xx DSP's.
+ * Benny <benny@hostmobility.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/cs46xx.h>
+
+#include <asm/io.h>
+
+#include "cs46xx_lib.h"
+#include "dsp_spos.h"
+
+static void amp_voyetra(cs46xx_t *chip, int change);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static snd_pcm_ops_t snd_cs46xx_playback_rear_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_clfe_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_clfe_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_iec958_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops;
+#endif
+
+static snd_pcm_ops_t snd_cs46xx_playback_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_ops;
+static snd_pcm_ops_t snd_cs46xx_capture_ops;
+static snd_pcm_ops_t snd_cs46xx_capture_indirect_ops;
+
+static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip,
+ unsigned short reg,
+ int codec_index)
+{
+ int count;
+ unsigned short result,tmp;
+ u32 offset = 0;
+ snd_assert ( (codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
+ (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
+ return -EINVAL);
+
+ chip->active_ctrl(chip, 1);
+
+ if (codec_index == CS46XX_SECONDARY_CODEC_INDEX)
+ offset = CS46XX_SECONDARY_CODEC_OFFSET;
+
+ /*
+ * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+ * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97
+ * 3. Write ACCTL = Control Register = 460h for initiating the write7---55
+ * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+ * 5. if DCV not cleared, break and return error
+ * 6. Read ACSTS = Status Register = 464h, check VSTS bit
+ */
+
+ snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset);
+
+ tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL);
+ if ((tmp & ACCTL_VFRM) == 0) {
+ snd_printk(KERN_WARNING "cs46xx: ACCTL_VFRM not set 0x%x\n",tmp);
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM );
+ msleep(50);
+ tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset);
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, tmp | ACCTL_ESYN | ACCTL_VFRM );
+
+ }
+
+ /*
+ * Setup the AC97 control registers on the CS461x to send the
+ * appropriate command to the AC97 to perform the read.
+ * ACCAD = Command Address Register = 46Ch
+ * ACCDA = Command Data Register = 470h
+ * ACCTL = Control Register = 460h
+ * set DCV - will clear when process completed
+ * set CRW - Read command
+ * set VFRM - valid frame enabled
+ * set ESYN - ASYNC generation enabled
+ * set RSTN - ARST# inactive, AC97 codec not reset
+ */
+
+ snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);
+ snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0);
+ if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL,/* clear ACCTL_DCV */ ACCTL_CRW |
+ ACCTL_VFRM | ACCTL_ESYN |
+ ACCTL_RSTN);
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |
+ ACCTL_VFRM | ACCTL_ESYN |
+ ACCTL_RSTN);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |
+ ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN |
+ ACCTL_RSTN);
+ }
+
+ /*
+ * Wait for the read to occur.
+ */
+ for (count = 0; count < 1000; count++) {
+ /*
+ * First, we want to wait for a short time.
+ */
+ udelay(10);
+ /*
+ * Now, check to see if the read has completed.
+ * ACCTL = 460h, DCV should be reset by now and 460h = 17h
+ */
+ if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV))
+ goto ok1;
+ }
+
+ snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+ result = 0xffff;
+ goto end;
+
+ ok1:
+ /*
+ * Wait for the valid status bit to go active.
+ */
+ for (count = 0; count < 100; count++) {
+ /*
+ * Read the AC97 status register.
+ * ACSTS = Status Register = 464h
+ * VSTS - Valid Status
+ */
+ if (snd_cs46xx_peekBA0(chip, BA0_ACSTS + offset) & ACSTS_VSTS)
+ goto ok2;
+ udelay(10);
+ }
+
+ snd_printk("AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n", codec_index, reg);
+ result = 0xffff;
+ goto end;
+
+ ok2:
+ /*
+ * Read the data returned from the AC97 register.
+ * ACSDA = Status Data Register = 474h
+ */
+#if 0
+ printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
+ snd_cs46xx_peekBA0(chip, BA0_ACSDA),
+ snd_cs46xx_peekBA0(chip, BA0_ACCAD));
+#endif
+
+ //snd_cs46xx_peekBA0(chip, BA0_ACCAD);
+ result = snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset);
+ end:
+ chip->active_ctrl(chip, -1);
+ return result;
+}
+
+static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97,
+ unsigned short reg)
+{
+ cs46xx_t *chip = ac97->private_data;
+ unsigned short val;
+ int codec_index = ac97->num;
+
+ snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
+ codec_index == CS46XX_SECONDARY_CODEC_INDEX,
+ return 0xffff);
+
+ val = snd_cs46xx_codec_read(chip, reg, codec_index);
+
+ return val;
+}
+
+
+static void snd_cs46xx_codec_write(cs46xx_t *chip,
+ unsigned short reg,
+ unsigned short val,
+ int codec_index)
+{
+ int count;
+
+ snd_assert ((codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
+ (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
+ return);
+
+ chip->active_ctrl(chip, 1);
+
+ /*
+ * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+ * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97
+ * 3. Write ACCTL = Control Register = 460h for initiating the write
+ * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+ * 5. if DCV not cleared, break and return error
+ */
+
+ /*
+ * Setup the AC97 control registers on the CS461x to send the
+ * appropriate command to the AC97 to perform the read.
+ * ACCAD = Command Address Register = 46Ch
+ * ACCDA = Command Data Register = 470h
+ * ACCTL = Control Register = 460h
+ * set DCV - will clear when process completed
+ * reset CRW - Write command
+ * set VFRM - valid frame enabled
+ * set ESYN - ASYNC generation enabled
+ * set RSTN - ARST# inactive, AC97 codec not reset
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_ACCAD , reg);
+ snd_cs46xx_pokeBA0(chip, BA0_ACCDA , val);
+ snd_cs46xx_peekBA0(chip, BA0_ACCTL);
+
+ if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, /* clear ACCTL_DCV */ ACCTL_VFRM |
+ ACCTL_ESYN | ACCTL_RSTN);
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM |
+ ACCTL_ESYN | ACCTL_RSTN);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |
+ ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+ }
+
+ for (count = 0; count < 4000; count++) {
+ /*
+ * First, we want to wait for a short time.
+ */
+ udelay(10);
+ /*
+ * Now, check to see if the write has completed.
+ * ACCTL = 460h, DCV should be reset by now and 460h = 07h
+ */
+ if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) {
+ goto end;
+ }
+ }
+ snd_printk("AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n", codec_index, reg, val);
+ end:
+ chip->active_ctrl(chip, -1);
+}
+
+static void snd_cs46xx_ac97_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ cs46xx_t *chip = ac97->private_data;
+ int codec_index = ac97->num;
+
+ snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
+ codec_index == CS46XX_SECONDARY_CODEC_INDEX,
+ return);
+
+ snd_cs46xx_codec_write(chip, reg, val, codec_index);
+}
+
+
+/*
+ * Chip initialization
+ */
+
+int snd_cs46xx_download(cs46xx_t *chip,
+ u32 *src,
+ unsigned long offset,
+ unsigned long len)
+{
+ void __iomem *dst;
+ unsigned int bank = offset >> 16;
+ offset = offset & 0xffff;
+
+ snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+ dst = chip->region.idx[bank+1].remap_addr + offset;
+ len /= sizeof(u32);
+
+ /* writel already converts 32-bit value to right endianess */
+ while (len-- > 0) {
+ writel(*src++, dst);
+ dst += sizeof(u32);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+#include "imgs/cwc4630.h"
+#include "imgs/cwcasync.h"
+#include "imgs/cwcsnoop.h"
+#include "imgs/cwcbinhack.h"
+#include "imgs/cwcdma.h"
+
+int snd_cs46xx_clear_BA1(cs46xx_t *chip,
+ unsigned long offset,
+ unsigned long len)
+{
+ void __iomem *dst;
+ unsigned int bank = offset >> 16;
+ offset = offset & 0xffff;
+
+ snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+ dst = chip->region.idx[bank+1].remap_addr + offset;
+ len /= sizeof(u32);
+
+ /* writel already converts 32-bit value to right endianess */
+ while (len-- > 0) {
+ writel(0, dst);
+ dst += sizeof(u32);
+ }
+ return 0;
+}
+
+#else /* old DSP image */
+
+#include "cs46xx_image.h"
+
+int snd_cs46xx_download_image(cs46xx_t *chip)
+{
+ int idx, err;
+ unsigned long offset = 0;
+
+ for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
+ if ((err = snd_cs46xx_download(chip,
+ &BA1Struct.map[offset],
+ BA1Struct.memory[idx].offset,
+ BA1Struct.memory[idx].size)) < 0)
+ return err;
+ offset += BA1Struct.memory[idx].size >> 2;
+ }
+ return 0;
+}
+#endif /* CONFIG_SND_CS46XX_NEW_DSP */
+
+/*
+ * Chip reset
+ */
+
+static void snd_cs46xx_reset(cs46xx_t *chip)
+{
+ int idx;
+
+ /*
+ * Write the reset bit of the SP control register.
+ */
+ snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP);
+
+ /*
+ * Write the control register.
+ */
+ snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN);
+
+ /*
+ * Clear the trap registers.
+ */
+ for (idx = 0; idx < 8; idx++) {
+ snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx);
+ snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF);
+ }
+ snd_cs46xx_poke(chip, BA1_DREG, 0);
+
+ /*
+ * Set the frame timer to reflect the number of cycles per frame.
+ */
+ snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
+}
+
+static int cs46xx_wait_for_fifo(cs46xx_t * chip,int retry_timeout)
+{
+ u32 i, status = 0;
+ /*
+ * Make sure the previous FIFO write operation has completed.
+ */
+ for(i = 0; i < 50; i++){
+ status = snd_cs46xx_peekBA0(chip, BA0_SERBST);
+
+ if( !(status & SERBST_WBSY) )
+ break;
+
+ mdelay(retry_timeout);
+ }
+
+ if(status & SERBST_WBSY) {
+ snd_printk( KERN_ERR "cs46xx: failure waiting for FIFO command to complete\n");
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip)
+{
+ int idx, powerdown = 0;
+ unsigned int tmp;
+
+ /*
+ * See if the devices are powered down. If so, we must power them up first
+ * or they will not respond.
+ */
+ tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
+ if (!(tmp & CLKCR1_SWCE)) {
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
+ powerdown = 1;
+ }
+
+ /*
+ * We want to clear out the serial port FIFOs so we don't end up playing
+ * whatever random garbage happens to be in them. We fill the sample FIFOS
+ * with zero (silence).
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0);
+
+ /*
+ * Fill all 256 sample FIFO locations.
+ */
+ for (idx = 0; idx < 0xFF; idx++) {
+ /*
+ * Make sure the previous FIFO write operation has completed.
+ */
+ if (cs46xx_wait_for_fifo(chip,1)) {
+ snd_printdd ("failed waiting for FIFO at addr (%02X)\n",idx);
+
+ if (powerdown)
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+
+ break;
+ }
+ /*
+ * Write the serial port FIFO index.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
+ /*
+ * Tell the serial port to load the new value into the FIFO location.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
+ }
+ /*
+ * Now, if we powered up the devices, then power them back down again.
+ * This is kinda ugly, but should never happen.
+ */
+ if (powerdown)
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+}
+
+static void snd_cs46xx_proc_start(cs46xx_t *chip)
+{
+ int cnt;
+
+ /*
+ * Set the frame timer to reflect the number of cycles per frame.
+ */
+ snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
+ /*
+ * Turn on the run, run at frame, and DMA enable bits in the local copy of
+ * the SP control register.
+ */
+ snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
+ /*
+ * Wait until the run at frame bit resets itself in the SP control
+ * register.
+ */
+ for (cnt = 0; cnt < 25; cnt++) {
+ udelay(50);
+ if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR))
+ break;
+ }
+
+ if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)
+ snd_printk("SPCR_RUNFR never reset\n");
+}
+
+static void snd_cs46xx_proc_stop(cs46xx_t *chip)
+{
+ /*
+ * Turn off the run, run at frame, and DMA enable bits in the local copy of
+ * the SP control register.
+ */
+ snd_cs46xx_poke(chip, BA1_SPCR, 0);
+}
+
+/*
+ * Sample rate routines
+ */
+
+#define GOF_PER_SEC 200
+
+static void snd_cs46xx_set_play_sample_rate(cs46xx_t *chip, unsigned int rate)
+{
+ unsigned long flags;
+ unsigned int tmp1, tmp2;
+ unsigned int phiIncr;
+ unsigned int correctionPerGOF, correctionPerSec;
+
+ /*
+ * Compute the values used to drive the actual sample rate conversion.
+ * The following formulas are being computed, using inline assembly
+ * since we need to use 64 bit arithmetic to compute the values:
+ *
+ * phiIncr = floor((Fs,in * 2^26) / Fs,out)
+ * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+ * GOF_PER_SEC)
+ * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
+ * GOF_PER_SEC * correctionPerGOF
+ *
+ * i.e.
+ *
+ * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
+ * correctionPerGOF:correctionPerSec =
+ * dividend:remainder(ulOther / GOF_PER_SEC)
+ */
+ tmp1 = rate << 16;
+ phiIncr = tmp1 / 48000;
+ tmp1 -= phiIncr * 48000;
+ tmp1 <<= 10;
+ phiIncr <<= 10;
+ tmp2 = tmp1 / 48000;
+ phiIncr += tmp2;
+ tmp1 -= tmp2 * 48000;
+ correctionPerGOF = tmp1 / GOF_PER_SEC;
+ tmp1 -= correctionPerGOF * GOF_PER_SEC;
+ correctionPerSec = tmp1;
+
+ /*
+ * Fill in the SampleRateConverter control block.
+ */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_cs46xx_poke(chip, BA1_PSRC,
+ ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+ snd_cs46xx_poke(chip, BA1_PPI, phiIncr);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs46xx_set_capture_sample_rate(cs46xx_t *chip, unsigned int rate)
+{
+ unsigned long flags;
+ unsigned int phiIncr, coeffIncr, tmp1, tmp2;
+ unsigned int correctionPerGOF, correctionPerSec, initialDelay;
+ unsigned int frameGroupLength, cnt;
+
+ /*
+ * We can only decimate by up to a factor of 1/9th the hardware rate.
+ * Correct the value if an attempt is made to stray outside that limit.
+ */
+ if ((rate * 9) < 48000)
+ rate = 48000 / 9;
+
+ /*
+ * We can not capture at at rate greater than the Input Rate (48000).
+ * Return an error if an attempt is made to stray outside that limit.
+ */
+ if (rate > 48000)
+ rate = 48000;
+
+ /*
+ * Compute the values used to drive the actual sample rate conversion.
+ * The following formulas are being computed, using inline assembly
+ * since we need to use 64 bit arithmetic to compute the values:
+ *
+ * coeffIncr = -floor((Fs,out * 2^23) / Fs,in)
+ * phiIncr = floor((Fs,in * 2^26) / Fs,out)
+ * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+ * GOF_PER_SEC)
+ * correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
+ * GOF_PER_SEC * correctionPerGOF
+ * initialDelay = ceil((24 * Fs,in) / Fs,out)
+ *
+ * i.e.
+ *
+ * coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
+ * phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
+ * correctionPerGOF:correctionPerSec =
+ * dividend:remainder(ulOther / GOF_PER_SEC)
+ * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
+ */
+
+ tmp1 = rate << 16;
+ coeffIncr = tmp1 / 48000;
+ tmp1 -= coeffIncr * 48000;
+ tmp1 <<= 7;
+ coeffIncr <<= 7;
+ coeffIncr += tmp1 / 48000;
+ coeffIncr ^= 0xFFFFFFFF;
+ coeffIncr++;
+ tmp1 = 48000 << 16;
+ phiIncr = tmp1 / rate;
+ tmp1 -= phiIncr * rate;
+ tmp1 <<= 10;
+ phiIncr <<= 10;
+ tmp2 = tmp1 / rate;
+ phiIncr += tmp2;
+ tmp1 -= tmp2 * rate;
+ correctionPerGOF = tmp1 / GOF_PER_SEC;
+ tmp1 -= correctionPerGOF * GOF_PER_SEC;
+ correctionPerSec = tmp1;
+ initialDelay = ((48000 * 24) + rate - 1) / rate;
+
+ /*
+ * Fill in the VariDecimate control block.
+ */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_cs46xx_poke(chip, BA1_CSRC,
+ ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+ snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
+ snd_cs46xx_poke(chip, BA1_CD,
+ (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
+ snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ /*
+ * Figure out the frame group length for the write back task. Basically,
+ * this is just the factors of 24000 (2^6*3*5^3) that are not present in
+ * the output sample rate.
+ */
+ frameGroupLength = 1;
+ for (cnt = 2; cnt <= 64; cnt *= 2) {
+ if (((rate / cnt) * cnt) != rate)
+ frameGroupLength *= 2;
+ }
+ if (((rate / 3) * 3) != rate) {
+ frameGroupLength *= 3;
+ }
+ for (cnt = 5; cnt <= 125; cnt *= 5) {
+ if (((rate / cnt) * cnt) != rate)
+ frameGroupLength *= 5;
+ }
+
+ /*
+ * Fill in the WriteBack control block.
+ */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength);
+ snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength));
+ snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF);
+ snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000));
+ snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ * PCM part
+ */
+
+static void snd_cs46xx_pb_trans_copy(snd_pcm_substream_t *substream,
+ snd_pcm_indirect_t *rec, size_t bytes)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs46xx_pcm_t * cpcm = runtime->private_data;
+ memcpy(cpcm->hw_buf.area + rec->hw_data, runtime->dma_area + rec->sw_data, bytes);
+}
+
+static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs46xx_pcm_t * cpcm = runtime->private_data;
+ snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, snd_cs46xx_pb_trans_copy);
+ return 0;
+}
+
+static void snd_cs46xx_cp_trans_copy(snd_pcm_substream_t *substream,
+ snd_pcm_indirect_t *rec, size_t bytes)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ memcpy(runtime->dma_area + rec->sw_data,
+ chip->capt.hw_buf.area + rec->hw_data, bytes);
+}
+
+static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, snd_cs46xx_cp_trans_copy);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+ cs46xx_pcm_t *cpcm = substream->runtime->private_data;
+ snd_assert (cpcm->pcm_channel,return -ENXIO);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
+#else
+ ptr = snd_cs46xx_peek(chip, BA1_PBA);
+#endif
+ ptr -= cpcm->hw_buf.addr;
+ return ptr >> cpcm->shift;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+ cs46xx_pcm_t *cpcm = substream->runtime->private_data;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_assert (cpcm->pcm_channel,return -ENXIO);
+ ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
+#else
+ ptr = snd_cs46xx_peek(chip, BA1_PBA);
+#endif
+ ptr -= cpcm->hw_buf.addr;
+ return snd_pcm_indirect_playback_pointer(substream, &cpcm->pcm_rec, ptr);
+}
+
+static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;
+ return ptr >> chip->capt.shift;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;
+ return snd_pcm_indirect_capture_pointer(substream, &chip->capt.pcm_rec, ptr);
+}
+
+static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ /*snd_pcm_runtime_t *runtime = substream->runtime;*/
+ int result = 0;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ cs46xx_pcm_t *cpcm = substream->runtime->private_data;
+ if (! cpcm->pcm_channel) {
+ return -ENXIO;
+ }
+#endif
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ /* magic value to unmute PCM stream playback volume */
+ snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address +
+ SCBVolumeCtrl) << 2, 0x80008000);
+
+ if (cpcm->pcm_channel->unlinked)
+ cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel);
+
+ if (substream->runtime->periods != CS46XX_FRAGS)
+ snd_cs46xx_playback_transfer(substream);
+#else
+ spin_lock(&chip->reg_lock);
+ if (substream->runtime->periods != CS46XX_FRAGS)
+ snd_cs46xx_playback_transfer(substream);
+ { unsigned int tmp;
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ tmp &= 0x0000ffff;
+ snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);
+ }
+ spin_unlock(&chip->reg_lock);
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ /* magic mute channel */
+ snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address +
+ SCBVolumeCtrl) << 2, 0xffffffff);
+
+ if (!cpcm->pcm_channel->unlinked)
+ cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
+#else
+ spin_lock(&chip->reg_lock);
+ { unsigned int tmp;
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ tmp &= 0x0000ffff;
+ snd_cs46xx_poke(chip, BA1_PCTL, tmp);
+ }
+ spin_unlock(&chip->reg_lock);
+#endif
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+ return result;
+}
+
+static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int tmp;
+ int result = 0;
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+ tmp &= 0xffff0000;
+ snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+ tmp &= 0xffff0000;
+ snd_cs46xx_poke(chip, BA1_CCTL, tmp);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ spin_unlock(&chip->reg_lock);
+
+ return result;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static int _cs46xx_adjust_sample_rate (cs46xx_t *chip, cs46xx_pcm_t *cpcm,
+ int sample_rate)
+{
+
+ /* If PCMReaderSCB and SrcTaskSCB not created yet ... */
+ if ( cpcm->pcm_channel == NULL) {
+ cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate,
+ cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id);
+ if (cpcm->pcm_channel == NULL) {
+ snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
+ return -ENOMEM;
+ }
+ cpcm->pcm_channel->sample_rate = sample_rate;
+ } else
+ /* if sample rate is changed */
+ if ((int)cpcm->pcm_channel->sample_rate != sample_rate) {
+ int unlinked = cpcm->pcm_channel->unlinked;
+ cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
+
+ if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm,
+ cpcm->hw_buf.addr,
+ cpcm->pcm_channel_id)) == NULL) {
+ snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
+ return -ENOMEM;
+ }
+
+ if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
+ cpcm->pcm_channel->sample_rate = sample_rate;
+ }
+
+ return 0;
+}
+#endif
+
+
+static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs46xx_pcm_t *cpcm;
+ int err;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ int sample_rate = params_rate(hw_params);
+ int period_size = params_period_bytes(hw_params);
+#endif
+ cpcm = runtime->private_data;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_assert (sample_rate != 0, return -ENXIO);
+
+ down (&chip->spos_mutex);
+
+ if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) {
+ up (&chip->spos_mutex);
+ return -ENXIO;
+ }
+
+ snd_assert (cpcm->pcm_channel != NULL);
+ if (!cpcm->pcm_channel) {
+ up (&chip->spos_mutex);
+ return -ENXIO;
+ }
+
+
+ if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) {
+ up (&chip->spos_mutex);
+ return -EINVAL;
+ }
+
+ snd_printdd ("period_size (%d), periods (%d) buffer_size(%d)\n",
+ period_size, params_periods(hw_params),
+ params_buffer_bytes(hw_params));
+#endif
+
+ if (params_periods(hw_params) == CS46XX_FRAGS) {
+ if (runtime->dma_area != cpcm->hw_buf.area)
+ snd_pcm_lib_free_pages(substream);
+ runtime->dma_area = cpcm->hw_buf.area;
+ runtime->dma_addr = cpcm->hw_buf.addr;
+ runtime->dma_bytes = cpcm->hw_buf.bytes;
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_ops;
+ } else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_rear_ops;
+ } else if (cpcm->pcm_channel_id == DSP_PCM_CENTER_LFE_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_clfe_ops;
+ } else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_iec958_ops;
+ } else {
+ snd_assert(0);
+ }
+#else
+ substream->ops = &snd_cs46xx_playback_ops;
+#endif
+
+ } else {
+ if (runtime->dma_area == cpcm->hw_buf.area) {
+ runtime->dma_area = NULL;
+ runtime->dma_addr = 0;
+ runtime->dma_bytes = 0;
+ }
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) {
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ up (&chip->spos_mutex);
+#endif
+ return err;
+ }
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_indirect_ops;
+ } else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_indirect_rear_ops;
+ } else if (cpcm->pcm_channel_id == DSP_PCM_CENTER_LFE_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_indirect_clfe_ops;
+ } else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
+ substream->ops = &snd_cs46xx_playback_indirect_iec958_ops;
+ } else {
+ snd_assert(0);
+ }
+#else
+ substream->ops = &snd_cs46xx_playback_indirect_ops;
+#endif
+
+ }
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ up (&chip->spos_mutex);
+#endif
+
+ return 0;
+}
+
+static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream)
+{
+ /*cs46xx_t *chip = snd_pcm_substream_chip(substream);*/
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs46xx_pcm_t *cpcm;
+
+ cpcm = runtime->private_data;
+
+ /* if play_back open fails, then this function
+ is called and cpcm can actually be NULL here */
+ if (!cpcm) return -ENXIO;
+
+ if (runtime->dma_area != cpcm->hw_buf.area)
+ snd_pcm_lib_free_pages(substream);
+
+ runtime->dma_area = NULL;
+ runtime->dma_addr = 0;
+ runtime->dma_bytes = 0;
+
+ return 0;
+}
+
+static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream)
+{
+ unsigned int tmp;
+ unsigned int pfie;
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs46xx_pcm_t *cpcm;
+
+ cpcm = runtime->private_data;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_assert (cpcm->pcm_channel != NULL, return -ENXIO);
+
+ pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
+ pfie &= ~0x0000f03f;
+#else
+ /* old dsp */
+ pfie = snd_cs46xx_peek(chip, BA1_PFIE);
+ pfie &= ~0x0000f03f;
+#endif
+
+ cpcm->shift = 2;
+ /* if to convert from stereo to mono */
+ if (runtime->channels == 1) {
+ cpcm->shift--;
+ pfie |= 0x00002000;
+ }
+ /* if to convert from 8 bit to 16 bit */
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ cpcm->shift--;
+ pfie |= 0x00001000;
+ }
+ /* if to convert to unsigned */
+ if (snd_pcm_format_unsigned(runtime->format))
+ pfie |= 0x00008000;
+
+ /* Never convert byte order when sample stream is 8 bit */
+ if (snd_pcm_format_width(runtime->format) != 8) {
+ /* convert from big endian to little endian */
+ if (snd_pcm_format_big_endian(runtime->format))
+ pfie |= 0x00004000;
+ }
+
+ memset(&cpcm->pcm_rec, 0, sizeof(cpcm->pcm_rec));
+ cpcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ cpcm->pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+ tmp = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2);
+ tmp &= ~0x000003ff;
+ tmp |= (4 << cpcm->shift) - 1;
+ /* playback transaction count register */
+ snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2, tmp);
+
+ /* playback format && interrupt enable */
+ snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2, pfie | cpcm->pcm_channel->pcm_slot);
+#else
+ snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_buf.addr);
+ tmp = snd_cs46xx_peek(chip, BA1_PDTC);
+ tmp &= ~0x000003ff;
+ tmp |= (4 << cpcm->shift) - 1;
+ snd_cs46xx_poke(chip, BA1_PDTC, tmp);
+ snd_cs46xx_poke(chip, BA1_PFIE, pfie);
+ snd_cs46xx_set_play_sample_rate(chip, runtime->rate);
+#endif
+
+ return 0;
+}
+
+static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ cs46xx_dsp_pcm_ostream_set_period (chip, params_period_bytes(hw_params));
+#endif
+ if (runtime->periods == CS46XX_FRAGS) {
+ if (runtime->dma_area != chip->capt.hw_buf.area)
+ snd_pcm_lib_free_pages(substream);
+ runtime->dma_area = chip->capt.hw_buf.area;
+ runtime->dma_addr = chip->capt.hw_buf.addr;
+ runtime->dma_bytes = chip->capt.hw_buf.bytes;
+ substream->ops = &snd_cs46xx_capture_ops;
+ } else {
+ if (runtime->dma_area == chip->capt.hw_buf.area) {
+ runtime->dma_area = NULL;
+ runtime->dma_addr = 0;
+ runtime->dma_bytes = 0;
+ }
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ substream->ops = &snd_cs46xx_capture_indirect_ops;
+ }
+
+ return 0;
+}
+
+static int snd_cs46xx_capture_hw_free(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (runtime->dma_area != chip->capt.hw_buf.area)
+ snd_pcm_lib_free_pages(substream);
+ runtime->dma_area = NULL;
+ runtime->dma_addr = 0;
+ runtime->dma_bytes = 0;
+
+ return 0;
+}
+
+static int snd_cs46xx_capture_prepare(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_buf.addr);
+ chip->capt.shift = 2;
+ memset(&chip->capt.pcm_rec, 0, sizeof(chip->capt.pcm_rec));
+ chip->capt.pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ chip->capt.pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << 2;
+ snd_cs46xx_set_capture_sample_rate(chip, runtime->rate);
+
+ return 0;
+}
+
+static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ cs46xx_t *chip = dev_id;
+ u32 status1;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ u32 status2;
+ int i;
+ cs46xx_pcm_t *cpcm = NULL;
+#endif
+
+ /*
+ * Read the Interrupt Status Register to clear the interrupt
+ */
+ status1 = snd_cs46xx_peekBA0(chip, BA0_HISR);
+ if ((status1 & 0x7fffffff) == 0) {
+ snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
+ return IRQ_NONE;
+ }
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0);
+
+ for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) {
+ if (i <= 15) {
+ if ( status1 & (1 << i) ) {
+ if (i == CS46XX_DSP_CAPTURE_CHANNEL) {
+ if (chip->capt.substream)
+ snd_pcm_period_elapsed(chip->capt.substream);
+ } else {
+ if (ins->pcm_channels[i].active &&
+ ins->pcm_channels[i].private_data &&
+ !ins->pcm_channels[i].unlinked) {
+ cpcm = ins->pcm_channels[i].private_data;
+ snd_pcm_period_elapsed(cpcm->substream);
+ }
+ }
+ }
+ } else {
+ if ( status2 & (1 << (i - 16))) {
+ if (ins->pcm_channels[i].active &&
+ ins->pcm_channels[i].private_data &&
+ !ins->pcm_channels[i].unlinked) {
+ cpcm = ins->pcm_channels[i].private_data;
+ snd_pcm_period_elapsed(cpcm->substream);
+ }
+ }
+ }
+ }
+
+#else
+ /* old dsp */
+ if ((status1 & HISR_VC0) && chip->playback_pcm) {
+ if (chip->playback_pcm->substream)
+ snd_pcm_period_elapsed(chip->playback_pcm->substream);
+ }
+ if ((status1 & HISR_VC1) && chip->pcm) {
+ if (chip->capt.substream)
+ snd_pcm_period_elapsed(chip->capt.substream);
+ }
+#endif
+
+ if ((status1 & HISR_MIDI) && chip->rmidi) {
+ unsigned char c;
+
+ spin_lock(&chip->reg_lock);
+ while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) {
+ c = snd_cs46xx_peekBA0(chip, BA0_MIDRP);
+ if ((chip->midcr & MIDCR_RIE) == 0)
+ continue;
+ snd_rawmidi_receive(chip->midi_input, &c, 1);
+ }
+ while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
+ if ((chip->midcr & MIDCR_TIE) == 0)
+ break;
+ if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) {
+ chip->midcr &= ~MIDCR_TIE;
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ break;
+ }
+ snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c);
+ }
+ spin_unlock(&chip->reg_lock);
+ }
+ /*
+ * EOI to the PCI part....reenables interrupts
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
+
+ return IRQ_HANDLED;
+}
+
+static snd_pcm_hardware_t snd_cs46xx_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5500,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (256 * 1024),
+ .period_bytes_min = CS46XX_MIN_PERIOD_SIZE,
+ .period_bytes_max = CS46XX_MAX_PERIOD_SIZE,
+ .periods_min = CS46XX_FRAGS,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_cs46xx_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5500,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (256 * 1024),
+ .period_bytes_min = CS46XX_MIN_PERIOD_SIZE,
+ .period_bytes_max = CS46XX_MAX_PERIOD_SIZE,
+ .periods_min = CS46XX_FRAGS,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 };
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
+ .count = ARRAY_SIZE(period_sizes),
+ .list = period_sizes,
+ .mask = 0
+};
+
+#endif
+
+static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ cs46xx_pcm_t * cpcm = runtime->private_data;
+ kfree(cpcm);
+}
+
+static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pcm_channel_id)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ cs46xx_pcm_t * cpcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ cpcm = kcalloc(1, sizeof(*cpcm), GFP_KERNEL);
+ if (cpcm == NULL)
+ return -ENOMEM;
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &cpcm->hw_buf) < 0) {
+ kfree(cpcm);
+ return -ENOMEM;
+ }
+
+ runtime->hw = snd_cs46xx_playback;
+ runtime->private_data = cpcm;
+ runtime->private_free = snd_cs46xx_pcm_free_substream;
+
+ cpcm->substream = substream;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ down (&chip->spos_mutex);
+ cpcm->pcm_channel = NULL;
+ cpcm->pcm_channel_id = pcm_channel_id;
+
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ &hw_constraints_period_sizes);
+
+ up (&chip->spos_mutex);
+#else
+ chip->playback_pcm = cpcm; /* HACK */
+#endif
+
+ if (chip->accept_valid)
+ substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+ chip->active_ctrl(chip, 1);
+
+ return 0;
+}
+
+static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
+{
+ snd_printdd("open front channel\n");
+ return _cs46xx_playback_open_channel(substream,DSP_PCM_MAIN_CHANNEL);
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static int snd_cs46xx_playback_open_rear(snd_pcm_substream_t * substream)
+{
+ snd_printdd("open rear channel\n");
+
+ return _cs46xx_playback_open_channel(substream,DSP_PCM_REAR_CHANNEL);
+}
+
+static int snd_cs46xx_playback_open_clfe(snd_pcm_substream_t * substream)
+{
+ snd_printdd("open center - LFE channel\n");
+
+ return _cs46xx_playback_open_channel(substream,DSP_PCM_CENTER_LFE_CHANNEL);
+}
+
+static int snd_cs46xx_playback_open_iec958(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_printdd("open raw iec958 channel\n");
+
+ down (&chip->spos_mutex);
+ cs46xx_iec958_pre_open (chip);
+ up (&chip->spos_mutex);
+
+ return _cs46xx_playback_open_channel(substream,DSP_IEC958_CHANNEL);
+}
+
+static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream);
+
+static int snd_cs46xx_playback_close_iec958(snd_pcm_substream_t * substream)
+{
+ int err;
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_printdd("close raw iec958 channel\n");
+
+ err = snd_cs46xx_playback_close(substream);
+
+ down (&chip->spos_mutex);
+ cs46xx_iec958_post_close (chip);
+ up (&chip->spos_mutex);
+
+ return err;
+}
+#endif
+
+static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->capt.hw_buf) < 0)
+ return -ENOMEM;
+ chip->capt.substream = substream;
+ substream->runtime->hw = snd_cs46xx_capture;
+
+ if (chip->accept_valid)
+ substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+
+ chip->active_ctrl(chip, 1);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ &hw_constraints_period_sizes);
+#endif
+ return 0;
+}
+
+static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ cs46xx_pcm_t * cpcm;
+
+ cpcm = runtime->private_data;
+
+ /* when playback_open fails, then cpcm can be NULL */
+ if (!cpcm) return -ENXIO;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ down (&chip->spos_mutex);
+ if (cpcm->pcm_channel) {
+ cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
+ cpcm->pcm_channel = NULL;
+ }
+ up (&chip->spos_mutex);
+#else
+ chip->playback_pcm = NULL;
+#endif
+
+ cpcm->substream = NULL;
+ snd_dma_free_pages(&cpcm->hw_buf);
+ chip->active_ctrl(chip, -1);
+
+ return 0;
+}
+
+static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream)
+{
+ cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->capt.substream = NULL;
+ snd_dma_free_pages(&chip->capt.hw_buf);
+ chip->active_ctrl(chip, -1);
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static snd_pcm_ops_t snd_cs46xx_playback_rear_ops = {
+ .open = snd_cs46xx_playback_open_rear,
+ .close = snd_cs46xx_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_direct_pointer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops = {
+ .open = snd_cs46xx_playback_open_rear,
+ .close = snd_cs46xx_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_indirect_pointer,
+ .ack = snd_cs46xx_playback_transfer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_playback_clfe_ops = {
+ .open = snd_cs46xx_playback_open_clfe,
+ .close = snd_cs46xx_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_direct_pointer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_clfe_ops = {
+ .open = snd_cs46xx_playback_open_clfe,
+ .close = snd_cs46xx_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_indirect_pointer,
+ .ack = snd_cs46xx_playback_transfer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_playback_iec958_ops = {
+ .open = snd_cs46xx_playback_open_iec958,
+ .close = snd_cs46xx_playback_close_iec958,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_direct_pointer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops = {
+ .open = snd_cs46xx_playback_open_iec958,
+ .close = snd_cs46xx_playback_close_iec958,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_indirect_pointer,
+ .ack = snd_cs46xx_playback_transfer,
+};
+
+#endif
+
+static snd_pcm_ops_t snd_cs46xx_playback_ops = {
+ .open = snd_cs46xx_playback_open,
+ .close = snd_cs46xx_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_direct_pointer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = {
+ .open = snd_cs46xx_playback_open,
+ .close = snd_cs46xx_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_playback_hw_params,
+ .hw_free = snd_cs46xx_playback_hw_free,
+ .prepare = snd_cs46xx_playback_prepare,
+ .trigger = snd_cs46xx_playback_trigger,
+ .pointer = snd_cs46xx_playback_indirect_pointer,
+ .ack = snd_cs46xx_playback_transfer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_capture_ops = {
+ .open = snd_cs46xx_capture_open,
+ .close = snd_cs46xx_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_capture_hw_params,
+ .hw_free = snd_cs46xx_capture_hw_free,
+ .prepare = snd_cs46xx_capture_prepare,
+ .trigger = snd_cs46xx_capture_trigger,
+ .pointer = snd_cs46xx_capture_direct_pointer,
+};
+
+static snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = {
+ .open = snd_cs46xx_capture_open,
+ .close = snd_cs46xx_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cs46xx_capture_hw_params,
+ .hw_free = snd_cs46xx_capture_hw_free,
+ .prepare = snd_cs46xx_capture_prepare,
+ .trigger = snd_cs46xx_capture_trigger,
+ .pointer = snd_cs46xx_capture_indirect_pointer,
+ .ack = snd_cs46xx_capture_transfer,
+};
+
+static void snd_cs46xx_pcm_free(snd_pcm_t *pcm)
+{
+ cs46xx_t *chip = pcm->private_data;
+ chip->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static void snd_cs46xx_pcm_rear_free(snd_pcm_t *pcm)
+{
+ cs46xx_t *chip = pcm->private_data;
+ chip->pcm_rear = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_cs46xx_pcm_center_lfe_free(snd_pcm_t *pcm)
+{
+ cs46xx_t *chip = pcm->private_data;
+ chip->pcm_center_lfe = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_cs46xx_pcm_iec958_free(snd_pcm_t *pcm)
+{
+ cs46xx_t *chip = pcm->private_data;
+ chip->pcm_iec958 = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+#define MAX_PLAYBACK_CHANNELS (DSP_MAX_PCM_CHANNELS - 1)
+#else
+#define MAX_PLAYBACK_CHANNELS 1
+#endif
+
+int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_cs46xx_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs46xx_capture_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "CS46xx");
+ chip->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+int __devinit snd_cs46xx_pcm_rear(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+
+ if ((err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_cs46xx_pcm_rear_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_rear_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "CS46xx - Rear");
+ chip->pcm_rear = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+int __devinit snd_cs46xx_pcm_center_lfe(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+
+ if ((err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_cs46xx_pcm_center_lfe_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_clfe_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "CS46xx - Center LFE");
+ chip->pcm_center_lfe = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+int __devinit snd_cs46xx_pcm_iec958(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+
+ if ((err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_cs46xx_pcm_iec958_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_iec958_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "CS46xx - IEC958");
+ chip->pcm_rear = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+#endif
+
+/*
+ * Mixer routines
+ */
+static void snd_cs46xx_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ cs46xx_t *chip = bus->private_data;
+
+ chip->ac97_bus = NULL;
+}
+
+static void snd_cs46xx_mixer_free_ac97(ac97_t *ac97)
+{
+ cs46xx_t *chip = ac97->private_data;
+
+ snd_assert ((ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) ||
+ (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]),
+ return);
+
+ if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) {
+ chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL;
+ chip->eapd_switch = NULL;
+ }
+ else
+ chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = NULL;
+}
+
+static int snd_cs46xx_vol_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x7fff;
+ return 0;
+}
+
+static int snd_cs46xx_vol_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value;
+ unsigned int val = snd_cs46xx_peek(chip, reg);
+ ucontrol->value.integer.value[0] = 0xffff - (val >> 16);
+ ucontrol->value.integer.value[1] = 0xffff - (val & 0xffff);
+ return 0;
+}
+
+static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value;
+ unsigned int val = ((0xffff - ucontrol->value.integer.value[0]) << 16 |
+ (0xffff - ucontrol->value.integer.value[1]));
+ unsigned int old = snd_cs46xx_peek(chip, reg);
+ int change = (old != val);
+
+ if (change) {
+ snd_cs46xx_poke(chip, reg, val);
+ }
+
+ return change;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+static int snd_cs46xx_vol_dac_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = chip->dsp_spos_instance->dac_volume_left;
+ ucontrol->value.integer.value[1] = chip->dsp_spos_instance->dac_volume_right;
+
+ return 0;
+}
+
+static int snd_cs46xx_vol_dac_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+
+ if (chip->dsp_spos_instance->dac_volume_right != ucontrol->value.integer.value[0] ||
+ chip->dsp_spos_instance->dac_volume_left != ucontrol->value.integer.value[1]) {
+ cs46xx_dsp_set_dac_volume(chip,
+ ucontrol->value.integer.value[0],
+ ucontrol->value.integer.value[1]);
+ change = 1;
+ }
+
+ return change;
+}
+
+#if 0
+static int snd_cs46xx_vol_iec958_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_input_volume_left;
+ ucontrol->value.integer.value[1] = chip->dsp_spos_instance->spdif_input_volume_right;
+ return 0;
+}
+
+static int snd_cs46xx_vol_iec958_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+
+ if (chip->dsp_spos_instance->spdif_input_volume_left != ucontrol->value.integer.value[0] ||
+ chip->dsp_spos_instance->spdif_input_volume_right!= ucontrol->value.integer.value[1]) {
+ cs46xx_dsp_set_iec958_volume (chip,
+ ucontrol->value.integer.value[0],
+ ucontrol->value.integer.value[1]);
+ change = 1;
+ }
+
+ return change;
+}
+#endif
+
+static int snd_mixer_boolean_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_cs46xx_iec958_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value;
+
+ if (reg == CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT)
+ ucontrol->value.integer.value[0] = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
+ else
+ ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in;
+
+ return 0;
+}
+
+static int snd_cs46xx_iec958_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int change, res;
+
+ switch (kcontrol->private_value) {
+ case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT:
+ down (&chip->spos_mutex);
+ change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
+ if (ucontrol->value.integer.value[0] && !change)
+ cs46xx_dsp_enable_spdif_out(chip);
+ else if (change && !ucontrol->value.integer.value[0])
+ cs46xx_dsp_disable_spdif_out(chip);
+
+ res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
+ up (&chip->spos_mutex);
+ break;
+ case CS46XX_MIXER_SPDIF_INPUT_ELEMENT:
+ change = chip->dsp_spos_instance->spdif_status_in;
+ if (ucontrol->value.integer.value[0] && !change) {
+ cs46xx_dsp_enable_spdif_in(chip);
+ /* restore volume */
+ }
+ else if (change && !ucontrol->value.integer.value[0])
+ cs46xx_dsp_disable_spdif_in(chip);
+
+ res = (change != chip->dsp_spos_instance->spdif_status_in);
+ break;
+ default:
+ res = -EINVAL;
+ snd_assert(0, (void)0);
+ }
+
+ return res;
+}
+
+static int snd_cs46xx_adc_capture_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ if (ins->adc_input != NULL)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int snd_cs46xx_adc_capture_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int change = 0;
+
+ if (ucontrol->value.integer.value[0] && !ins->adc_input) {
+ cs46xx_dsp_enable_adc_capture(chip);
+ change = 1;
+ } else if (!ucontrol->value.integer.value[0] && ins->adc_input) {
+ cs46xx_dsp_disable_adc_capture(chip);
+ change = 1;
+ }
+ return change;
+}
+
+static int snd_cs46xx_pcm_capture_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ if (ins->pcm_input != NULL)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+
+static int snd_cs46xx_pcm_capture_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int change = 0;
+
+ if (ucontrol->value.integer.value[0] && !ins->pcm_input) {
+ cs46xx_dsp_enable_pcm_capture(chip);
+ change = 1;
+ } else if (!ucontrol->value.integer.value[0] && ins->pcm_input) {
+ cs46xx_dsp_disable_pcm_capture(chip);
+ change = 1;
+ }
+
+ return change;
+}
+
+static int snd_herc_spdif_select_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+
+ int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+
+ if (val1 & EGPIODR_GPOE0)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+/*
+ * Game Theatre XP card - EGPIO[0] is used to select SPDIF input optical or coaxial.
+ */
+static int snd_herc_spdif_select_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+ int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
+
+ if (ucontrol->value.integer.value[0]) {
+ /* optical is default */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,
+ EGPIODR_GPOE0 | val1); /* enable EGPIO0 output */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR,
+ EGPIOPTR_GPPT0 | val2); /* open-drain on output */
+ } else {
+ /* coaxial */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, val1 & ~EGPIODR_GPOE0); /* disable */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT0); /* disable */
+ }
+
+ /* checking diff from the EGPIO direction register
+ should be enough */
+ return (val1 != (int)snd_cs46xx_peekBA0(chip, BA0_EGPIODR));
+}
+
+
+static int snd_cs46xx_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_cs46xx_spdif_default_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ down (&chip->spos_mutex);
+ ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_default >> 24) & 0xff);
+ ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_default >> 16) & 0xff);
+ ucontrol->value.iec958.status[2] = 0;
+ ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_default) & 0xff);
+ up (&chip->spos_mutex);
+
+ return 0;
+}
+
+static int snd_cs46xx_spdif_default_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t * chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ unsigned int val;
+ int change;
+
+ down (&chip->spos_mutex);
+ val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
+ ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[2]) << 16) |
+ ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
+ /* left and right validity bit */
+ (1 << 13) | (1 << 12);
+
+
+ change = (unsigned int)ins->spdif_csuv_default != val;
+ ins->spdif_csuv_default = val;
+
+ if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) )
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
+
+ up (&chip->spos_mutex);
+
+ return change;
+}
+
+static int snd_cs46xx_spdif_mask_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = 0xff;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0x00;
+ ucontrol->value.iec958.status[3] = 0xff;
+ return 0;
+}
+
+static int snd_cs46xx_spdif_stream_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ down (&chip->spos_mutex);
+ ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_stream >> 24) & 0xff);
+ ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_stream >> 16) & 0xff);
+ ucontrol->value.iec958.status[2] = 0;
+ ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_stream) & 0xff);
+ up (&chip->spos_mutex);
+
+ return 0;
+}
+
+static int snd_cs46xx_spdif_stream_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ cs46xx_t * chip = snd_kcontrol_chip(kcontrol);
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ unsigned int val;
+ int change;
+
+ down (&chip->spos_mutex);
+ val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
+ ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[1]) << 16) |
+ ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
+ /* left and right validity bit */
+ (1 << 13) | (1 << 12);
+
+
+ change = ins->spdif_csuv_stream != val;
+ ins->spdif_csuv_stream = val;
+
+ if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN )
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
+
+ up (&chip->spos_mutex);
+
+ return change;
+}
+
+#endif /* CONFIG_SND_CS46XX_NEW_DSP */
+
+
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+static int snd_cs46xx_egpio_select_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 8;
+ return 0;
+}
+
+static int snd_cs46xx_egpio_select_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = chip->current_gpio;
+
+ return 0;
+}
+
+static int snd_cs46xx_egpio_select_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int change = (chip->current_gpio != ucontrol->value.integer.value[0]);
+ chip->current_gpio = ucontrol->value.integer.value[0];
+
+ return change;
+}
+
+
+static int snd_cs46xx_egpio_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value;
+
+ snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+ ucontrol->value.integer.value[0] =
+ (snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0;
+
+ return 0;
+}
+
+static int snd_cs46xx_egpio_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value;
+ int val = snd_cs46xx_peekBA0(chip, reg);
+ int oldval = val;
+ snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+
+ if (ucontrol->value.integer.value[0])
+ val |= (1 << chip->current_gpio);
+ else
+ val &= ~(1 << chip->current_gpio);
+
+ snd_cs46xx_pokeBA0(chip, reg,val);
+ snd_printdd ("put: val %08x oldval %08x\n",val,oldval);
+
+ return (oldval != val);
+}
+#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */
+
+static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Volume",
+ .info = snd_cs46xx_vol_info,
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+ .get = snd_cs46xx_vol_get,
+ .put = snd_cs46xx_vol_put,
+ .private_value = BA1_PVOL,
+#else
+ .get = snd_cs46xx_vol_dac_get,
+ .put = snd_cs46xx_vol_dac_put,
+#endif
+},
+
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Volume",
+ .info = snd_cs46xx_vol_info,
+ .get = snd_cs46xx_vol_get,
+ .put = snd_cs46xx_vol_put,
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+ .private_value = BA1_CVOL,
+#else
+ .private_value = (VARIDECIMATE_SCB_ADDR + 0xE) << 2,
+#endif
+},
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Capture Switch",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_adc_capture_get,
+ .put = snd_cs46xx_adc_capture_put
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Capture Switch",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_pcm_capture_get,
+ .put = snd_cs46xx_pcm_capture_put
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Output Switch",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_iec958_get,
+ .put = snd_cs46xx_iec958_put,
+ .private_value = CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Input Switch",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_iec958_get,
+ .put = snd_cs46xx_iec958_put,
+ .private_value = CS46XX_MIXER_SPDIF_INPUT_ELEMENT,
+},
+#if 0
+/* Input IEC958 volume does not work for the moment. (Benny) */
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Input Volume",
+ .info = snd_cs46xx_vol_info,
+ .get = snd_cs46xx_vol_iec958_get,
+ .put = snd_cs46xx_vol_iec958_put,
+ .private_value = (ASYNCRX_SCB_ADDR + 0xE) << 2,
+},
+#endif
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_cs46xx_spdif_info,
+ .get = snd_cs46xx_spdif_default_get,
+ .put = snd_cs46xx_spdif_default_put,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+ .info = snd_cs46xx_spdif_info,
+ .get = snd_cs46xx_spdif_mask_get,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_cs46xx_spdif_info,
+ .get = snd_cs46xx_spdif_stream_get,
+ .put = snd_cs46xx_spdif_stream_put
+},
+
+#endif
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EGPIO select",
+ .info = snd_cs46xx_egpio_select_info,
+ .get = snd_cs46xx_egpio_select_get,
+ .put = snd_cs46xx_egpio_select_put,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EGPIO Input/Output",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_egpio_get,
+ .put = snd_cs46xx_egpio_put,
+ .private_value = BA0_EGPIODR,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EGPIO CMOS/Open drain",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_egpio_get,
+ .put = snd_cs46xx_egpio_put,
+ .private_value = BA0_EGPIOPTR,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EGPIO On/Off",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_egpio_get,
+ .put = snd_cs46xx_egpio_put,
+ .private_value = BA0_EGPIOSR,
+},
+#endif
+};
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+/* set primary cs4294 codec into Extended Audio Mode */
+static int snd_cs46xx_front_dup_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+ val = snd_ac97_read(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX], AC97_CSR_ACMODE);
+ ucontrol->value.integer.value[0] = (val & 0x200) ? 0 : 1;
+ return 0;
+}
+
+static int snd_cs46xx_front_dup_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+ return snd_ac97_update_bits(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
+ AC97_CSR_ACMODE, 0x200,
+ ucontrol->value.integer.value[0] ? 0 : 0x200);
+}
+
+static snd_kcontrol_new_t snd_cs46xx_front_dup_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Duplicate Front",
+ .info = snd_mixer_boolean_info,
+ .get = snd_cs46xx_front_dup_get,
+ .put = snd_cs46xx_front_dup_put,
+};
+#endif
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+/* Only available on the Hercules Game Theater XP soundcard */
+static snd_kcontrol_new_t snd_hercules_controls[] __devinitdata = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Optical/Coaxial SPDIF Input Switch",
+ .info = snd_mixer_boolean_info,
+ .get = snd_herc_spdif_select_get,
+ .put = snd_herc_spdif_select_put,
+},
+};
+
+
+static void snd_cs46xx_codec_reset (ac97_t * ac97)
+{
+ unsigned long end_time;
+ int err;
+
+ /* reset to defaults */
+ snd_ac97_write(ac97, AC97_RESET, 0);
+
+ /* set the desired CODEC mode */
+ if (ac97->num == CS46XX_PRIMARY_CODEC_INDEX) {
+ snd_printdd("cs46xx: CODOEC1 mode %04x\n",0x0);
+ snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x0);
+ } else if (ac97->num == CS46XX_SECONDARY_CODEC_INDEX) {
+ snd_printdd("cs46xx: CODOEC2 mode %04x\n",0x3);
+ snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3);
+ } else {
+ snd_assert(0); /* should never happen ... */
+ }
+
+ udelay(50);
+
+ /* it's necessary to wait awhile until registers are accessible after RESET */
+ /* because the PCM or MASTER volume registers can be modified, */
+ /* the REC_GAIN register is used for tests */
+ end_time = jiffies + HZ;
+ do {
+ unsigned short ext_mid;
+
+ /* use preliminary reads to settle the communication */
+ snd_ac97_read(ac97, AC97_RESET);
+ snd_ac97_read(ac97, AC97_VENDOR_ID1);
+ snd_ac97_read(ac97, AC97_VENDOR_ID2);
+ /* modem? */
+ ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+ if (ext_mid != 0xffff && (ext_mid & 1) != 0)
+ return;
+
+ /* test if we can write to the record gain volume register */
+ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
+ if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
+ return;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/100);
+ } while (time_after_eq(end_time, jiffies));
+
+ snd_printk("CS46xx secondary codec dont respond!\n");
+}
+#endif
+
+static int __devinit cs46xx_detect_codec(cs46xx_t *chip, int codec)
+{
+ int idx, err;
+ ac97_template_t ac97;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_cs46xx_mixer_free_ac97;
+ ac97.num = codec;
+ if (chip->amplifier_ctrl == amp_voyetra)
+ ac97.scaps = AC97_SCAP_INV_EAPD;
+
+ if (codec == CS46XX_SECONDARY_CODEC_INDEX) {
+ snd_cs46xx_codec_write(chip, AC97_RESET, 0, codec);
+ udelay(10);
+ if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) {
+ snd_printdd("snd_cs46xx: seconadry codec not present\n");
+ return -ENXIO;
+ }
+ }
+
+ snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000, codec);
+ for (idx = 0; idx < 100; ++idx) {
+ if (snd_cs46xx_codec_read(chip, AC97_MASTER, codec) == 0x8000) {
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[codec]);
+ return err;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/100);
+ }
+ snd_printdd("snd_cs46xx: codec %d detection timeout\n", codec);
+ return -ENXIO;
+}
+
+int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
+{
+ snd_card_t *card = chip->card;
+ snd_ctl_elem_id_t id;
+ int err;
+ unsigned int idx;
+ static ac97_bus_ops_t ops = {
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ .reset = snd_cs46xx_codec_reset,
+#endif
+ .write = snd_cs46xx_ac97_write,
+ .read = snd_cs46xx_ac97_read,
+ };
+
+ /* detect primary codec */
+ chip->nr_ac97_codecs = 0;
+ snd_printdd("snd_cs46xx: detecting primary codec\n");
+ if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ return err;
+ chip->ac97_bus->private_free = snd_cs46xx_mixer_free_ac97_bus;
+
+ if (cs46xx_detect_codec(chip, CS46XX_PRIMARY_CODEC_INDEX) < 0)
+ return -ENXIO;
+ chip->nr_ac97_codecs = 1;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_printdd("snd_cs46xx: detecting seconadry codec\n");
+ /* try detect a secondary codec */
+ if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX))
+ chip->nr_ac97_codecs = 2;
+#endif /* CONFIG_SND_CS46XX_NEW_DSP */
+
+ /* add cs4630 mixer controls */
+ for (idx = 0; idx < ARRAY_SIZE(snd_cs46xx_controls); idx++) {
+ snd_kcontrol_t *kctl;
+ kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip);
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ return err;
+ }
+
+ /* get EAPD mixer switch (for voyetra hack) */
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, "External Amplifier");
+ chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if (chip->nr_ac97_codecs == 1) {
+ unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
+ if (id2 == 0x592b || id2 == 0x592d) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
+ if (err < 0)
+ return err;
+ snd_ac97_write_cache(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
+ AC97_CSR_ACMODE, 0x200);
+ }
+ }
+ /* do soundcard specific mixer setup */
+ if (chip->mixer_init) {
+ snd_printdd ("calling chip->mixer_init(chip);\n");
+ chip->mixer_init(chip);
+ }
+#endif
+
+ /* turn on amplifier */
+ chip->amplifier_ctrl(chip, 1);
+
+ return 0;
+}
+
+/*
+ * RawMIDI interface
+ */
+
+static void snd_cs46xx_midi_reset(cs46xx_t *chip)
+{
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, MIDCR_MRST);
+ udelay(100);
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+}
+
+static int snd_cs46xx_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+ cs46xx_t *chip = substream->rmidi->private_data;
+
+ chip->active_ctrl(chip, 1);
+ spin_lock_irq(&chip->reg_lock);
+ chip->uartm |= CS46XX_MODE_INPUT;
+ chip->midcr |= MIDCR_RXE;
+ chip->midi_input = substream;
+ if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cs46xx_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+ cs46xx_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
+ chip->midi_input = NULL;
+ if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS46XX_MODE_INPUT;
+ spin_unlock_irq(&chip->reg_lock);
+ chip->active_ctrl(chip, -1);
+ return 0;
+}
+
+static int snd_cs46xx_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+ cs46xx_t *chip = substream->rmidi->private_data;
+
+ chip->active_ctrl(chip, 1);
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->uartm |= CS46XX_MODE_OUTPUT;
+ chip->midcr |= MIDCR_TXE;
+ chip->midi_output = substream;
+ if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_cs46xx_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+ cs46xx_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
+ chip->midi_output = NULL;
+ if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS46XX_MODE_OUTPUT;
+ spin_unlock_irq(&chip->reg_lock);
+ chip->active_ctrl(chip, -1);
+ return 0;
+}
+
+static void snd_cs46xx_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ cs46xx_t *chip = substream->rmidi->private_data;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (up) {
+ if ((chip->midcr & MIDCR_RIE) == 0) {
+ chip->midcr |= MIDCR_RIE;
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ } else {
+ if (chip->midcr & MIDCR_RIE) {
+ chip->midcr &= ~MIDCR_RIE;
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs46xx_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ cs46xx_t *chip = substream->rmidi->private_data;
+ unsigned char byte;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (up) {
+ if ((chip->midcr & MIDCR_TIE) == 0) {
+ chip->midcr |= MIDCR_TIE;
+ /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+ while ((chip->midcr & MIDCR_TIE) &&
+ (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
+ if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ chip->midcr &= ~MIDCR_TIE;
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte);
+ }
+ }
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ } else {
+ if (chip->midcr & MIDCR_TIE) {
+ chip->midcr &= ~MIDCR_TIE;
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static snd_rawmidi_ops_t snd_cs46xx_midi_output =
+{
+ .open = snd_cs46xx_midi_output_open,
+ .close = snd_cs46xx_midi_output_close,
+ .trigger = snd_cs46xx_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_cs46xx_midi_input =
+{
+ .open = snd_cs46xx_midi_input_open,
+ .close = snd_cs46xx_midi_input_close,
+ .trigger = snd_cs46xx_midi_input_trigger,
+};
+
+int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi)
+{
+ snd_rawmidi_t *rmidi;
+ int err;
+
+ if (rrawmidi)
+ *rrawmidi = NULL;
+ if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0)
+ return err;
+ strcpy(rmidi->name, "CS46XX");
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = chip;
+ chip->rmidi = rmidi;
+ if (rrawmidi)
+ *rrawmidi = NULL;
+ return 0;
+}
+
+
+/*
+ * gameport interface
+ */
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+static void snd_cs46xx_gameport_trigger(struct gameport *gameport)
+{
+ cs46xx_t *chip = gameport_get_port_data(gameport);
+
+ snd_assert(chip, return);
+ snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF);
+}
+
+static unsigned char snd_cs46xx_gameport_read(struct gameport *gameport)
+{
+ cs46xx_t *chip = gameport_get_port_data(gameport);
+
+ snd_assert(chip, return 0);
+ return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io);
+}
+
+static int snd_cs46xx_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ cs46xx_t *chip = gameport_get_port_data(gameport);
+ unsigned js1, js2, jst;
+
+ snd_assert(chip, return 0);
+
+ js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1);
+ js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2);
+ jst = snd_cs46xx_peekBA0(chip, BA0_JSPT);
+
+ *buttons = (~jst >> 4) & 0x0F;
+
+ axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
+ axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
+ axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
+ axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
+
+ for(jst=0;jst<4;++jst)
+ if(axes[jst]==0xFFFF) axes[jst] = -1;
+ return 0;
+}
+
+static int snd_cs46xx_gameport_open(struct gameport *gameport, int mode)
+{
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ return 0;
+ case GAMEPORT_MODE_RAW:
+ return 0;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int __devinit snd_cs46xx_gameport(cs46xx_t *chip)
+{
+ struct gameport *gp;
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "cs46xx: cannot allocate memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "CS46xx Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gameport_set_port_data(gp, chip);
+
+ gp->open = snd_cs46xx_gameport_open;
+ gp->read = snd_cs46xx_gameport_read;
+ gp->trigger = snd_cs46xx_gameport_trigger;
+ gp->cooked_read = snd_cs46xx_gameport_cooked_read;
+
+ snd_cs46xx_pokeBA0(chip, BA0_JSIO, 0xFF); // ?
+ snd_cs46xx_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static inline void snd_cs46xx_remove_gameport(cs46xx_t *chip)
+{
+ if (chip->gameport) {
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+ }
+}
+#else
+int __devinit snd_cs46xx_gameport(cs46xx_t *chip) { return -ENOSYS; }
+static inline void snd_cs46xx_remove_gameport(cs46xx_t *chip) { }
+#endif /* CONFIG_GAMEPORT */
+
+/*
+ * proc interface
+ */
+
+static long snd_cs46xx_io_read(snd_info_entry_t *entry, void *file_private_data,
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
+{
+ long size;
+ snd_cs46xx_region_t *region = (snd_cs46xx_region_t *)entry->private_data;
+
+ size = count;
+ if (pos + (size_t)size > region->size)
+ size = region->size - pos;
+ if (size > 0) {
+ if (copy_to_user_fromio(buf, region->remap_addr + pos, size))
+ return -EFAULT;
+ }
+ return size;
+}
+
+static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
+ .read = snd_cs46xx_io_read,
+};
+
+static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip)
+{
+ snd_info_entry_t *entry;
+ int idx;
+
+ for (idx = 0; idx < 5; idx++) {
+ snd_cs46xx_region_t *region = &chip->region.idx[idx];
+ if (! snd_card_proc_new(card, region->name, &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = chip;
+ entry->c.ops = &snd_cs46xx_proc_io_ops;
+ entry->size = region->size;
+ entry->mode = S_IFREG | S_IRUSR;
+ }
+ }
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ cs46xx_dsp_proc_init(card, chip);
+#endif
+ return 0;
+}
+
+static int snd_cs46xx_proc_done(cs46xx_t *chip)
+{
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ cs46xx_dsp_proc_done(chip);
+#endif
+ return 0;
+}
+
+/*
+ * stop the h/w
+ */
+static void snd_cs46xx_hw_stop(cs46xx_t *chip)
+{
+ unsigned int tmp;
+
+ tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+ tmp &= ~0x0000f03f;
+ tmp |= 0x00000010;
+ snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt disable */
+
+ tmp = snd_cs46xx_peek(chip, BA1_CIE);
+ tmp &= ~0x0000003f;
+ tmp |= 0x00000011;
+ snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt disable */
+
+ /*
+ * Stop playback DMA.
+ */
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
+
+ /*
+ * Stop capture DMA.
+ */
+ tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+ snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
+
+ /*
+ * Reset the processor.
+ */
+ snd_cs46xx_reset(chip);
+
+ snd_cs46xx_proc_stop(chip);
+
+ /*
+ * Power down the PLL.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
+
+ /*
+ * Turn off the Processor by turning off the software clock enable flag in
+ * the clock control register.
+ */
+ tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE;
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+}
+
+
+static int snd_cs46xx_free(cs46xx_t *chip)
+{
+ int idx;
+
+ snd_assert(chip != NULL, return -EINVAL);
+
+ if (chip->active_ctrl)
+ chip->active_ctrl(chip, 1);
+
+ snd_cs46xx_remove_gameport(chip);
+
+ if (chip->amplifier_ctrl)
+ chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */
+
+ snd_cs46xx_proc_done(chip);
+
+ if (chip->region.idx[0].resource)
+ snd_cs46xx_hw_stop(chip);
+
+ for (idx = 0; idx < 5; idx++) {
+ snd_cs46xx_region_t *region = &chip->region.idx[idx];
+ if (region->remap_addr)
+ iounmap(region->remap_addr);
+ if (region->resource) {
+ release_resource(region->resource);
+ kfree_nocheck(region->resource);
+ }
+ }
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+
+ if (chip->active_ctrl)
+ chip->active_ctrl(chip, -chip->amplifier);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if (chip->dsp_spos_instance) {
+ cs46xx_dsp_spos_destroy(chip);
+ chip->dsp_spos_instance = NULL;
+ }
+#endif
+
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_cs46xx_dev_free(snd_device_t *device)
+{
+ cs46xx_t *chip = device->device_data;
+ return snd_cs46xx_free(chip);
+}
+
+/*
+ * initialize chip
+ */
+static int snd_cs46xx_chip_init(cs46xx_t *chip)
+{
+ int timeout;
+
+ /*
+ * First, blast the clock control register to zero so that the PLL starts
+ * out in a known state, and blast the master serial port control register
+ * to zero so that the serial ports also start out in a known state.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
+ snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0);
+
+ /*
+ * If we are in AC97 mode, then we must set the part to a host controlled
+ * AC-link. Otherwise, we won't be able to bring up the link.
+ */
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0 |
+ SERACC_TWO_CODECS); /* 2.00 dual codecs */
+ /* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */
+#else
+ snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03); /* 1.03 codec */
+#endif
+
+ /*
+ * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
+ * spec) and then drive it high. This is done for non AC97 modes since
+ * there might be logic external to the CS461x that uses the ARST# line
+ * for a reset.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, 0);
+#endif
+ udelay(50);
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_RSTN);
+#endif
+
+ /*
+ * The first thing we do here is to enable sync generation. As soon
+ * as we start receiving bit clock, we'll start producing the SYNC
+ * signal.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_ESYN | ACCTL_RSTN);
+#endif
+
+ /*
+ * Now wait for a short while to allow the AC97 part to start
+ * generating bit clock (so we don't try to start the PLL without an
+ * input clock).
+ */
+ mdelay(10);
+
+ /*
+ * Set the serial port timing configuration, so that
+ * the clock control circuit gets its clock from the correct place.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97);
+
+ /*
+ * Write the selected clock control setup to the hardware. Do not turn on
+ * SWCE yet (if requested), so that the devices clocked by the output of
+ * PLL are not clocked until the PLL is stable.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ);
+ snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a);
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8);
+
+ /*
+ * Power up the PLL.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP);
+
+ /*
+ * Wait until the PLL has stabilized.
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/10); /* 100ms */
+
+ /*
+ * Turn on clocking of the core so that we can setup the serial ports.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE);
+
+ /*
+ * Enable FIFO Host Bypass
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERBCF, SERBCF_HBP);
+
+ /*
+ * Fill the serial port FIFOs with silence.
+ */
+ snd_cs46xx_clear_serial_FIFOs(chip);
+
+ /*
+ * Set the serial port FIFO pointer to the first sample in the FIFO.
+ */
+ /* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */
+
+ /*
+ * Write the serial port configuration to the part. The master
+ * enable bit is not set until all other values have been written.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN);
+ snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
+ snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_cs46xx_pokeBA0(chip, BA0_SERC7, SERC7_ASDI2EN);
+ snd_cs46xx_pokeBA0(chip, BA0_SERC3, 0);
+ snd_cs46xx_pokeBA0(chip, BA0_SERC4, 0);
+ snd_cs46xx_pokeBA0(chip, BA0_SERC5, 0);
+ snd_cs46xx_pokeBA0(chip, BA0_SERC6, 1);
+#endif
+
+ mdelay(5);
+
+
+ /*
+ * Wait for the codec ready signal from the AC97 codec.
+ */
+ timeout = 150;
+ while (timeout-- > 0) {
+ /*
+ * Read the AC97 status register to see if we've seen a CODEC READY
+ * signal from the AC97 codec.
+ */
+ if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY)
+ goto ok1;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((HZ+99)/100);
+ }
+
+
+ snd_printk("create - never read codec ready from AC'97\n");
+ snd_printk("it is not probably bug, try to use CS4236 driver\n");
+ return -EIO;
+ ok1:
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ {
+ int count;
+ for (count = 0; count < 150; count++) {
+ /* First, we want to wait for a short time. */
+ udelay(25);
+
+ if (snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY)
+ break;
+ }
+
+ /*
+ * Make sure CODEC is READY.
+ */
+ if (!(snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY))
+ snd_printdd("cs46xx: never read card ready from secondary AC'97\n");
+ }
+#endif
+
+ /*
+ * Assert the vaid frame signal so that we can start sending commands
+ * to the AC97 codec.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+#endif
+
+
+ /*
+ * Wait until we've sampled input slots 3 and 4 as valid, meaning that
+ * the codec is pumping ADC data across the AC-link.
+ */
+ timeout = 150;
+ while (timeout-- > 0) {
+ /*
+ * Read the input slot valid register and see if input slots 3 and
+ * 4 are valid yet.
+ */
+ if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
+ goto ok2;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((HZ+99)/100);
+ }
+
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+ snd_printk("create - never read ISV3 & ISV4 from AC'97\n");
+ return -EIO;
+#else
+ /* This may happen on a cold boot with a Terratec SiXPack 5.1.
+ Reloading the driver may help, if there's other soundcards
+ with the same problem I would like to know. (Benny) */
+
+ snd_printk("ERROR: snd-cs46xx: never read ISV3 & ISV4 from AC'97\n");
+ snd_printk(" Try reloading the ALSA driver, if you find something\n");
+ snd_printk(" broken or not working on your soundcard upon\n");
+ snd_printk(" this message please report to alsa-devel@lists.sourceforge.net\n");
+
+ return -EIO;
+#endif
+ ok2:
+
+ /*
+ * Now, assert valid frame and the slot 3 and 4 valid bits. This will
+ * commense the transfer of digital audio data to the AC97 codec.
+ */
+
+ snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
+
+
+ /*
+ * Power down the DAC and ADC. We will power them up (if) when we need
+ * them.
+ */
+ /* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */
+
+ /*
+ * Turn off the Processor by turning off the software clock enable flag in
+ * the clock control register.
+ */
+ /* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */
+ /* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */
+
+ return 0;
+}
+
+/*
+ * start and load DSP
+ */
+int __devinit snd_cs46xx_start_dsp(cs46xx_t *chip)
+{
+ unsigned int tmp;
+ /*
+ * Reset the processor.
+ */
+ snd_cs46xx_reset(chip);
+ /*
+ * Download the image to the processor.
+ */
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+#if 0
+ if (cs46xx_dsp_load_module(chip, &cwcemb80_module) < 0) {
+ snd_printk(KERN_ERR "image download error\n");
+ return -EIO;
+ }
+#endif
+
+ if (cs46xx_dsp_load_module(chip, &cwc4630_module) < 0) {
+ snd_printk(KERN_ERR "image download error [cwc4630]\n");
+ return -EIO;
+ }
+
+ if (cs46xx_dsp_load_module(chip, &cwcasync_module) < 0) {
+ snd_printk(KERN_ERR "image download error [cwcasync]\n");
+ return -EIO;
+ }
+
+ if (cs46xx_dsp_load_module(chip, &cwcsnoop_module) < 0) {
+ snd_printk(KERN_ERR "image download error [cwcsnoop]\n");
+ return -EIO;
+ }
+
+ if (cs46xx_dsp_load_module(chip, &cwcbinhack_module) < 0) {
+ snd_printk(KERN_ERR "image download error [cwcbinhack]\n");
+ return -EIO;
+ }
+
+ if (cs46xx_dsp_load_module(chip, &cwcdma_module) < 0) {
+ snd_printk(KERN_ERR "image download error [cwcdma]\n");
+ return -EIO;
+ }
+
+ if (cs46xx_dsp_scb_and_task_init(chip) < 0)
+ return -EIO;
+#else
+ /* old image */
+ if (snd_cs46xx_download_image(chip) < 0) {
+ snd_printk("image download error\n");
+ return -EIO;
+ }
+
+ /*
+ * Stop playback DMA.
+ */
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ chip->play_ctl = tmp & 0xffff0000;
+ snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
+#endif
+
+ /*
+ * Stop capture DMA.
+ */
+ tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+ chip->capt.ctl = tmp & 0x0000ffff;
+ snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
+
+ mdelay(5);
+
+ snd_cs46xx_set_play_sample_rate(chip, 8000);
+ snd_cs46xx_set_capture_sample_rate(chip, 8000);
+
+ snd_cs46xx_proc_start(chip);
+
+ /*
+ * Enable interrupts on the part.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
+
+ tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+ tmp &= ~0x0000f03f;
+ snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */
+
+ tmp = snd_cs46xx_peek(chip, BA1_CIE);
+ tmp &= ~0x0000003f;
+ tmp |= 0x00000001;
+ snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
+
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+ /* set the attenuation to 0dB */
+ snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000);
+ snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * AMP control - null AMP
+ */
+
+static void amp_none(cs46xx_t *chip, int change)
+{
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static int voyetra_setup_eapd_slot(cs46xx_t *chip)
+{
+
+ u32 idx, valid_slots,tmp,powerdown = 0;
+ u16 modem_power,pin_config,logic_type;
+
+ snd_printdd ("cs46xx: cs46xx_setup_eapd_slot()+\n");
+
+ /*
+ * See if the devices are powered down. If so, we must power them up first
+ * or they will not respond.
+ */
+ tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
+
+ if (!(tmp & CLKCR1_SWCE)) {
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
+ powerdown = 1;
+ }
+
+ /*
+ * Clear PRA. The Bonzo chip will be used for GPIO not for modem
+ * stuff.
+ */
+ if(chip->nr_ac97_codecs != 2) {
+ snd_printk (KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() - no secondary codec configured\n");
+ return -EINVAL;
+ }
+
+ modem_power = snd_cs46xx_codec_read (chip,
+ AC97_EXTENDED_MSTATUS,
+ CS46XX_SECONDARY_CODEC_INDEX);
+ modem_power &=0xFEFF;
+
+ snd_cs46xx_codec_write(chip,
+ AC97_EXTENDED_MSTATUS, modem_power,
+ CS46XX_SECONDARY_CODEC_INDEX);
+
+ /*
+ * Set GPIO pin's 7 and 8 so that they are configured for output.
+ */
+ pin_config = snd_cs46xx_codec_read (chip,
+ AC97_GPIO_CFG,
+ CS46XX_SECONDARY_CODEC_INDEX);
+ pin_config &=0x27F;
+
+ snd_cs46xx_codec_write(chip,
+ AC97_GPIO_CFG, pin_config,
+ CS46XX_SECONDARY_CODEC_INDEX);
+
+ /*
+ * Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic.
+ */
+
+ logic_type = snd_cs46xx_codec_read(chip, AC97_GPIO_POLARITY,
+ CS46XX_SECONDARY_CODEC_INDEX);
+ logic_type &=0x27F;
+
+ snd_cs46xx_codec_write (chip, AC97_GPIO_POLARITY, logic_type,
+ CS46XX_SECONDARY_CODEC_INDEX);
+
+ valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
+ valid_slots |= 0x200;
+ snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
+
+ if ( cs46xx_wait_for_fifo(chip,1) ) {
+ snd_printdd("FIFO is busy\n");
+
+ return -EINVAL;
+ }
+
+ /*
+ * Fill slots 12 with the correct value for the GPIO pins.
+ */
+ for(idx = 0x90; idx <= 0x9F; idx++) {
+ /*
+ * Initialize the fifo so that bits 7 and 8 are on.
+ *
+ * Remember that the GPIO pins in bonzo are shifted by 4 bits to
+ * the left. 0x1800 corresponds to bits 7 and 8.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0x1800);
+
+ /*
+ * Wait for command to complete
+ */
+ if ( cs46xx_wait_for_fifo(chip,200) ) {
+ snd_printdd("failed waiting for FIFO at addr (%02X)\n",idx);
+
+ return -EINVAL;
+ }
+
+ /*
+ * Write the serial port FIFO index.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
+
+ /*
+ * Tell the serial port to load the new value into the FIFO location.
+ */
+ snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
+ }
+
+ /* wait for last command to complete */
+ cs46xx_wait_for_fifo(chip,200);
+
+ /*
+ * Now, if we powered up the devices, then power them back down again.
+ * This is kinda ugly, but should never happen.
+ */
+ if (powerdown)
+ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+
+ return 0;
+}
+#endif
+
+/*
+ * Crystal EAPD mode
+ */
+
+static void amp_voyetra(cs46xx_t *chip, int change)
+{
+ /* Manage the EAPD bit on the Crystal 4297
+ and the Analog AD1885 */
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ int old = chip->amplifier;
+#endif
+ int oval, val;
+
+ chip->amplifier += change;
+ oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN,
+ CS46XX_PRIMARY_CODEC_INDEX);
+ val = oval;
+ if (chip->amplifier) {
+ /* Turn the EAPD amp on */
+ val |= 0x8000;
+ } else {
+ /* Turn the EAPD amp off */
+ val &= ~0x8000;
+ }
+ if (val != oval) {
+ snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val,
+ CS46XX_PRIMARY_CODEC_INDEX);
+ if (chip->eapd_switch)
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->eapd_switch->id);
+ }
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if (chip->amplifier && !old) {
+ voyetra_setup_eapd_slot(chip);
+ }
+#endif
+}
+
+static void hercules_init(cs46xx_t *chip)
+{
+ /* default: AMP off, and SPDIF input optical */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
+}
+
+
+/*
+ * Game Theatre XP card - EGPIO[2] is used to enable the external amp.
+ */
+static void amp_hercules(cs46xx_t *chip, int change)
+{
+ int old = chip->amplifier;
+ int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+ int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
+
+ chip->amplifier += change;
+ if (chip->amplifier && !old) {
+ snd_printdd ("Hercules amplifier ON\n");
+
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,
+ EGPIODR_GPOE2 | val1); /* enable EGPIO2 output */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR,
+ EGPIOPTR_GPPT2 | val2); /* open-drain on output */
+ } else if (old && !chip->amplifier) {
+ snd_printdd ("Hercules amplifier OFF\n");
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, val1 & ~EGPIODR_GPOE2); /* disable */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT2); /* disable */
+ }
+}
+
+static void voyetra_mixer_init (cs46xx_t *chip)
+{
+ snd_printdd ("initializing Voyetra mixer\n");
+
+ /* Enable SPDIF out */
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
+ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
+}
+
+static void hercules_mixer_init (cs46xx_t *chip)
+{
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ unsigned int idx;
+ int err;
+ snd_card_t *card = chip->card;
+#endif
+
+ /* set EGPIO to default */
+ hercules_init(chip);
+
+ snd_printdd ("initializing Hercules mixer\n");
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ for (idx = 0 ; idx < ARRAY_SIZE(snd_hercules_controls); idx++) {
+ snd_kcontrol_t *kctl;
+
+ kctl = snd_ctl_new1(&snd_hercules_controls[idx], chip);
+ if ((err = snd_ctl_add(card, kctl)) < 0) {
+ printk (KERN_ERR "cs46xx: failed to initialize Hercules mixer (%d)\n",err);
+ break;
+ }
+ }
+#endif
+}
+
+
+#if 0
+/*
+ * Untested
+ */
+
+static void amp_voyetra_4294(cs46xx_t *chip, int change)
+{
+ chip->amplifier += change;
+
+ if (chip->amplifier) {
+ /* Switch the GPIO pins 7 and 8 to open drain */
+ snd_cs46xx_codec_write(chip, 0x4C,
+ snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F);
+ snd_cs46xx_codec_write(chip, 0x4E,
+ snd_cs46xx_codec_read(chip, 0x4E) | 0x0180);
+ /* Now wake the AMP (this might be backwards) */
+ snd_cs46xx_codec_write(chip, 0x54,
+ snd_cs46xx_codec_read(chip, 0x54) & ~0x0180);
+ } else {
+ snd_cs46xx_codec_write(chip, 0x54,
+ snd_cs46xx_codec_read(chip, 0x54) | 0x0180);
+ }
+}
+#endif
+
+
+/*
+ * piix4 pci ids
+ */
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif /* PCI_VENDOR_ID_INTEL */
+
+#ifndef PCI_DEVICE_ID_INTEL_82371AB_3
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#endif /* PCI_DEVICE_ID_INTEL_82371AB_3 */
+
+/*
+ * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support
+ * whenever we need to beat on the chip.
+ *
+ * The original idea and code for this hack comes from David Kaiser at
+ * Linuxcare. Perhaps one day Crystal will document their chips well
+ * enough to make them useful.
+ */
+
+static void clkrun_hack(cs46xx_t *chip, int change)
+{
+ u16 control, nval;
+
+ if (chip->acpi_dev == NULL)
+ return;
+
+ chip->amplifier += change;
+
+ /* Read ACPI port */
+ nval = control = inw(chip->acpi_port + 0x10);
+
+ /* Flip CLKRUN off while running */
+ if (! chip->amplifier)
+ nval |= 0x2000;
+ else
+ nval &= ~0x2000;
+ if (nval != control)
+ outw(nval, chip->acpi_port + 0x10);
+}
+
+
+/*
+ * detect intel piix4
+ */
+static void clkrun_init(cs46xx_t *chip)
+{
+ u8 pp;
+
+ chip->acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+ if (chip->acpi_dev == NULL)
+ return; /* Not a thinkpad thats for sure */
+
+ /* Find the control port */
+ pci_read_config_byte(chip->acpi_dev, 0x41, &pp);
+ chip->acpi_port = pp << 8;
+}
+
+
+/*
+ * Card subid table
+ */
+
+struct cs_card_type
+{
+ u16 vendor;
+ u16 id;
+ char *name;
+ void (*init)(cs46xx_t *);
+ void (*amp)(cs46xx_t *, int);
+ void (*active)(cs46xx_t *, int);
+ void (*mixer_init)(cs46xx_t *);
+};
+
+static struct cs_card_type __devinitdata cards[] = {
+ {
+ .vendor = 0x1489,
+ .id = 0x7001,
+ .name = "Genius Soundmaker 128 value",
+ /* nothing special */
+ },
+ {
+ .vendor = 0x5053,
+ .id = 0x3357,
+ .name = "Voyetra",
+ .amp = amp_voyetra,
+ .mixer_init = voyetra_mixer_init,
+ },
+ {
+ .vendor = 0x1071,
+ .id = 0x6003,
+ .name = "Mitac MI6020/21",
+ .amp = amp_voyetra,
+ },
+ {
+ .vendor = 0x14AF,
+ .id = 0x0050,
+ .name = "Hercules Game Theatre XP",
+ .amp = amp_hercules,
+ .mixer_init = hercules_mixer_init,
+ },
+ {
+ .vendor = 0x1681,
+ .id = 0x0050,
+ .name = "Hercules Game Theatre XP",
+ .amp = amp_hercules,
+ .mixer_init = hercules_mixer_init,
+ },
+ {
+ .vendor = 0x1681,
+ .id = 0x0051,
+ .name = "Hercules Game Theatre XP",
+ .amp = amp_hercules,
+ .mixer_init = hercules_mixer_init,
+
+ },
+ {
+ .vendor = 0x1681,
+ .id = 0x0052,
+ .name = "Hercules Game Theatre XP",
+ .amp = amp_hercules,
+ .mixer_init = hercules_mixer_init,
+ },
+ {
+ .vendor = 0x1681,
+ .id = 0x0053,
+ .name = "Hercules Game Theatre XP",
+ .amp = amp_hercules,
+ .mixer_init = hercules_mixer_init,
+ },
+ {
+ .vendor = 0x1681,
+ .id = 0x0054,
+ .name = "Hercules Game Theatre XP",
+ .amp = amp_hercules,
+ .mixer_init = hercules_mixer_init,
+ },
+ /* Teratec */
+ {
+ .vendor = 0x153b,
+ .id = 0x1136,
+ .name = "Terratec SiXPack 5.1",
+ },
+ /* Not sure if the 570 needs the clkrun hack */
+ {
+ .vendor = PCI_VENDOR_ID_IBM,
+ .id = 0x0132,
+ .name = "Thinkpad 570",
+ .init = clkrun_init,
+ .active = clkrun_hack,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_IBM,
+ .id = 0x0153,
+ .name = "Thinkpad 600X/A20/T20",
+ .init = clkrun_init,
+ .active = clkrun_hack,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_IBM,
+ .id = 0x1010,
+ .name = "Thinkpad 600E (unsupported)",
+ },
+ {} /* terminator */
+};
+
+
+/*
+ * APM support
+ */
+#ifdef CONFIG_PM
+static int snd_cs46xx_suspend(snd_card_t *card, pm_message_t state)
+{
+ cs46xx_t *chip = card->pm_private_data;
+ int amp_saved;
+
+ snd_pcm_suspend_all(chip->pcm);
+ // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
+ // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
+
+ snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
+ if (chip->ac97[CS46XX_SECONDARY_CODEC_INDEX])
+ snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+
+ amp_saved = chip->amplifier;
+ /* turn off amp */
+ chip->amplifier_ctrl(chip, -chip->amplifier);
+ snd_cs46xx_hw_stop(chip);
+ /* disable CLKRUN */
+ chip->active_ctrl(chip, -chip->amplifier);
+ chip->amplifier = amp_saved; /* restore the status */
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int snd_cs46xx_resume(snd_card_t *card)
+{
+ cs46xx_t *chip = card->pm_private_data;
+ int amp_saved;
+
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+ amp_saved = chip->amplifier;
+ chip->amplifier = 0;
+ chip->active_ctrl(chip, 1); /* force to on */
+
+ snd_cs46xx_chip_init(chip);
+
+#if 0
+ snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE,
+ chip->ac97_general_purpose);
+ snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL,
+ chip->ac97_powerdown);
+ mdelay(10);
+ snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN,
+ chip->ac97_powerdown);
+ mdelay(5);
+#endif
+
+ snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
+ if (chip->ac97[CS46XX_SECONDARY_CODEC_INDEX])
+ snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+
+ if (amp_saved)
+ chip->amplifier_ctrl(chip, 1); /* turn amp on */
+ else
+ chip->active_ctrl(chip, -1); /* disable CLKRUN */
+ chip->amplifier = amp_saved;
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ */
+
+int __devinit snd_cs46xx_create(snd_card_t * card,
+ struct pci_dev * pci,
+ int external_amp, int thinkpad,
+ cs46xx_t ** rchip)
+{
+ cs46xx_t *chip;
+ int err, idx;
+ snd_cs46xx_region_t *region;
+ struct cs_card_type *cp;
+ u16 ss_card, ss_vendor;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_cs46xx_dev_free,
+ };
+
+ *rchip = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&chip->reg_lock);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ init_MUTEX(&chip->spos_mutex);
+#endif
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ chip->ba0_addr = pci_resource_start(pci, 0);
+ chip->ba1_addr = pci_resource_start(pci, 1);
+ if (chip->ba0_addr == 0 || chip->ba0_addr == (unsigned long)~0 ||
+ chip->ba1_addr == 0 || chip->ba1_addr == (unsigned long)~0) {
+ snd_printk("wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n", chip->ba0_addr, chip->ba1_addr);
+ snd_cs46xx_free(chip);
+ return -ENOMEM;
+ }
+
+ region = &chip->region.name.ba0;
+ strcpy(region->name, "CS46xx_BA0");
+ region->base = chip->ba0_addr;
+ region->size = CS46XX_BA0_SIZE;
+
+ region = &chip->region.name.data0;
+ strcpy(region->name, "CS46xx_BA1_data0");
+ region->base = chip->ba1_addr + BA1_SP_DMEM0;
+ region->size = CS46XX_BA1_DATA0_SIZE;
+
+ region = &chip->region.name.data1;
+ strcpy(region->name, "CS46xx_BA1_data1");
+ region->base = chip->ba1_addr + BA1_SP_DMEM1;
+ region->size = CS46XX_BA1_DATA1_SIZE;
+
+ region = &chip->region.name.pmem;
+ strcpy(region->name, "CS46xx_BA1_pmem");
+ region->base = chip->ba1_addr + BA1_SP_PMEM;
+ region->size = CS46XX_BA1_PRG_SIZE;
+
+ region = &chip->region.name.reg;
+ strcpy(region->name, "CS46xx_BA1_reg");
+ region->base = chip->ba1_addr + BA1_SP_REG;
+ region->size = CS46XX_BA1_REG_SIZE;
+
+ /* set up amp and clkrun hack */
+ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card);
+
+ for (cp = &cards[0]; cp->name; cp++) {
+ if (cp->vendor == ss_vendor && cp->id == ss_card) {
+ snd_printdd ("hack for %s enabled\n", cp->name);
+
+ chip->amplifier_ctrl = cp->amp;
+ chip->active_ctrl = cp->active;
+ chip->mixer_init = cp->mixer_init;
+
+ if (cp->init)
+ cp->init(chip);
+ break;
+ }
+ }
+
+ if (external_amp) {
+ snd_printk("Crystal EAPD support forced on.\n");
+ chip->amplifier_ctrl = amp_voyetra;
+ }
+
+ if (thinkpad) {
+ snd_printk("Activating CLKRUN hack for Thinkpad.\n");
+ chip->active_ctrl = clkrun_hack;
+ clkrun_init(chip);
+ }
+
+ if (chip->amplifier_ctrl == NULL)
+ chip->amplifier_ctrl = amp_none;
+ if (chip->active_ctrl == NULL)
+ chip->active_ctrl = amp_none;
+
+ chip->active_ctrl(chip, 1); /* enable CLKRUN */
+
+ pci_set_master(pci);
+
+ for (idx = 0; idx < 5; idx++) {
+ region = &chip->region.idx[idx];
+ if ((region->resource = request_mem_region(region->base, region->size, region->name)) == NULL) {
+ snd_printk("unable to request memory region 0x%lx-0x%lx\n", region->base, region->base + region->size - 1);
+ snd_cs46xx_free(chip);
+ return -EBUSY;
+ }
+ region->remap_addr = ioremap_nocache(region->base, region->size);
+ if (region->remap_addr == NULL) {
+ snd_printk("%s ioremap problem\n", region->name);
+ snd_cs46xx_free(chip);
+ return -ENOMEM;
+ }
+ }
+
+ if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS46XX", (void *) chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_cs46xx_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ chip->dsp_spos_instance = cs46xx_dsp_spos_create(chip);
+ if (chip->dsp_spos_instance == NULL) {
+ snd_cs46xx_free(chip);
+ return -ENOMEM;
+ }
+#endif
+
+ err = snd_cs46xx_chip_init(chip);
+ if (err < 0) {
+ snd_cs46xx_free(chip);
+ return err;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_cs46xx_free(chip);
+ return err;
+ }
+
+ snd_cs46xx_proc_init(card, chip);
+
+ snd_card_set_pm_callback(card, snd_cs46xx_suspend, snd_cs46xx_resume, chip);
+
+ chip->active_ctrl(chip, -1); /* disable CLKRUN */
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = chip;
+ return 0;
+}
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
new file mode 100644
index 00000000000..d7bec096d24
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_lib.h
@@ -0,0 +1,182 @@
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __CS46XX_LIB_H__
+#define __CS46XX_LIB_H__
+
+/*
+ * constants
+ */
+
+#define CS46XX_BA0_SIZE 0x1000
+#define CS46XX_BA1_DATA0_SIZE 0x3000
+#define CS46XX_BA1_DATA1_SIZE 0x3800
+#define CS46XX_BA1_PRG_SIZE 0x7000
+#define CS46XX_BA1_REG_SIZE 0x0100
+
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+#define CS46XX_MIN_PERIOD_SIZE 1
+#define CS46XX_MAX_PERIOD_SIZE 1024*1024
+#else
+#define CS46XX_MIN_PERIOD_SIZE 2048
+#define CS46XX_MAX_PERIOD_SIZE 2048
+#endif
+
+#define CS46XX_FRAGS 2
+/* #define CS46XX_BUFFER_SIZE CS46XX_MAX_PERIOD_SIZE * CS46XX_FRAGS */
+
+#define SCB_NO_PARENT 0
+#define SCB_ON_PARENT_NEXT_SCB 1
+#define SCB_ON_PARENT_SUBLIST_SCB 2
+
+/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */
+#define BA1_DWORD_SIZE (13 * 1024 + 512)
+#define BA1_MEMORY_COUNT 3
+
+/*
+ * common I/O routines
+ */
+
+static inline void snd_cs46xx_poke(cs46xx_t *chip, unsigned long reg, unsigned int val)
+{
+ unsigned int bank = reg >> 16;
+ unsigned int offset = reg & 0xffff;
+
+ /*if (bank == 0) printk("snd_cs46xx_poke: %04X - %08X\n",reg >> 2,val); */
+ writel(val, chip->region.idx[bank+1].remap_addr + offset);
+}
+
+static inline unsigned int snd_cs46xx_peek(cs46xx_t *chip, unsigned long reg)
+{
+ unsigned int bank = reg >> 16;
+ unsigned int offset = reg & 0xffff;
+ return readl(chip->region.idx[bank+1].remap_addr + offset);
+}
+
+static inline void snd_cs46xx_pokeBA0(cs46xx_t *chip, unsigned long offset, unsigned int val)
+{
+ writel(val, chip->region.name.ba0.remap_addr + offset);
+}
+
+static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offset)
+{
+ return readl(chip->region.name.ba0.remap_addr + offset);
+}
+
+dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip);
+void cs46xx_dsp_spos_destroy (cs46xx_t * chip);
+int cs46xx_dsp_load_module (cs46xx_t * chip,dsp_module_desc_t * module);
+symbol_entry_t * cs46xx_dsp_lookup_symbol (cs46xx_t * chip,char * symbol_name,int symbol_type);
+int cs46xx_dsp_proc_init (snd_card_t * card, cs46xx_t *chip);
+int cs46xx_dsp_proc_done (cs46xx_t *chip);
+int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip);
+int snd_cs46xx_download (cs46xx_t *chip,u32 *src,unsigned long offset,
+ unsigned long len);
+int snd_cs46xx_clear_BA1(cs46xx_t *chip,unsigned long offset,unsigned long len);
+int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip);
+int cs46xx_dsp_enable_spdif_hw (cs46xx_t *chip);
+int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip);
+int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip);
+int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip);
+int cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip);
+int cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip);
+int cs46xx_dsp_enable_adc_capture (cs46xx_t *chip);
+int cs46xx_dsp_disable_adc_capture (cs46xx_t *chip);
+int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data);
+dsp_scb_descriptor_t * cs46xx_dsp_create_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest);
+void cs46xx_dsp_proc_free_scb_desc (dsp_scb_descriptor_t * scb);
+void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * scb);
+dsp_scb_descriptor_t * cs46xx_dsp_create_timing_master_scb (cs46xx_t *chip);
+dsp_scb_descriptor_t * cs46xx_dsp_create_codec_out_scb(cs46xx_t * chip,char * codec_name,
+ u16 channel_disp,u16 fifo_addr,
+ u16 child_scb_addr,
+ u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+dsp_scb_descriptor_t * cs46xx_dsp_create_codec_in_scb(cs46xx_t * chip,char * codec_name,
+ u16 channel_disp,u16 fifo_addr,
+ u16 sample_buffer_addr,
+ u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+void cs46xx_dsp_remove_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb);
+dsp_scb_descriptor_t * cs46xx_dsp_create_codec_in_scb(cs46xx_t * chip,char * codec_name,
+ u16 channel_disp,u16 fifo_addr,
+ u16 sample_buffer_addr,
+ u32 dest,dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+dsp_scb_descriptor_t * cs46xx_dsp_create_src_task_scb(cs46xx_t * chip,char * scb_name,
+ int sample_rate,
+ u16 src_buffer_addr,
+ u16 src_delay_buffer_addr,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type,
+ int pass_through);
+dsp_scb_descriptor_t * cs46xx_dsp_create_mix_only_scb(cs46xx_t * chip,char * scb_name,
+ u16 mix_buffer_addr,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+
+dsp_scb_descriptor_t * cs46xx_dsp_create_vari_decimate_scb(cs46xx_t * chip,char * scb_name,
+ u16 vari_buffer_addr0,
+ u16 vari_buffer_addr1,
+ u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+dsp_scb_descriptor_t * cs46xx_dsp_create_asynch_fg_rx_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ u16 hfg_scb_address,
+ u16 asynch_buffer_address,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+dsp_scb_descriptor_t * cs46xx_dsp_create_spio_write_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+dsp_scb_descriptor_t * cs46xx_dsp_create_mix_to_ostream_scb(cs46xx_t * chip,char * scb_name,
+ u16 mix_buffer_addr,u16 writeback_spb,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+dsp_scb_descriptor_t * cs46xx_dsp_create_magic_snoop_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ u16 snoop_buffer_address,
+ dsp_scb_descriptor_t * snoop_scb,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type);
+pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data, u32 hw_dma_addr,
+ int pcm_channel_id);
+void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,
+ pcm_channel_descriptor_t * pcm_channel);
+int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel);
+int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel);
+dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
+ u16 addr,char * scb_name);
+int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src);
+int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src);
+int cs46xx_iec958_pre_open (cs46xx_t *chip);
+int cs46xx_iec958_post_close (cs46xx_t *chip);
+int cs46xx_dsp_pcm_channel_set_period (cs46xx_t * chip,
+ pcm_channel_descriptor_t * pcm_channel,
+ int period_size);
+int cs46xx_dsp_pcm_ostream_set_period (cs46xx_t * chip,
+ int period_size);
+int cs46xx_dsp_set_dac_volume (cs46xx_t * chip,u16 left,u16 right);
+int cs46xx_dsp_set_iec958_volume (cs46xx_t * chip,u16 left,u16 right);
+#endif /* __CS46XX_LIB_H__ */
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
new file mode 100644
index 00000000000..b66304fc4e4
--- /dev/null
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -0,0 +1,1892 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * 2002-07 Benny Sjostrand benny@hostmobility.com
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#include <sound/cs46xx.h>
+
+#include "cs46xx_lib.h"
+#include "dsp_spos.h"
+
+static int cs46xx_dsp_async_init (cs46xx_t *chip, dsp_scb_descriptor_t * fg_entry);
+
+static wide_opcode_t wide_opcodes[] = {
+ WIDE_FOR_BEGIN_LOOP,
+ WIDE_FOR_BEGIN_LOOP2,
+ WIDE_COND_GOTO_ADDR,
+ WIDE_COND_GOTO_CALL,
+ WIDE_TBEQ_COND_GOTO_ADDR,
+ WIDE_TBEQ_COND_CALL_ADDR,
+ WIDE_TBEQ_NCOND_GOTO_ADDR,
+ WIDE_TBEQ_NCOND_CALL_ADDR,
+ WIDE_TBEQ_COND_GOTO1_ADDR,
+ WIDE_TBEQ_COND_CALL1_ADDR,
+ WIDE_TBEQ_NCOND_GOTOI_ADDR,
+ WIDE_TBEQ_NCOND_CALL1_ADDR
+};
+
+static int shadow_and_reallocate_code (cs46xx_t * chip,u32 * data,u32 size, u32 overlay_begin_address)
+{
+ unsigned int i = 0, j, nreallocated = 0;
+ u32 hival,loval,address;
+ u32 mop_operands,mop_type,wide_op;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert( ((size % 2) == 0), return -EINVAL);
+
+ while (i < size) {
+ loval = data[i++];
+ hival = data[i++];
+
+ if (ins->code.offset > 0) {
+ mop_operands = (hival >> 6) & 0x03fff;
+ mop_type = mop_operands >> 10;
+
+ /* check for wide type instruction */
+ if (mop_type == 0 &&
+ (mop_operands & WIDE_LADD_INSTR_MASK) == 0 &&
+ (mop_operands & WIDE_INSTR_MASK) != 0) {
+ wide_op = loval & 0x7f;
+ for (j = 0;j < ARRAY_SIZE(wide_opcodes); ++j) {
+ if (wide_opcodes[j] == wide_op) {
+ /* need to reallocate instruction */
+ address = (hival & 0x00FFF) << 5;
+ address |= loval >> 15;
+
+ snd_printdd("handle_wideop[1]: %05x:%05x addr %04x\n",hival,loval,address);
+
+ if ( !(address & 0x8000) ) {
+ address += (ins->code.offset / 2) - overlay_begin_address;
+ } else {
+ snd_printdd("handle_wideop[1]: ROM symbol not reallocated\n");
+ }
+
+ hival &= 0xFF000;
+ loval &= 0x07FFF;
+
+ hival |= ( (address >> 5) & 0x00FFF);
+ loval |= ( (address << 15) & 0xF8000);
+
+ address = (hival & 0x00FFF) << 5;
+ address |= loval >> 15;
+
+ snd_printdd("handle_wideop:[2] %05x:%05x addr %04x\n",hival,loval,address);
+ nreallocated ++;
+ } /* wide_opcodes[j] == wide_op */
+ } /* for */
+ } /* mod_type == 0 ... */
+ } /* ins->code.offset > 0 */
+
+ ins->code.data[ins->code.size++] = loval;
+ ins->code.data[ins->code.size++] = hival;
+ }
+
+ snd_printdd("dsp_spos: %d instructions reallocated\n",nreallocated);
+ return nreallocated;
+}
+
+static segment_desc_t * get_segment_desc (dsp_module_desc_t * module, int seg_type)
+{
+ int i;
+ for (i = 0;i < module->nsegments; ++i) {
+ if (module->segments[i].segment_type == seg_type) {
+ return (module->segments + i);
+ }
+ }
+
+ return NULL;
+};
+
+static int find_free_symbol_index (dsp_spos_instance_t * ins)
+{
+ int index = ins->symbol_table.nsymbols,i;
+
+ for (i = ins->symbol_table.highest_frag_index; i < ins->symbol_table.nsymbols; ++i) {
+ if (ins->symbol_table.symbols[i].deleted) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+}
+
+static int add_symbols (cs46xx_t * chip, dsp_module_desc_t * module)
+{
+ int i;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ if (module->symbol_table.nsymbols > 0) {
+ if (!strcmp(module->symbol_table.symbols[0].symbol_name, "OVERLAYBEGINADDRESS") &&
+ module->symbol_table.symbols[0].symbol_type == SYMBOL_CONSTANT ) {
+ module->overlay_begin_address = module->symbol_table.symbols[0].address;
+ }
+ }
+
+ for (i = 0;i < module->symbol_table.nsymbols; ++i) {
+ if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
+ snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+ return -ENOMEM;
+ }
+
+
+ if (cs46xx_dsp_lookup_symbol(chip,
+ module->symbol_table.symbols[i].symbol_name,
+ module->symbol_table.symbols[i].symbol_type) == NULL) {
+
+ ins->symbol_table.symbols[ins->symbol_table.nsymbols] = module->symbol_table.symbols[i];
+ ins->symbol_table.symbols[ins->symbol_table.nsymbols].address += ((ins->code.offset / 2) - module->overlay_begin_address);
+ ins->symbol_table.symbols[ins->symbol_table.nsymbols].module = module;
+ ins->symbol_table.symbols[ins->symbol_table.nsymbols].deleted = 0;
+
+ if (ins->symbol_table.nsymbols > ins->symbol_table.highest_frag_index)
+ ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols;
+
+ ins->symbol_table.nsymbols++;
+ } else {
+ /* if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
+ module->symbol_table.symbols[i].symbol_name); */
+ }
+ }
+
+ return 0;
+}
+
+static symbol_entry_t * add_symbol (cs46xx_t * chip, char * symbol_name, u32 address, int type)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ symbol_entry_t * symbol = NULL;
+ int index;
+
+ if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
+ snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+ return NULL;
+ }
+
+ if (cs46xx_dsp_lookup_symbol(chip,
+ symbol_name,
+ type) != NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol <%s> duplicated\n", symbol_name);
+ return NULL;
+ }
+
+ index = find_free_symbol_index (ins);
+
+ strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name);
+ ins->symbol_table.symbols[index].address = address;
+ ins->symbol_table.symbols[index].symbol_type = type;
+ ins->symbol_table.symbols[index].module = NULL;
+ ins->symbol_table.symbols[index].deleted = 0;
+ symbol = (ins->symbol_table.symbols + index);
+
+ if (index > ins->symbol_table.highest_frag_index)
+ ins->symbol_table.highest_frag_index = index;
+
+ if (index == ins->symbol_table.nsymbols)
+ ins->symbol_table.nsymbols++; /* no frag. in list */
+
+ return symbol;
+}
+
+dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip)
+{
+ dsp_spos_instance_t * ins = kmalloc(sizeof(dsp_spos_instance_t), GFP_KERNEL);
+
+ if (ins == NULL)
+ return NULL;
+ memset(ins, 0, sizeof(*ins));
+
+ /* better to use vmalloc for this big table */
+ ins->symbol_table.nsymbols = 0;
+ ins->symbol_table.symbols = vmalloc(sizeof(symbol_entry_t) * DSP_MAX_SYMBOLS);
+ ins->symbol_table.highest_frag_index = 0;
+
+ if (ins->symbol_table.symbols == NULL) {
+ cs46xx_dsp_spos_destroy(chip);
+ return NULL;
+ }
+
+ ins->code.offset = 0;
+ ins->code.size = 0;
+ ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
+
+ if (ins->code.data == NULL) {
+ cs46xx_dsp_spos_destroy(chip);
+ return NULL;
+ }
+
+ ins->nscb = 0;
+ ins->ntask = 0;
+
+ ins->nmodules = 0;
+ ins->modules = kmalloc(sizeof(dsp_module_desc_t) * DSP_MAX_MODULES, GFP_KERNEL);
+
+ if (ins->modules == NULL) {
+ cs46xx_dsp_spos_destroy(chip);
+ return NULL;
+ }
+
+ /* default SPDIF input sample rate
+ to 48000 khz */
+ ins->spdif_in_sample_rate = 48000;
+
+ /* maximize volume */
+ ins->dac_volume_right = 0x8000;
+ ins->dac_volume_left = 0x8000;
+ ins->spdif_input_volume_right = 0x8000;
+ ins->spdif_input_volume_left = 0x8000;
+
+ /* set left and right validity bits and
+ default channel status */
+ ins->spdif_csuv_default =
+ ins->spdif_csuv_stream =
+ /* byte 0 */ ((unsigned int)_wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF & 0xff)) << 24) |
+ /* byte 1 */ ((unsigned int)_wrap_all_bits( ((SNDRV_PCM_DEFAULT_CON_SPDIF >> 8) & 0xff)) << 16) |
+ /* byte 3 */ (unsigned int)_wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF >> 24) & 0xff) |
+ /* left and right validity bits */ (1 << 13) | (1 << 12);
+
+ return ins;
+}
+
+void cs46xx_dsp_spos_destroy (cs46xx_t * chip)
+{
+ int i;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert(ins != NULL, return);
+
+ down(&chip->spos_mutex);
+ for (i = 0; i < ins->nscb; ++i) {
+ if (ins->scbs[i].deleted) continue;
+
+ cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
+ }
+
+ kfree(ins->code.data);
+ vfree(ins->symbol_table.symbols);
+ kfree(ins->modules);
+ kfree(ins);
+ up(&chip->spos_mutex);
+}
+
+int cs46xx_dsp_load_module (cs46xx_t * chip, dsp_module_desc_t * module)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ segment_desc_t * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM);
+ segment_desc_t * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER);
+ segment_desc_t * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE);
+ u32 doffset, dsize;
+
+ if (ins->nmodules == DSP_MAX_MODULES - 1) {
+ snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
+ return -ENOMEM;
+ }
+
+ snd_printdd("dsp_spos: loading module %s into DSP\n", module->module_name);
+
+ if (ins->nmodules == 0) {
+ snd_printdd("dsp_spos: clearing parameter area\n");
+ snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
+ }
+
+ if (parameter == NULL) {
+ snd_printdd("dsp_spos: module got no parameter segment\n");
+ } else {
+ if (ins->nmodules > 0) {
+ snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n");
+ }
+
+ doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
+ dsize = parameter->size * 4;
+
+ snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
+ doffset,doffset + dsize);
+
+ if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
+ snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n");
+ return -EINVAL;
+ }
+ }
+
+ if (ins->nmodules == 0) {
+ snd_printdd("dsp_spos: clearing sample area\n");
+ snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
+ }
+
+ if (sample == NULL) {
+ snd_printdd("dsp_spos: module got no sample segment\n");
+ } else {
+ if (ins->nmodules > 0) {
+ snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n");
+ }
+
+ doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET);
+ dsize = sample->size * 4;
+
+ snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
+ doffset,doffset + dsize);
+
+ if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
+ snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
+ return -EINVAL;
+ }
+ }
+
+
+ if (ins->nmodules == 0) {
+ snd_printdd("dsp_spos: clearing code area\n");
+ snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
+ }
+
+ if (code == NULL) {
+ snd_printdd("dsp_spos: module got no code segment\n");
+ } else {
+ if (ins->code.offset + code->size > DSP_CODE_BYTE_SIZE) {
+ snd_printk(KERN_ERR "dsp_spos: no space available in DSP\n");
+ return -ENOMEM;
+ }
+
+ module->load_address = ins->code.offset;
+ module->overlay_begin_address = 0x000;
+
+ /* if module has a code segment it must have
+ symbol table */
+ snd_assert(module->symbol_table.symbols != NULL ,return -ENOMEM);
+ if (add_symbols(chip,module)) {
+ snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n");
+ return -ENOMEM;
+ }
+
+ doffset = (code->offset * 4 + ins->code.offset * 4 + DSP_CODE_BYTE_OFFSET);
+ dsize = code->size * 4;
+ snd_printdd("dsp_spos: downloading code to chip (%08x-%08x)\n",
+ doffset,doffset + dsize);
+
+ module->nfixups = shadow_and_reallocate_code(chip,code->data,code->size,module->overlay_begin_address);
+
+ if (snd_cs46xx_download (chip,(ins->code.data + ins->code.offset),doffset,dsize)) {
+ snd_printk(KERN_ERR "dsp_spos: failed to download code to DSP\n");
+ return -EINVAL;
+ }
+
+ ins->code.offset += code->size;
+ }
+
+ /* NOTE: module segments and symbol table must be
+ statically allocated. Case that module data is
+ not generated by the ospparser */
+ ins->modules[ins->nmodules] = *module;
+ ins->nmodules++;
+
+ return 0;
+}
+
+symbol_entry_t * cs46xx_dsp_lookup_symbol (cs46xx_t * chip, char * symbol_name, int symbol_type)
+{
+ int i;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) {
+
+ if (ins->symbol_table.symbols[i].deleted)
+ continue;
+
+ if (!strcmp(ins->symbol_table.symbols[i].symbol_name,symbol_name) &&
+ ins->symbol_table.symbols[i].symbol_type == symbol_type) {
+ return (ins->symbol_table.symbols + i);
+ }
+ }
+
+#if 0
+ printk ("dsp_spos: symbol <%s> type %02x not found\n",
+ symbol_name,symbol_type);
+#endif
+
+ return NULL;
+}
+
+
+static symbol_entry_t * cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip, u32 address, int symbol_type)
+{
+ int i;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) {
+
+ if (ins->symbol_table.symbols[i].deleted)
+ continue;
+
+ if (ins->symbol_table.symbols[i].address == address &&
+ ins->symbol_table.symbols[i].symbol_type == symbol_type) {
+ return (ins->symbol_table.symbols + i);
+ }
+ }
+
+
+ return NULL;
+}
+
+
+static void cs46xx_dsp_proc_symbol_table_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ cs46xx_t *chip = entry->private_data;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int i;
+
+ snd_iprintf(buffer, "SYMBOLS:\n");
+ for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) {
+ char *module_str = "system";
+
+ if (ins->symbol_table.symbols[i].deleted)
+ continue;
+
+ if (ins->symbol_table.symbols[i].module != NULL) {
+ module_str = ins->symbol_table.symbols[i].module->module_name;
+ }
+
+
+ snd_iprintf(buffer, "%04X <%02X> %s [%s]\n",
+ ins->symbol_table.symbols[i].address,
+ ins->symbol_table.symbols[i].symbol_type,
+ ins->symbol_table.symbols[i].symbol_name,
+ module_str);
+ }
+}
+
+
+static void cs46xx_dsp_proc_modules_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ cs46xx_t *chip = entry->private_data;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int i,j;
+
+ down(&chip->spos_mutex);
+ snd_iprintf(buffer, "MODULES:\n");
+ for ( i = 0; i < ins->nmodules; ++i ) {
+ snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name);
+ snd_iprintf(buffer, " %d symbols\n", ins->modules[i].symbol_table.nsymbols);
+ snd_iprintf(buffer, " %d fixups\n", ins->modules[i].nfixups);
+
+ for (j = 0; j < ins->modules[i].nsegments; ++ j) {
+ segment_desc_t * desc = (ins->modules[i].segments + j);
+ snd_iprintf(buffer, " segment %02x offset %08x size %08x\n",
+ desc->segment_type,desc->offset, desc->size);
+ }
+ }
+ up(&chip->spos_mutex);
+}
+
+static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ cs46xx_t *chip = entry->private_data;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int i,j,col;
+ void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
+
+ down(&chip->spos_mutex);
+ snd_iprintf(buffer, "TASK TREES:\n");
+ for ( i = 0; i < ins->ntask; ++i) {
+ snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name);
+
+ for (col = 0,j = 0;j < ins->tasks[i].size; j++,col++) {
+ u32 val;
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+ val = readl(dst + (ins->tasks[i].address + j) * sizeof(u32));
+ snd_iprintf(buffer,"%08x ",val);
+ }
+ }
+
+ snd_iprintf(buffer,"\n");
+ up(&chip->spos_mutex);
+}
+
+static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ cs46xx_t *chip = entry->private_data;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int i;
+
+ down(&chip->spos_mutex);
+ snd_iprintf(buffer, "SCB's:\n");
+ for ( i = 0; i < ins->nscb; ++i) {
+ if (ins->scbs[i].deleted)
+ continue;
+ snd_iprintf(buffer,"\n%04x %s:\n\n",ins->scbs[i].address,ins->scbs[i].scb_name);
+
+ if (ins->scbs[i].parent_scb_ptr != NULL) {
+ snd_iprintf(buffer,"parent [%s:%04x] ",
+ ins->scbs[i].parent_scb_ptr->scb_name,
+ ins->scbs[i].parent_scb_ptr->address);
+ } else snd_iprintf(buffer,"parent [none] ");
+
+ snd_iprintf(buffer,"sub_list_ptr [%s:%04x]\nnext_scb_ptr [%s:%04x] task_entry [%s:%04x]\n",
+ ins->scbs[i].sub_list_ptr->scb_name,
+ ins->scbs[i].sub_list_ptr->address,
+ ins->scbs[i].next_scb_ptr->scb_name,
+ ins->scbs[i].next_scb_ptr->address,
+ ins->scbs[i].task_entry->symbol_name,
+ ins->scbs[i].task_entry->address);
+ }
+
+ snd_iprintf(buffer,"\n");
+ up(&chip->spos_mutex);
+}
+
+static void cs46xx_dsp_proc_parameter_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ cs46xx_t *chip = entry->private_data;
+ /*dsp_spos_instance_t * ins = chip->dsp_spos_instance; */
+ unsigned int i,col = 0;
+ void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
+ symbol_entry_t * symbol;
+
+ for (i = 0;i < DSP_PARAMETER_BYTE_SIZE; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if ( (symbol = cs46xx_dsp_lookup_symbol_addr (chip,i / sizeof(u32), SYMBOL_PARAMETER)) != NULL) {
+ col = 0;
+ snd_iprintf (buffer,"\n%s:\n",symbol->symbol_name);
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ", i / (unsigned int)sizeof(u32));
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+}
+
+static void cs46xx_dsp_proc_sample_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ cs46xx_t *chip = entry->private_data;
+ int i,col = 0;
+ void __iomem *dst = chip->region.idx[2].remap_addr;
+
+ snd_iprintf(buffer,"PCMREADER:\n");
+ for (i = PCM_READER_BUF1;i < PCM_READER_BUF1 + 0x30; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+
+ snd_iprintf(buffer,"\nMIX_SAMPLE_BUF1:\n");
+
+ col = 0;
+ for (i = MIX_SAMPLE_BUF1;i < MIX_SAMPLE_BUF1 + 0x40; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+
+ snd_iprintf(buffer,"\nSRC_TASK_SCB1:\n");
+ col = 0;
+ for (i = 0x2480 ; i < 0x2480 + 0x40 ; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+
+
+ snd_iprintf(buffer,"\nSPDIFO_BUFFER:\n");
+ col = 0;
+ for (i = SPDIFO_IP_OUTPUT_BUFFER1;i < SPDIFO_IP_OUTPUT_BUFFER1 + 0x30; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+
+ snd_iprintf(buffer,"\n...\n");
+ col = 0;
+
+ for (i = SPDIFO_IP_OUTPUT_BUFFER1+0xD0;i < SPDIFO_IP_OUTPUT_BUFFER1 + 0x110; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+
+
+ snd_iprintf(buffer,"\nOUTPUT_SNOOP:\n");
+ col = 0;
+ for (i = OUTPUT_SNOOP_BUFFER;i < OUTPUT_SNOOP_BUFFER + 0x40; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+
+ snd_iprintf(buffer,"\nCODEC_INPUT_BUF1: \n");
+ col = 0;
+ for (i = CODEC_INPUT_BUF1;i < CODEC_INPUT_BUF1 + 0x40; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+#if 0
+ snd_iprintf(buffer,"\nWRITE_BACK_BUF1: \n");
+ col = 0;
+ for (i = WRITE_BACK_BUF1;i < WRITE_BACK_BUF1 + 0x40; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+#endif
+
+ snd_iprintf(buffer,"\nSPDIFI_IP_OUTPUT_BUFFER1: \n");
+ col = 0;
+ for (i = SPDIFI_IP_OUTPUT_BUFFER1;i < SPDIFI_IP_OUTPUT_BUFFER1 + 0x80; i += sizeof(u32),col ++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+
+ if (col == 0) {
+ snd_iprintf(buffer, "%04X ",i);
+ }
+
+ snd_iprintf(buffer,"%08X ",readl(dst + i));
+ }
+ snd_iprintf(buffer,"\n");
+}
+
+int cs46xx_dsp_proc_init (snd_card_t * card, cs46xx_t *chip)
+{
+ snd_info_entry_t *entry;
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int i;
+
+ ins->snd_card = card;
+
+ if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ entry->c.text.read_size = 512;
+
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+
+ ins->proc_dsp_dir = entry;
+
+ if (!ins->proc_dsp_dir)
+ return -ENOMEM;
+
+ if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = chip;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 512;
+ entry->c.text.read = cs46xx_dsp_proc_symbol_table_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ins->proc_sym_info_entry = entry;
+
+ if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = chip;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 512;
+ entry->c.text.read = cs46xx_dsp_proc_modules_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ins->proc_modules_info_entry = entry;
+
+ if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = chip;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 512;
+ entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ins->proc_parameter_dump_info_entry = entry;
+
+ if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = chip;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 512;
+ entry->c.text.read = cs46xx_dsp_proc_sample_dump_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ins->proc_sample_dump_info_entry = entry;
+
+ if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = chip;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 512;
+ entry->c.text.read = cs46xx_dsp_proc_task_tree_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ins->proc_task_info_entry = entry;
+
+ if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = chip;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 1024;
+ entry->c.text.read = cs46xx_dsp_proc_scb_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ ins->proc_scb_info_entry = entry;
+
+ down(&chip->spos_mutex);
+ /* register/update SCB's entries on proc */
+ for (i = 0; i < ins->nscb; ++i) {
+ if (ins->scbs[i].deleted) continue;
+
+ cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i));
+ }
+ up(&chip->spos_mutex);
+
+ return 0;
+}
+
+int cs46xx_dsp_proc_done (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int i;
+
+ if (ins->proc_sym_info_entry) {
+ snd_info_unregister(ins->proc_sym_info_entry);
+ ins->proc_sym_info_entry = NULL;
+ }
+
+ if (ins->proc_modules_info_entry) {
+ snd_info_unregister(ins->proc_modules_info_entry);
+ ins->proc_modules_info_entry = NULL;
+ }
+
+ if (ins->proc_parameter_dump_info_entry) {
+ snd_info_unregister(ins->proc_parameter_dump_info_entry);
+ ins->proc_parameter_dump_info_entry = NULL;
+ }
+
+ if (ins->proc_sample_dump_info_entry) {
+ snd_info_unregister(ins->proc_sample_dump_info_entry);
+ ins->proc_sample_dump_info_entry = NULL;
+ }
+
+ if (ins->proc_scb_info_entry) {
+ snd_info_unregister(ins->proc_scb_info_entry);
+ ins->proc_scb_info_entry = NULL;
+ }
+
+ if (ins->proc_task_info_entry) {
+ snd_info_unregister(ins->proc_task_info_entry);
+ ins->proc_task_info_entry = NULL;
+ }
+
+ down(&chip->spos_mutex);
+ for (i = 0; i < ins->nscb; ++i) {
+ if (ins->scbs[i].deleted) continue;
+ cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
+ }
+ up(&chip->spos_mutex);
+
+ if (ins->proc_dsp_dir) {
+ snd_info_unregister (ins->proc_dsp_dir);
+ ins->proc_dsp_dir = NULL;
+ }
+
+ return 0;
+}
+
+static int debug_tree;
+static void _dsp_create_task_tree (cs46xx_t *chip,u32 * task_data, u32 dest, int size)
+{
+ void __iomem *spdst = chip->region.idx[1].remap_addr +
+ DSP_PARAMETER_BYTE_OFFSET + dest * sizeof(u32);
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (debug_tree) printk ("addr %p, val %08x\n",spdst,task_data[i]);
+ writel(task_data[i],spdst);
+ spdst += sizeof(u32);
+ }
+}
+
+static int debug_scb;
+static void _dsp_create_scb (cs46xx_t *chip,u32 * scb_data, u32 dest)
+{
+ void __iomem *spdst = chip->region.idx[1].remap_addr +
+ DSP_PARAMETER_BYTE_OFFSET + dest * sizeof(u32);
+ int i;
+
+ for (i = 0; i < 0x10; ++i) {
+ if (debug_scb) printk ("addr %p, val %08x\n",spdst,scb_data[i]);
+ writel(scb_data[i],spdst);
+ spdst += sizeof(u32);
+ }
+}
+
+static int find_free_scb_index (dsp_spos_instance_t * ins)
+{
+ int index = ins->nscb, i;
+
+ for (i = ins->scb_highest_frag_index; i < ins->nscb; ++i) {
+ if (ins->scbs[i].deleted) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+}
+
+static dsp_scb_descriptor_t * _map_scb (cs46xx_t *chip,char * name,u32 dest)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * desc = NULL;
+ int index;
+
+ if (ins->nscb == DSP_MAX_SCB_DESC - 1) {
+ snd_printk(KERN_ERR "dsp_spos: got no place for other SCB\n");
+ return NULL;
+ }
+
+ index = find_free_scb_index (ins);
+
+ strcpy(ins->scbs[index].scb_name, name);
+ ins->scbs[index].address = dest;
+ ins->scbs[index].index = index;
+ ins->scbs[index].proc_info = NULL;
+ ins->scbs[index].ref_count = 1;
+ ins->scbs[index].deleted = 0;
+ spin_lock_init(&ins->scbs[index].lock);
+
+ desc = (ins->scbs + index);
+ ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
+
+ if (index > ins->scb_highest_frag_index)
+ ins->scb_highest_frag_index = index;
+
+ if (index == ins->nscb)
+ ins->nscb++;
+
+ return desc;
+}
+
+static dsp_task_descriptor_t * _map_task_tree (cs46xx_t *chip,char * name,u32 dest,u32 size)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_task_descriptor_t * desc = NULL;
+
+ if (ins->ntask == DSP_MAX_TASK_DESC - 1) {
+ snd_printk(KERN_ERR "dsp_spos: got no place for other TASK\n");
+ return NULL;
+ }
+
+ strcpy(ins->tasks[ins->ntask].task_name,name);
+ ins->tasks[ins->ntask].address = dest;
+ ins->tasks[ins->ntask].size = size;
+
+ /* quick find in list */
+ ins->tasks[ins->ntask].index = ins->ntask;
+ desc = (ins->tasks + ins->ntask);
+ ins->ntask++;
+
+ add_symbol (chip,name,dest,SYMBOL_PARAMETER);
+ return desc;
+}
+
+dsp_scb_descriptor_t * cs46xx_dsp_create_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest)
+{
+ dsp_scb_descriptor_t * desc;
+
+ desc = _map_scb (chip,name,dest);
+ if (desc) {
+ _dsp_create_scb(chip,scb_data,dest);
+ } else {
+ snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
+ }
+
+ return desc;
+}
+
+
+static dsp_task_descriptor_t * cs46xx_dsp_create_task_tree (cs46xx_t *chip,char * name, u32 * task_data,u32 dest,int size)
+{
+ dsp_task_descriptor_t * desc;
+
+ desc = _map_task_tree (chip,name,dest,size);
+ if (desc) {
+ _dsp_create_task_tree(chip,task_data,dest,size);
+ } else {
+ snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
+ }
+
+ return desc;
+}
+
+int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ symbol_entry_t * fg_task_tree_header_code;
+ symbol_entry_t * task_tree_header_code;
+ symbol_entry_t * task_tree_thread;
+ symbol_entry_t * null_algorithm;
+ symbol_entry_t * magic_snoop_task;
+
+ dsp_scb_descriptor_t * timing_master_scb;
+ dsp_scb_descriptor_t * codec_out_scb;
+ dsp_scb_descriptor_t * codec_in_scb;
+ dsp_scb_descriptor_t * src_task_scb;
+ dsp_scb_descriptor_t * master_mix_scb;
+ dsp_scb_descriptor_t * rear_mix_scb;
+ dsp_scb_descriptor_t * record_mix_scb;
+ dsp_scb_descriptor_t * write_back_scb;
+ dsp_scb_descriptor_t * vari_decimate_scb;
+ dsp_scb_descriptor_t * rear_codec_out_scb;
+ dsp_scb_descriptor_t * clfe_codec_out_scb;
+ dsp_scb_descriptor_t * magic_snoop_scb;
+
+ int fifo_addr,fifo_span,valid_slots;
+
+ static spos_control_block_t sposcb = {
+ /* 0 */ HFG_TREE_SCB,HFG_STACK,
+ /* 1 */ SPOSCB_ADDR,BG_TREE_SCB_ADDR,
+ /* 2 */ DSP_SPOS_DC,0,
+ /* 3 */ DSP_SPOS_DC,DSP_SPOS_DC,
+ /* 4 */ 0,0,
+ /* 5 */ DSP_SPOS_UU,0,
+ /* 6 */ FG_TASK_HEADER_ADDR,0,
+ /* 7 */ 0,0,
+ /* 8 */ DSP_SPOS_UU,DSP_SPOS_DC,
+ /* 9 */ 0,
+ /* A */ 0,HFG_FIRST_EXECUTE_MODE,
+ /* B */ DSP_SPOS_UU,DSP_SPOS_UU,
+ /* C */ DSP_SPOS_DC_DC,
+ /* D */ DSP_SPOS_DC_DC,
+ /* E */ DSP_SPOS_DC_DC,
+ /* F */ DSP_SPOS_DC_DC
+ };
+
+ cs46xx_dsp_create_task_tree(chip, "sposCB", (u32 *)&sposcb, SPOSCB_ADDR, 0x10);
+
+ null_algorithm = cs46xx_dsp_lookup_symbol(chip, "NULLALGORITHM", SYMBOL_CODE);
+ if (null_algorithm == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+ return -EIO;
+ }
+
+ fg_task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "FGTASKTREEHEADERCODE", SYMBOL_CODE);
+ if (fg_task_tree_header_code == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol FGTASKTREEHEADERCODE not found\n");
+ return -EIO;
+ }
+
+ task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "TASKTREEHEADERCODE", SYMBOL_CODE);
+ if (task_tree_header_code == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol TASKTREEHEADERCODE not found\n");
+ return -EIO;
+ }
+
+ task_tree_thread = cs46xx_dsp_lookup_symbol(chip, "TASKTREETHREAD", SYMBOL_CODE);
+ if (task_tree_thread == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol TASKTREETHREAD not found\n");
+ return -EIO;
+ }
+
+ magic_snoop_task = cs46xx_dsp_lookup_symbol(chip, "MAGICSNOOPTASK", SYMBOL_CODE);
+ if (magic_snoop_task == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol MAGICSNOOPTASK not found\n");
+ return -EIO;
+ }
+
+ {
+ /* create the null SCB */
+ static generic_scb_t null_scb = {
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 },
+ NULL_SCB_ADDR, NULL_SCB_ADDR,
+ 0, 0, 0, 0, 0,
+ {
+ 0,0,
+ 0,0,
+ }
+ };
+
+ null_scb.entry_point = null_algorithm->address;
+ ins->the_null_scb = cs46xx_dsp_create_scb(chip, "nullSCB", (u32 *)&null_scb, NULL_SCB_ADDR);
+ ins->the_null_scb->task_entry = null_algorithm;
+ ins->the_null_scb->sub_list_ptr = ins->the_null_scb;
+ ins->the_null_scb->next_scb_ptr = ins->the_null_scb;
+ ins->the_null_scb->parent_scb_ptr = NULL;
+ cs46xx_dsp_proc_register_scb_desc (chip,ins->the_null_scb);
+ }
+
+ {
+ /* setup foreground task tree */
+ static task_tree_control_block_t fg_task_tree_hdr = {
+ { FG_TASK_HEADER_ADDR | (DSP_SPOS_DC << 0x10),
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC_DC,
+ 0x0000,DSP_SPOS_DC,
+ DSP_SPOS_DC, DSP_SPOS_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC },
+
+ {
+ BG_TREE_SCB_ADDR,TIMINGMASTER_SCB_ADDR,
+ 0,
+ FG_TASK_HEADER_ADDR + TCBData,
+ },
+
+ {
+ 4,0,
+ 1,0,
+ 2,SPOSCB_ADDR + HFGFlags,
+ 0,0,
+ FG_TASK_HEADER_ADDR + TCBContextBlk,FG_STACK
+ },
+
+ {
+ DSP_SPOS_DC,0,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_UU,1,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC
+ },
+ {
+ FG_INTERVAL_TIMER_PERIOD,DSP_SPOS_UU,
+ 0,0
+ }
+ };
+
+ fg_task_tree_hdr.links.entry_point = fg_task_tree_header_code->address;
+ fg_task_tree_hdr.context_blk.stack0 = task_tree_thread->address;
+ cs46xx_dsp_create_task_tree(chip,"FGtaskTreeHdr",(u32 *)&fg_task_tree_hdr,FG_TASK_HEADER_ADDR,0x35);
+ }
+
+
+ {
+ /* setup foreground task tree */
+ static task_tree_control_block_t bg_task_tree_hdr = {
+ { DSP_SPOS_DC_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC, DSP_SPOS_DC,
+ DSP_SPOS_DC, DSP_SPOS_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC },
+
+ {
+ NULL_SCB_ADDR,NULL_SCB_ADDR, /* Set up the background to do nothing */
+ 0,
+ BG_TREE_SCB_ADDR + TCBData,
+ },
+
+ {
+ 9999,0,
+ 0,1,
+ 0,SPOSCB_ADDR + HFGFlags,
+ 0,0,
+ BG_TREE_SCB_ADDR + TCBContextBlk,BG_STACK
+ },
+
+ {
+ DSP_SPOS_DC,0,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DC,DSP_SPOS_DC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_UU,1,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC,
+ DSP_SPOS_DCDC
+ },
+ {
+ BG_INTERVAL_TIMER_PERIOD,DSP_SPOS_UU,
+ 0,0
+ }
+ };
+
+ bg_task_tree_hdr.links.entry_point = task_tree_header_code->address;
+ bg_task_tree_hdr.context_blk.stack0 = task_tree_thread->address;
+ cs46xx_dsp_create_task_tree(chip,"BGtaskTreeHdr",(u32 *)&bg_task_tree_hdr,BG_TREE_SCB_ADDR,0x35);
+ }
+
+ /* create timing master SCB */
+ timing_master_scb = cs46xx_dsp_create_timing_master_scb(chip);
+
+ /* create the CODEC output task */
+ codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_I",0x0010,0x0000,
+ MASTERMIX_SCB_ADDR,
+ CODECOUT_SCB_ADDR,timing_master_scb,
+ SCB_ON_PARENT_SUBLIST_SCB);
+
+ if (!codec_out_scb) goto _fail_end;
+ /* create the master mix SCB */
+ master_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"MasterMixSCB",
+ MIX_SAMPLE_BUF1,MASTERMIX_SCB_ADDR,
+ codec_out_scb,
+ SCB_ON_PARENT_SUBLIST_SCB);
+ ins->master_mix_scb = master_mix_scb;
+
+ if (!master_mix_scb) goto _fail_end;
+
+ /* create codec in */
+ codec_in_scb = cs46xx_dsp_create_codec_in_scb(chip,"CodecInSCB",0x0010,0x00A0,
+ CODEC_INPUT_BUF1,
+ CODECIN_SCB_ADDR,codec_out_scb,
+ SCB_ON_PARENT_NEXT_SCB);
+ if (!codec_in_scb) goto _fail_end;
+ ins->codec_in_scb = codec_in_scb;
+
+ /* create write back scb */
+ write_back_scb = cs46xx_dsp_create_mix_to_ostream_scb(chip,"WriteBackSCB",
+ WRITE_BACK_BUF1,WRITE_BACK_SPB,
+ WRITEBACK_SCB_ADDR,
+ timing_master_scb,
+ SCB_ON_PARENT_NEXT_SCB);
+ if (!write_back_scb) goto _fail_end;
+
+ {
+ static mix2_ostream_spb_t mix2_ostream_spb = {
+ 0x00020000,
+ 0x0000ffff
+ };
+
+ /* dirty hack ... */
+ _dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2);
+ }
+
+ /* input sample converter */
+ vari_decimate_scb = cs46xx_dsp_create_vari_decimate_scb(chip,"VariDecimateSCB",
+ VARI_DECIMATE_BUF0,
+ VARI_DECIMATE_BUF1,
+ VARIDECIMATE_SCB_ADDR,
+ write_back_scb,
+ SCB_ON_PARENT_SUBLIST_SCB);
+ if (!vari_decimate_scb) goto _fail_end;
+
+ /* create the record mixer SCB */
+ record_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RecordMixerSCB",
+ MIX_SAMPLE_BUF2,
+ RECORD_MIXER_SCB_ADDR,
+ vari_decimate_scb,
+ SCB_ON_PARENT_SUBLIST_SCB);
+ ins->record_mixer_scb = record_mix_scb;
+
+ if (!record_mix_scb) goto _fail_end;
+
+ valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
+
+ snd_assert (chip->nr_ac97_codecs == 1 || chip->nr_ac97_codecs == 2);
+
+ if (chip->nr_ac97_codecs == 1) {
+ /* output on slot 5 and 11
+ on primary CODEC */
+ fifo_addr = 0x20;
+ fifo_span = 0x60;
+
+ /* enable slot 5 and 11 */
+ valid_slots |= ACOSV_SLV5 | ACOSV_SLV11;
+ } else {
+ /* output on slot 7 and 8
+ on secondary CODEC */
+ fifo_addr = 0x40;
+ fifo_span = 0x10;
+
+ /* enable slot 7 and 8 */
+ valid_slots |= ACOSV_SLV7 | ACOSV_SLV8;
+ }
+ /* create CODEC tasklet for rear speakers output*/
+ rear_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_Rear",fifo_span,fifo_addr,
+ REAR_MIXER_SCB_ADDR,
+ REAR_CODECOUT_SCB_ADDR,codec_in_scb,
+ SCB_ON_PARENT_NEXT_SCB);
+ if (!rear_codec_out_scb) goto _fail_end;
+
+
+ /* create the rear PCM channel mixer SCB */
+ rear_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RearMixerSCB",
+ MIX_SAMPLE_BUF3,
+ REAR_MIXER_SCB_ADDR,
+ rear_codec_out_scb,
+ SCB_ON_PARENT_SUBLIST_SCB);
+ ins->rear_mix_scb = rear_mix_scb;
+ if (!rear_mix_scb) goto _fail_end;
+
+ if (chip->nr_ac97_codecs == 2) {
+ /* create CODEC tasklet for rear Center/LFE output
+ slot 6 and 9 on seconadry CODEC */
+ clfe_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_CLFE",0x0030,0x0030,
+ CLFE_MIXER_SCB_ADDR,
+ CLFE_CODEC_SCB_ADDR,
+ rear_codec_out_scb,
+ SCB_ON_PARENT_NEXT_SCB);
+ if (!clfe_codec_out_scb) goto _fail_end;
+
+
+ /* create the rear PCM channel mixer SCB */
+ ins->center_lfe_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"CLFEMixerSCB",
+ MIX_SAMPLE_BUF4,
+ CLFE_MIXER_SCB_ADDR,
+ clfe_codec_out_scb,
+ SCB_ON_PARENT_SUBLIST_SCB);
+ if (!ins->center_lfe_mix_scb) goto _fail_end;
+
+ /* enable slot 6 and 9 */
+ valid_slots |= ACOSV_SLV6 | ACOSV_SLV9;
+ } else {
+ clfe_codec_out_scb = rear_codec_out_scb;
+ ins->center_lfe_mix_scb = rear_mix_scb;
+ }
+
+ /* enable slots depending on CODEC configuration */
+ snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
+
+ /* the magic snooper */
+ magic_snoop_scb = cs46xx_dsp_create_magic_snoop_scb (chip,"MagicSnoopSCB_I",OUTPUTSNOOP_SCB_ADDR,
+ OUTPUT_SNOOP_BUFFER,
+ codec_out_scb,
+ clfe_codec_out_scb,
+ SCB_ON_PARENT_NEXT_SCB);
+
+
+ if (!magic_snoop_scb) goto _fail_end;
+ ins->ref_snoop_scb = magic_snoop_scb;
+
+ /* SP IO access */
+ if (!cs46xx_dsp_create_spio_write_scb(chip,"SPIOWriteSCB",SPIOWRITE_SCB_ADDR,
+ magic_snoop_scb,
+ SCB_ON_PARENT_NEXT_SCB))
+ goto _fail_end;
+
+ /* SPDIF input sampel rate converter */
+ src_task_scb = cs46xx_dsp_create_src_task_scb(chip,"SrcTaskSCB_SPDIFI",
+ ins->spdif_in_sample_rate,
+ SRC_OUTPUT_BUF1,
+ SRC_DELAY_BUF1,SRCTASK_SCB_ADDR,
+ master_mix_scb,
+ SCB_ON_PARENT_SUBLIST_SCB,1);
+
+ if (!src_task_scb) goto _fail_end;
+ cs46xx_src_unlink(chip,src_task_scb);
+
+ /* NOTE: when we now how to detect the SPDIF input
+ sample rate we will use this SRC to adjust it */
+ ins->spdif_in_src = src_task_scb;
+
+ cs46xx_dsp_async_init(chip,timing_master_scb);
+ return 0;
+
+ _fail_end:
+ snd_printk(KERN_ERR "dsp_spos: failed to setup SCB's in DSP\n");
+ return -EINVAL;
+}
+
+static int cs46xx_dsp_async_init (cs46xx_t *chip, dsp_scb_descriptor_t * fg_entry)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ symbol_entry_t * s16_async_codec_input_task;
+ symbol_entry_t * spdifo_task;
+ symbol_entry_t * spdifi_task;
+ dsp_scb_descriptor_t * spdifi_scb_desc,* spdifo_scb_desc,* async_codec_scb_desc;
+
+ s16_async_codec_input_task = cs46xx_dsp_lookup_symbol(chip, "S16_ASYNCCODECINPUTTASK", SYMBOL_CODE);
+ if (s16_async_codec_input_task == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol S16_ASYNCCODECINPUTTASK not found\n");
+ return -EIO;
+ }
+ spdifo_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFOTASK", SYMBOL_CODE);
+ if (spdifo_task == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol SPDIFOTASK not found\n");
+ return -EIO;
+ }
+
+ spdifi_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFITASK", SYMBOL_CODE);
+ if (spdifi_task == NULL) {
+ snd_printk(KERN_ERR "dsp_spos: symbol SPDIFITASK not found\n");
+ return -EIO;
+ }
+
+ {
+ /* 0xBC0 */
+ spdifoscb_t spdifo_scb = {
+ /* 0 */ DSP_SPOS_UUUU,
+ {
+ /* 1 */ 0xb0,
+ /* 2 */ 0,
+ /* 3 */ 0,
+ /* 4 */ 0,
+ },
+ /* NOTE: the SPDIF output task read samples in mono
+ format, the AsynchFGTxSCB task writes to buffer
+ in stereo format
+ */
+ /* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_256,
+ /* 6 */ ( SPDIFO_IP_OUTPUT_BUFFER1 << 0x10 ) | 0xFFFC,
+ /* 7 */ 0,0,
+ /* 8 */ 0,
+ /* 9 */ FG_TASK_HEADER_ADDR, NULL_SCB_ADDR,
+ /* A */ spdifo_task->address,
+ SPDIFO_SCB_INST + SPDIFOFIFOPointer,
+ {
+ /* B */ 0x0040, /*DSP_SPOS_UUUU,*/
+ /* C */ 0x20ff, /*DSP_SPOS_UUUU,*/
+ },
+ /* D */ 0x804c,0, /* SPDIFOFIFOPointer:SPDIFOStatRegAddr; */
+ /* E */ 0x0108,0x0001, /* SPDIFOStMoFormat:SPDIFOFIFOBaseAddr; */
+ /* F */ DSP_SPOS_UUUU /* SPDIFOFree; */
+ };
+
+ /* 0xBB0 */
+ spdifiscb_t spdifi_scb = {
+ /* 0 */ DSP_SPOS_UULO,DSP_SPOS_UUHI,
+ /* 1 */ 0,
+ /* 2 */ 0,
+ /* 3 */ 1,4000, /* SPDIFICountLimit SPDIFICount */
+ /* 4 */ DSP_SPOS_UUUU, /* SPDIFIStatusData */
+ /* 5 */ 0,DSP_SPOS_UUHI, /* StatusData, Free4 */
+ /* 6 */ DSP_SPOS_UUUU, /* Free3 */
+ /* 7 */ DSP_SPOS_UU,DSP_SPOS_DC, /* Free2 BitCount*/
+ /* 8 */ DSP_SPOS_UUUU, /* TempStatus */
+ /* 9 */ SPDIFO_SCB_INST, NULL_SCB_ADDR,
+ /* A */ spdifi_task->address,
+ SPDIFI_SCB_INST + SPDIFIFIFOPointer,
+ /* NOTE: The SPDIF input task write the sample in mono
+ format from the HW FIFO, the AsynchFGRxSCB task reads
+ them in stereo
+ */
+ /* B */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_128,
+ /* C */ (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,
+ /* D */ 0x8048,0,
+ /* E */ 0x01f0,0x0001,
+ /* F */ DSP_SPOS_UUUU /* SPDIN_STATUS monitor */
+ };
+
+ /* 0xBA0 */
+ async_codec_input_scb_t async_codec_input_scb = {
+ /* 0 */ DSP_SPOS_UUUU,
+ /* 1 */ 0,
+ /* 2 */ 0,
+ /* 3 */ 1,4000,
+ /* 4 */ 0x0118,0x0001,
+ /* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_64,
+ /* 6 */ (ASYNC_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,
+ /* 7 */ DSP_SPOS_UU,0x3,
+ /* 8 */ DSP_SPOS_UUUU,
+ /* 9 */ SPDIFI_SCB_INST,NULL_SCB_ADDR,
+ /* A */ s16_async_codec_input_task->address,
+ HFG_TREE_SCB + AsyncCIOFIFOPointer,
+
+ /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
+ /* C */ (ASYNC_IP_OUTPUT_BUFFER1 << 0x10), /*(ASYNC_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,*/
+
+#ifdef UseASER1Input
+ /* short AsyncCIFIFOPointer:AsyncCIStatRegAddr;
+ Init. 0000:8042: for ASER1
+ 0000:8044: for ASER2 */
+ /* D */ 0x8042,0,
+
+ /* short AsyncCIStMoFormat:AsyncCIFIFOBaseAddr;
+ Init 1 stero:8050 ASER1
+ Init 0 mono:8070 ASER2
+ Init 1 Stereo : 0100 ASER1 (Set by script) */
+ /* E */ 0x0100,0x0001,
+
+#endif
+
+#ifdef UseASER2Input
+ /* short AsyncCIFIFOPointer:AsyncCIStatRegAddr;
+ Init. 0000:8042: for ASER1
+ 0000:8044: for ASER2 */
+ /* D */ 0x8044,0,
+
+ /* short AsyncCIStMoFormat:AsyncCIFIFOBaseAddr;
+ Init 1 stero:8050 ASER1
+ Init 0 mono:8070 ASER2
+ Init 1 Stereo : 0100 ASER1 (Set by script) */
+ /* E */ 0x0110,0x0001,
+
+#endif
+
+ /* short AsyncCIOutputBufModulo:AsyncCIFree;
+ AsyncCIOutputBufModulo: The modulo size for
+ the output buffer of this task */
+ /* F */ 0, /* DSP_SPOS_UUUU */
+ };
+
+ spdifo_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFOSCB",(u32 *)&spdifo_scb,SPDIFO_SCB_INST);
+
+ snd_assert(spdifo_scb_desc, return -EIO);
+ spdifi_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFISCB",(u32 *)&spdifi_scb,SPDIFI_SCB_INST);
+ snd_assert(spdifi_scb_desc, return -EIO);
+ async_codec_scb_desc = cs46xx_dsp_create_scb(chip,"AsynCodecInputSCB",(u32 *)&async_codec_input_scb, HFG_TREE_SCB);
+ snd_assert(async_codec_scb_desc, return -EIO);
+
+ async_codec_scb_desc->parent_scb_ptr = NULL;
+ async_codec_scb_desc->next_scb_ptr = spdifi_scb_desc;
+ async_codec_scb_desc->sub_list_ptr = ins->the_null_scb;
+ async_codec_scb_desc->task_entry = s16_async_codec_input_task;
+
+ spdifi_scb_desc->parent_scb_ptr = async_codec_scb_desc;
+ spdifi_scb_desc->next_scb_ptr = spdifo_scb_desc;
+ spdifi_scb_desc->sub_list_ptr = ins->the_null_scb;
+ spdifi_scb_desc->task_entry = spdifi_task;
+
+ spdifo_scb_desc->parent_scb_ptr = spdifi_scb_desc;
+ spdifo_scb_desc->next_scb_ptr = fg_entry;
+ spdifo_scb_desc->sub_list_ptr = ins->the_null_scb;
+ spdifo_scb_desc->task_entry = spdifo_task;
+
+ /* this one is faked, as the parnet of SPDIFO task
+ is the FG task tree */
+ fg_entry->parent_scb_ptr = spdifo_scb_desc;
+
+ /* for proc fs */
+ cs46xx_dsp_proc_register_scb_desc (chip,spdifo_scb_desc);
+ cs46xx_dsp_proc_register_scb_desc (chip,spdifi_scb_desc);
+ cs46xx_dsp_proc_register_scb_desc (chip,async_codec_scb_desc);
+
+ /* Async MASTER ENABLE, affects both SPDIF input and output */
+ snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x1 );
+ }
+
+ return 0;
+}
+
+
+static void cs46xx_dsp_disable_spdif_hw (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ /* set SPDIF output FIFO slot */
+ snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, 0);
+
+ /* SPDIF output MASTER ENABLE */
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0);
+
+ /* right and left validate bit */
+ /*cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);*/
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, 0x0);
+
+ /* clear fifo pointer */
+ cs46xx_poke_via_dsp (chip,SP_SPDIN_FIFOPTR, 0x0);
+
+ /* monitor state */
+ ins->spdif_status_out &= ~DSP_SPDIF_STATUS_HW_ENABLED;
+}
+
+int cs46xx_dsp_enable_spdif_hw (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ /* if hw-ctrl already enabled, turn off to reset logic ... */
+ cs46xx_dsp_disable_spdif_hw (chip);
+ udelay(50);
+
+ /* set SPDIF output FIFO slot */
+ snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, ( 0x8000 | ((SP_SPDOUT_FIFO >> 4) << 4) ));
+
+ /* SPDIF output MASTER ENABLE */
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0x80000000);
+
+ /* right and left validate bit */
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);
+
+ /* monitor state */
+ ins->spdif_status_out |= DSP_SPDIF_STATUS_HW_ENABLED;
+
+ return 0;
+}
+
+int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ /* turn on amplifier */
+ chip->active_ctrl(chip, 1);
+ chip->amplifier_ctrl(chip, 1);
+
+ snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL);
+ snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+
+ down(&chip->spos_mutex);
+
+ if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED) ) {
+ /* time countdown enable */
+ cs46xx_poke_via_dsp (chip,SP_ASER_COUNTDOWN, 0x80000005);
+ /* NOTE: 80000005 value is just magic. With all values
+ that I've tested this one seem to give the best result.
+ Got no explication why. (Benny) */
+
+ /* SPDIF input MASTER ENABLE */
+ cs46xx_poke_via_dsp (chip,SP_SPDIN_CONTROL, 0x800003ff);
+
+ ins->spdif_status_out |= DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED;
+ }
+
+ /* create and start the asynchronous receiver SCB */
+ ins->asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB",
+ ASYNCRX_SCB_ADDR,
+ SPDIFI_SCB_INST,
+ SPDIFI_IP_OUTPUT_BUFFER1,
+ ins->spdif_in_src,
+ SCB_ON_PARENT_SUBLIST_SCB);
+
+ spin_lock_irq(&chip->reg_lock);
+
+ /* reset SPDIF input sample buffer pointer */
+ /*snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2,
+ (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC);*/
+
+ /* reset FIFO ptr */
+ /*cs46xx_poke_via_dsp (chip,SP_SPDIN_FIFOPTR, 0x0);*/
+ cs46xx_src_link(chip,ins->spdif_in_src);
+
+ /* unmute SRC volume */
+ cs46xx_dsp_scb_set_volume (chip,ins->spdif_in_src,0x7fff,0x7fff);
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ /* set SPDIF input sample rate and unmute
+ NOTE: only 48khz support for SPDIF input this time */
+ /* cs46xx_dsp_set_src_sample_rate(chip,ins->spdif_in_src,48000); */
+
+ /* monitor state */
+ ins->spdif_status_in = 1;
+ up(&chip->spos_mutex);
+
+ return 0;
+}
+
+int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL);
+ snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+
+ down(&chip->spos_mutex);
+
+ /* Remove the asynchronous receiver SCB */
+ cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb);
+ ins->asynch_rx_scb = NULL;
+
+ cs46xx_src_unlink(chip,ins->spdif_in_src);
+
+ /* monitor state */
+ ins->spdif_status_in = 0;
+ up(&chip->spos_mutex);
+
+ /* restore amplifier */
+ chip->active_ctrl(chip, -1);
+ chip->amplifier_ctrl(chip, -1);
+
+ return 0;
+}
+
+int cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert (ins->pcm_input == NULL,return -EINVAL);
+ snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL);
+
+ down(&chip->spos_mutex);
+ ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
+ "PCMSerialInput_Wave");
+ up(&chip->spos_mutex);
+
+ return 0;
+}
+
+int cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert (ins->pcm_input != NULL,return -EINVAL);
+
+ down(&chip->spos_mutex);
+ cs46xx_dsp_remove_scb (chip,ins->pcm_input);
+ ins->pcm_input = NULL;
+ up(&chip->spos_mutex);
+
+ return 0;
+}
+
+int cs46xx_dsp_enable_adc_capture (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert (ins->adc_input == NULL,return -EINVAL);
+ snd_assert (ins->codec_in_scb != NULL,return -EINVAL);
+
+ down(&chip->spos_mutex);
+ ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
+ "PCMSerialInput_ADC");
+ up(&chip->spos_mutex);
+
+ return 0;
+}
+
+int cs46xx_dsp_disable_adc_capture (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert (ins->adc_input != NULL,return -EINVAL);
+
+ down(&chip->spos_mutex);
+ cs46xx_dsp_remove_scb (chip,ins->adc_input);
+ ins->adc_input = NULL;
+ up(&chip->spos_mutex);
+
+ return 0;
+}
+
+int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data)
+{
+ u32 temp;
+ int i;
+
+ /* santiy check the parameters. (These numbers are not 100% correct. They are
+ a rough guess from looking at the controller spec.) */
+ if (address < 0x8000 || address >= 0x9000)
+ return -EINVAL;
+
+ /* initialize the SP_IO_WRITE SCB with the data. */
+ temp = ( address << 16 ) | ( address & 0x0000FFFF); /* offset 0 <-- address2 : address1 */
+
+ snd_cs46xx_poke(chip,( SPIOWRITE_SCB_ADDR << 2), temp);
+ snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 1) << 2), data); /* offset 1 <-- data1 */
+ snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2 */
+
+ /* Poke this location to tell the task to start */
+ snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 6) << 2), SPIOWRITE_SCB_ADDR << 0x10);
+
+ /* Verify that the task ran */
+ for (i=0; i<25; i++) {
+ udelay(125);
+
+ temp = snd_cs46xx_peek(chip,((SPIOWRITE_SCB_ADDR + 6) << 2));
+ if (temp == 0x00000000)
+ break;
+ }
+
+ if (i == 25) {
+ snd_printk(KERN_ERR "dsp_spos: SPIOWriteTask not responding\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int cs46xx_dsp_set_dac_volume (cs46xx_t * chip,u16 left,u16 right)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * scb;
+
+ down(&chip->spos_mutex);
+
+ /* main output */
+ scb = ins->master_mix_scb->sub_list_ptr;
+ while (scb != ins->the_null_scb) {
+ cs46xx_dsp_scb_set_volume (chip,scb,left,right);
+ scb = scb->next_scb_ptr;
+ }
+
+ /* rear output */
+ scb = ins->rear_mix_scb->sub_list_ptr;
+ while (scb != ins->the_null_scb) {
+ cs46xx_dsp_scb_set_volume (chip,scb,left,right);
+ scb = scb->next_scb_ptr;
+ }
+
+ ins->dac_volume_left = left;
+ ins->dac_volume_right = right;
+
+ up(&chip->spos_mutex);
+
+ return 0;
+}
+
+int cs46xx_dsp_set_iec958_volume (cs46xx_t * chip,u16 left,u16 right) {
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ down(&chip->spos_mutex);
+
+ if (ins->asynch_rx_scb != NULL)
+ cs46xx_dsp_scb_set_volume (chip,ins->asynch_rx_scb,
+ left,right);
+
+ ins->spdif_input_volume_left = left;
+ ins->spdif_input_volume_right = right;
+
+ up(&chip->spos_mutex);
+
+ return 0;
+}
diff --git a/sound/pci/cs46xx/dsp_spos.h b/sound/pci/cs46xx/dsp_spos.h
new file mode 100644
index 00000000000..90871bf9762
--- /dev/null
+++ b/sound/pci/cs46xx/dsp_spos.h
@@ -0,0 +1,225 @@
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * 2002-07 Benny Sjostrand benny@hostmobility.com
+ */
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP /* hack ... */
+#ifndef __DSP_SPOS_H__
+#define __DSP_SPOS_H__
+
+#define DSP_MAX_SYMBOLS 1024
+#define DSP_MAX_MODULES 64
+
+#define DSP_CODE_BYTE_SIZE 0x00007000UL
+#define DSP_PARAMETER_BYTE_SIZE 0x00003000UL
+#define DSP_SAMPLE_BYTE_SIZE 0x00003800UL
+#define DSP_PARAMETER_BYTE_OFFSET 0x00000000UL
+#define DSP_SAMPLE_BYTE_OFFSET 0x00010000UL
+#define DSP_CODE_BYTE_OFFSET 0x00020000UL
+
+#define WIDE_INSTR_MASK 0x0040
+#define WIDE_LADD_INSTR_MASK 0x0380
+
+/* this instruction types
+ needs to be reallocated when load
+ code into DSP */
+typedef enum {
+ WIDE_FOR_BEGIN_LOOP = 0x20,
+ WIDE_FOR_BEGIN_LOOP2,
+
+ WIDE_COND_GOTO_ADDR = 0x30,
+ WIDE_COND_GOTO_CALL,
+
+ WIDE_TBEQ_COND_GOTO_ADDR = 0x70,
+ WIDE_TBEQ_COND_CALL_ADDR,
+ WIDE_TBEQ_NCOND_GOTO_ADDR,
+ WIDE_TBEQ_NCOND_CALL_ADDR,
+ WIDE_TBEQ_COND_GOTO1_ADDR,
+ WIDE_TBEQ_COND_CALL1_ADDR,
+ WIDE_TBEQ_NCOND_GOTOI_ADDR,
+ WIDE_TBEQ_NCOND_CALL1_ADDR,
+} wide_opcode_t;
+
+/* SAMPLE segment */
+#define VARI_DECIMATE_BUF1 0x0000
+#define WRITE_BACK_BUF1 0x0400
+#define CODEC_INPUT_BUF1 0x0500
+#define PCM_READER_BUF1 0x0600
+#define SRC_DELAY_BUF1 0x0680
+#define VARI_DECIMATE_BUF0 0x0780
+#define SRC_OUTPUT_BUF1 0x07A0
+#define ASYNC_IP_OUTPUT_BUFFER1 0x0A00
+#define OUTPUT_SNOOP_BUFFER 0x0B00
+#define SPDIFI_IP_OUTPUT_BUFFER1 0x0E00
+#define SPDIFO_IP_OUTPUT_BUFFER1 0x1000
+#define MIX_SAMPLE_BUF1 0x1400
+#define MIX_SAMPLE_BUF2 0x2E80
+#define MIX_SAMPLE_BUF3 0x2F00
+#define MIX_SAMPLE_BUF4 0x2F80
+#define MIX_SAMPLE_BUF5 0x3000
+
+/* Task stack address */
+#define HFG_STACK 0x066A
+#define FG_STACK 0x066E
+#define BG_STACK 0x068E
+
+/* SCB's addresses */
+#define SPOSCB_ADDR 0x070
+#define BG_TREE_SCB_ADDR 0x635
+#define NULL_SCB_ADDR 0x000
+#define TIMINGMASTER_SCB_ADDR 0x010
+#define CODECOUT_SCB_ADDR 0x020
+#define PCMREADER_SCB_ADDR 0x030
+#define WRITEBACK_SCB_ADDR 0x040
+#define CODECIN_SCB_ADDR 0x080
+#define MASTERMIX_SCB_ADDR 0x090
+#define SRCTASK_SCB_ADDR 0x0A0
+#define VARIDECIMATE_SCB_ADDR 0x0B0
+#define PCMSERIALIN_SCB_ADDR 0x0C0
+#define FG_TASK_HEADER_ADDR 0x600
+#define ASYNCTX_SCB_ADDR 0x0E0
+#define ASYNCRX_SCB_ADDR 0x0F0
+#define SRCTASKII_SCB_ADDR 0x100
+#define OUTPUTSNOOP_SCB_ADDR 0x110
+#define PCMSERIALINII_SCB_ADDR 0x120
+#define SPIOWRITE_SCB_ADDR 0x130
+#define REAR_CODECOUT_SCB_ADDR 0x140
+#define OUTPUTSNOOPII_SCB_ADDR 0x150
+#define PCMSERIALIN_PCM_SCB_ADDR 0x160
+#define RECORD_MIXER_SCB_ADDR 0x170
+#define REAR_MIXER_SCB_ADDR 0x180
+#define CLFE_MIXER_SCB_ADDR 0x190
+#define CLFE_CODEC_SCB_ADDR 0x1A0
+
+/* hyperforground SCB's*/
+#define HFG_TREE_SCB 0xBA0
+#define SPDIFI_SCB_INST 0xBB0
+#define SPDIFO_SCB_INST 0xBC0
+#define WRITE_BACK_SPB 0x0D0
+
+/* offsets */
+#define AsyncCIOFIFOPointer 0xd
+#define SPDIFOFIFOPointer 0xd
+#define SPDIFIFIFOPointer 0xd
+#define TCBData 0xb
+#define HFGFlags 0xa
+#define TCBContextBlk 0x10
+#define AFGTxAccumPhi 0x4
+#define SCBsubListPtr 0x9
+#define SCBfuncEntryPtr 0xA
+#define SRCCorPerGof 0x2
+#define SRCPhiIncr6Int26Frac 0xd
+#define SCBVolumeCtrl 0xe
+
+/* conf */
+#define UseASER1Input 1
+
+
+
+/*
+ * The following defines are for the flags in the rsConfig01/23 registers of
+ * the SP.
+ */
+
+#define RSCONFIG_MODULO_SIZE_MASK 0x0000000FL
+#define RSCONFIG_MODULO_16 0x00000001L
+#define RSCONFIG_MODULO_32 0x00000002L
+#define RSCONFIG_MODULO_64 0x00000003L
+#define RSCONFIG_MODULO_128 0x00000004L
+#define RSCONFIG_MODULO_256 0x00000005L
+#define RSCONFIG_MODULO_512 0x00000006L
+#define RSCONFIG_MODULO_1024 0x00000007L
+#define RSCONFIG_MODULO_4 0x00000008L
+#define RSCONFIG_MODULO_8 0x00000009L
+#define RSCONFIG_SAMPLE_SIZE_MASK 0x000000C0L
+#define RSCONFIG_SAMPLE_8MONO 0x00000000L
+#define RSCONFIG_SAMPLE_8STEREO 0x00000040L
+#define RSCONFIG_SAMPLE_16MONO 0x00000080L
+#define RSCONFIG_SAMPLE_16STEREO 0x000000C0L
+#define RSCONFIG_UNDERRUN_ZERO 0x00004000L
+#define RSCONFIG_DMA_TO_HOST 0x00008000L
+#define RSCONFIG_STREAM_NUM_MASK 0x00FF0000L
+#define RSCONFIG_MAX_DMA_SIZE_MASK 0x1F000000L
+#define RSCONFIG_DMA_ENABLE 0x20000000L
+#define RSCONFIG_PRIORITY_MASK 0xC0000000L
+#define RSCONFIG_PRIORITY_HIGH 0x00000000L
+#define RSCONFIG_PRIORITY_MEDIUM_HIGH 0x40000000L
+#define RSCONFIG_PRIORITY_MEDIUM_LOW 0x80000000L
+#define RSCONFIG_PRIORITY_LOW 0xC0000000L
+#define RSCONFIG_STREAM_NUM_SHIFT 16L
+#define RSCONFIG_MAX_DMA_SIZE_SHIFT 24L
+
+/* SP constants */
+#define FG_INTERVAL_TIMER_PERIOD 0x0051
+#define BG_INTERVAL_TIMER_PERIOD 0x0100
+
+
+/* Only SP accessible registers */
+#define SP_ASER_COUNTDOWN 0x8040
+#define SP_SPDOUT_FIFO 0x0108
+#define SP_SPDIN_MI_FIFO 0x01E0
+#define SP_SPDIN_D_FIFO 0x01F0
+#define SP_SPDIN_STATUS 0x8048
+#define SP_SPDIN_CONTROL 0x8049
+#define SP_SPDIN_FIFOPTR 0x804A
+#define SP_SPDOUT_STATUS 0x804C
+#define SP_SPDOUT_CONTROL 0x804D
+#define SP_SPDOUT_CSUV 0x808E
+
+static inline u8 _wrap_all_bits (u8 val) {
+ u8 wrapped;
+
+ /* wrap all 8 bits */
+ wrapped =
+ ((val & 0x1 ) << 7) |
+ ((val & 0x2 ) << 5) |
+ ((val & 0x4 ) << 3) |
+ ((val & 0x8 ) << 1) |
+ ((val & 0x10) >> 1) |
+ ((val & 0x20) >> 3) |
+ ((val & 0x40) >> 5) |
+ ((val & 0x80) >> 7);
+
+ return wrapped;
+
+}
+
+
+static inline void cs46xx_dsp_spos_update_scb (cs46xx_t * chip,dsp_scb_descriptor_t * scb)
+{
+ /* update nextSCB and subListPtr in SCB */
+ snd_cs46xx_poke(chip,
+ (scb->address + SCBsubListPtr) << 2,
+ (scb->sub_list_ptr->address << 0x10) |
+ (scb->next_scb_ptr->address));
+}
+
+static inline void cs46xx_dsp_scb_set_volume (cs46xx_t * chip,dsp_scb_descriptor_t * scb,
+ u16 left,u16 right) {
+ unsigned int val = ((0xffff - left) << 16 | (0xffff - right));
+
+ snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val);
+ snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val);
+}
+#endif /* __DSP_SPOS_H__ */
+#endif /* CONFIG_SND_CS46XX_NEW_DSP */
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
new file mode 100644
index 00000000000..92849e1340b
--- /dev/null
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -0,0 +1,1750 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * 2002-07 Benny Sjostrand benny@hostmobility.com
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/cs46xx.h>
+
+#include "cs46xx_lib.h"
+#include "dsp_spos.h"
+
+typedef struct _proc_scb_info_t {
+ dsp_scb_descriptor_t * scb_desc;
+ cs46xx_t *chip;
+} proc_scb_info_t;
+
+static void remove_symbol (cs46xx_t * chip,symbol_entry_t * symbol)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ int symbol_index = (int)(symbol - ins->symbol_table.symbols);
+
+ snd_assert(ins->symbol_table.nsymbols > 0,return);
+ snd_assert(symbol_index >= 0 && symbol_index < ins->symbol_table.nsymbols, return);
+
+ ins->symbol_table.symbols[symbol_index].deleted = 1;
+
+ if (symbol_index < ins->symbol_table.highest_frag_index) {
+ ins->symbol_table.highest_frag_index = symbol_index;
+ }
+
+ if (symbol_index == ins->symbol_table.nsymbols - 1)
+ ins->symbol_table.nsymbols --;
+
+ if (ins->symbol_table.highest_frag_index > ins->symbol_table.nsymbols) {
+ ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols;
+ }
+
+}
+
+static void cs46xx_dsp_proc_scb_info_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ proc_scb_info_t * scb_info = (proc_scb_info_t *)entry->private_data;
+ dsp_scb_descriptor_t * scb = scb_info->scb_desc;
+ dsp_spos_instance_t * ins;
+ cs46xx_t *chip = scb_info->chip;
+ int j,col;
+ void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
+
+ ins = chip->dsp_spos_instance;
+
+ down(&chip->spos_mutex);
+ snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
+
+ for (col = 0,j = 0;j < 0x10; j++,col++) {
+ if (col == 4) {
+ snd_iprintf(buffer,"\n");
+ col = 0;
+ }
+ snd_iprintf(buffer,"%08x ",readl(dst + (scb->address + j) * sizeof(u32)));
+ }
+
+ snd_iprintf(buffer,"\n");
+
+ if (scb->parent_scb_ptr != NULL) {
+ snd_iprintf(buffer,"parent [%s:%04x] ",
+ scb->parent_scb_ptr->scb_name,
+ scb->parent_scb_ptr->address);
+ } else snd_iprintf(buffer,"parent [none] ");
+
+ snd_iprintf(buffer,"sub_list_ptr [%s:%04x]\nnext_scb_ptr [%s:%04x] task_entry [%s:%04x]\n",
+ scb->sub_list_ptr->scb_name,
+ scb->sub_list_ptr->address,
+ scb->next_scb_ptr->scb_name,
+ scb->next_scb_ptr->address,
+ scb->task_entry->symbol_name,
+ scb->task_entry->address);
+
+ snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);
+ up(&chip->spos_mutex);
+}
+
+static void _dsp_unlink_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ unsigned long flags;
+
+ if ( scb->parent_scb_ptr ) {
+ /* unlink parent SCB */
+ snd_assert ((scb->parent_scb_ptr->sub_list_ptr == scb ||
+ scb->parent_scb_ptr->next_scb_ptr == scb),return);
+
+ if (scb->parent_scb_ptr->sub_list_ptr == scb) {
+
+ if (scb->next_scb_ptr == ins->the_null_scb) {
+ /* last and only node in parent sublist */
+ scb->parent_scb_ptr->sub_list_ptr = scb->sub_list_ptr;
+
+ if (scb->sub_list_ptr != ins->the_null_scb) {
+ scb->sub_list_ptr->parent_scb_ptr = scb->parent_scb_ptr;
+ }
+ scb->sub_list_ptr = ins->the_null_scb;
+ } else {
+ /* first node in parent sublist */
+ scb->parent_scb_ptr->sub_list_ptr = scb->next_scb_ptr;
+
+ if (scb->next_scb_ptr != ins->the_null_scb) {
+ /* update next node parent ptr. */
+ scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr;
+ }
+ scb->next_scb_ptr = ins->the_null_scb;
+ }
+ } else {
+ /* snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return); */
+ scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr;
+
+ if (scb->next_scb_ptr != ins->the_null_scb) {
+ /* update next node parent ptr. */
+ scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr;
+ }
+ scb->next_scb_ptr = ins->the_null_scb;
+ }
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+
+ /* update parent first entry in DSP RAM */
+ cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
+
+ /* then update entry in DSP RAM */
+ cs46xx_dsp_spos_update_scb(chip,scb);
+
+ scb->parent_scb_ptr = NULL;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ }
+}
+
+static void _dsp_clear_sample_buffer (cs46xx_t *chip, u32 sample_buffer_addr, int dword_count)
+{
+ void __iomem *dst = chip->region.idx[2].remap_addr + sample_buffer_addr;
+ int i;
+
+ for (i = 0; i < dword_count ; ++i ) {
+ writel(0, dst);
+ dst += 4;
+ }
+}
+
+void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ /* check integrety */
+ snd_assert ( (scb->index >= 0 &&
+ scb->index < ins->nscb &&
+ (ins->scbs + scb->index) == scb), return );
+
+#if 0
+ /* can't remove a SCB with childs before
+ removing childs first */
+ snd_assert ( (scb->sub_list_ptr == ins->the_null_scb &&
+ scb->next_scb_ptr == ins->the_null_scb),
+ goto _end);
+#endif
+
+ spin_lock(&scb->lock);
+ _dsp_unlink_scb (chip,scb);
+ spin_unlock(&scb->lock);
+
+ cs46xx_dsp_proc_free_scb_desc(scb);
+ snd_assert (scb->scb_symbol != NULL, return );
+ remove_symbol (chip,scb->scb_symbol);
+
+ ins->scbs[scb->index].deleted = 1;
+
+ if (scb->index < ins->scb_highest_frag_index)
+ ins->scb_highest_frag_index = scb->index;
+
+ if (scb->index == ins->nscb - 1) {
+ ins->nscb --;
+ }
+
+ if (ins->scb_highest_frag_index > ins->nscb) {
+ ins->scb_highest_frag_index = ins->nscb;
+ }
+
+#if 0
+ /* !!!! THIS IS A PIECE OF SHIT MADE BY ME !!! */
+ for(i = scb->index + 1;i < ins->nscb; ++i) {
+ ins->scbs[i - 1].index = i - 1;
+ }
+#endif
+}
+
+
+void cs46xx_dsp_proc_free_scb_desc (dsp_scb_descriptor_t * scb)
+{
+ if (scb->proc_info) {
+ proc_scb_info_t * scb_info = (proc_scb_info_t *)scb->proc_info->private_data;
+
+ snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name);
+
+ snd_info_unregister(scb->proc_info);
+ scb->proc_info = NULL;
+
+ snd_assert (scb_info != NULL, return);
+ kfree (scb_info);
+ }
+}
+
+void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * scb)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ snd_info_entry_t * entry;
+ proc_scb_info_t * scb_info;
+
+ /* register to proc */
+ if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
+ scb->proc_info == NULL) {
+
+ if ((entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name,
+ ins->proc_dsp_dir)) != NULL) {
+ scb_info = kmalloc(sizeof(proc_scb_info_t), GFP_KERNEL);
+ if (!scb_info) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ goto out;
+ }
+
+ scb_info->chip = chip;
+ scb_info->scb_desc = scb;
+
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = scb_info;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+
+ entry->c.text.read_size = 512;
+ entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
+
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ kfree (scb_info);
+ entry = NULL;
+ }
+ }
+out:
+ scb->proc_info = entry;
+ }
+}
+
+static dsp_scb_descriptor_t *
+_dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest,
+ symbol_entry_t * task_entry,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * scb;
+
+ unsigned long flags;
+
+ snd_assert (ins->the_null_scb != NULL,return NULL);
+
+ /* fill the data that will be wroten to DSP */
+ scb_data[SCBsubListPtr] =
+ (ins->the_null_scb->address << 0x10) | ins->the_null_scb->address;
+
+ scb_data[SCBfuncEntryPtr] &= 0xFFFF0000;
+ scb_data[SCBfuncEntryPtr] |= task_entry->address;
+
+ snd_printdd("dsp_spos: creating SCB <%s>\n",name);
+
+ scb = cs46xx_dsp_create_scb(chip,name,scb_data,dest);
+
+
+ scb->sub_list_ptr = ins->the_null_scb;
+ scb->next_scb_ptr = ins->the_null_scb;
+
+ scb->parent_scb_ptr = parent_scb;
+ scb->task_entry = task_entry;
+
+
+ /* update parent SCB */
+ if (scb->parent_scb_ptr) {
+#if 0
+ printk ("scb->parent_scb_ptr = %s\n",scb->parent_scb_ptr->scb_name);
+ printk ("scb->parent_scb_ptr->next_scb_ptr = %s\n",scb->parent_scb_ptr->next_scb_ptr->scb_name);
+ printk ("scb->parent_scb_ptr->sub_list_ptr = %s\n",scb->parent_scb_ptr->sub_list_ptr->scb_name);
+#endif
+ /* link to parent SCB */
+ if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
+ snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb),
+ return NULL);
+
+ scb->parent_scb_ptr->next_scb_ptr = scb;
+
+ } else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
+ snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb),
+ return NULL);
+
+ scb->parent_scb_ptr->sub_list_ptr = scb;
+ } else {
+ snd_assert (0,return NULL);
+ }
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+
+ /* update entry in DSP RAM */
+ cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
+
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ }
+
+
+ cs46xx_dsp_proc_register_scb_desc (chip,scb);
+
+ return scb;
+}
+
+static dsp_scb_descriptor_t *
+cs46xx_dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest,
+ char * task_entry_name,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ symbol_entry_t * task_entry;
+
+ task_entry = cs46xx_dsp_lookup_symbol (chip,task_entry_name,
+ SYMBOL_CODE);
+
+ if (task_entry == NULL) {
+ snd_printk (KERN_ERR "dsp_spos: symbol %s not found\n",task_entry_name);
+ return NULL;
+ }
+
+ return _dsp_create_generic_scb (chip,name,scb_data,dest,task_entry,
+ parent_scb,scb_child_type);
+}
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_timing_master_scb (cs46xx_t *chip)
+{
+ dsp_scb_descriptor_t * scb;
+
+ timing_master_scb_t timing_master_scb = {
+ { 0,
+ 0,
+ 0,
+ 0
+ },
+ { 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ 0,0,
+ 0,NULL_SCB_ADDR,
+ 0,0, /* extraSampleAccum:TMreserved */
+ 0,0, /* codecFIFOptr:codecFIFOsyncd */
+ 0x0001,0x8000, /* fracSampAccumQm1:TMfrmsLeftInGroup */
+ 0x0001,0x0000, /* fracSampCorrectionQm1:TMfrmGroupLength */
+ 0x00060000 /* nSampPerFrmQ15 */
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,"TimingMasterSCBInst",(u32 *)&timing_master_scb,
+ TIMINGMASTER_SCB_ADDR,
+ "TIMINGMASTER",NULL,SCB_NO_PARENT);
+
+ return scb;
+}
+
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_codec_out_scb(cs46xx_t * chip,char * codec_name,
+ u16 channel_disp,u16 fifo_addr,
+ u16 child_scb_addr,
+ u32 dest,dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_scb_descriptor_t * scb;
+
+ codec_output_scb_t codec_out_scb = {
+ { 0,
+ 0,
+ 0,
+ 0
+ },
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ 0,0,
+ 0,NULL_SCB_ADDR,
+ 0, /* COstrmRsConfig */
+ 0, /* COstrmBufPtr */
+ channel_disp,fifo_addr, /* leftChanBaseIOaddr:rightChanIOdisp */
+ 0x0000,0x0080, /* (!AC97!) COexpVolChangeRate:COscaleShiftCount */
+ 0,child_scb_addr /* COreserved - need child scb to work with rom code */
+ };
+
+
+ scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_out_scb,
+ dest,"S16_CODECOUTPUTTASK",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_codec_in_scb(cs46xx_t * chip,char * codec_name,
+ u16 channel_disp,u16 fifo_addr,
+ u16 sample_buffer_addr,
+ u32 dest,dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+
+ dsp_scb_descriptor_t * scb;
+ codec_input_scb_t codec_input_scb = {
+ { 0,
+ 0,
+ 0,
+ 0
+ },
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+
+#if 0 /* cs4620 */
+ SyncIOSCB,NULL_SCB_ADDR
+#else
+ 0 , 0,
+#endif
+ 0,0,
+
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64, /* strmRsConfig */
+ sample_buffer_addr << 0x10, /* strmBufPtr; defined as a dword ptr, used as a byte ptr */
+ channel_disp,fifo_addr, /* (!AC97!) leftChanBaseINaddr=AC97primary
+ link input slot 3 :rightChanINdisp=""slot 4 */
+ 0x0000,0x0000, /* (!AC97!) ????:scaleShiftCount; no shift needed
+ because AC97 is already 20 bits */
+ 0x80008000 /* ??clw cwcgame.scb has 0 */
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_input_scb,
+ dest,"S16_CODECINPUTTASK",parent_scb,
+ scb_child_type);
+ return scb;
+}
+
+
+static dsp_scb_descriptor_t *
+cs46xx_dsp_create_pcm_reader_scb(cs46xx_t * chip,char * scb_name,
+ u16 sample_buffer_addr,u32 dest,
+ int virtual_channel, u32 playback_hw_addr,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * scb;
+
+ generic_scb_t pcm_reader_scb = {
+
+ /*
+ Play DMA Task xfers data from host buffer to SP buffer
+ init/runtime variables:
+ PlayAC: Play Audio Data Conversion - SCB loc: 2nd dword, mask: 0x0000F000L
+ DATA_FMT_16BIT_ST_LTLEND(0x00000000L) from 16-bit stereo, little-endian
+ DATA_FMT_8_BIT_ST_SIGNED(0x00001000L) from 8-bit stereo, signed
+ DATA_FMT_16BIT_MN_LTLEND(0x00002000L) from 16-bit mono, little-endian
+ DATA_FMT_8_BIT_MN_SIGNED(0x00003000L) from 8-bit mono, signed
+ DATA_FMT_16BIT_ST_BIGEND(0x00004000L) from 16-bit stereo, big-endian
+ DATA_FMT_16BIT_MN_BIGEND(0x00006000L) from 16-bit mono, big-endian
+ DATA_FMT_8_BIT_ST_UNSIGNED(0x00009000L) from 8-bit stereo, unsigned
+ DATA_FMT_8_BIT_MN_UNSIGNED(0x0000b000L) from 8-bit mono, unsigned
+ ? Other combinations possible from:
+ DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000L
+ DMA_RQ_C2_AC_NONE 0x00000000L
+ DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000L
+ DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000L
+ DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000L
+ DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000L
+
+ HostBuffAddr: Host Buffer Physical Byte Address - SCB loc:3rd dword, Mask: 0xFFFFFFFFL
+ aligned to dword boundary
+ */
+ /* Basic (non scatter/gather) DMA requestor (4 ints) */
+ { DMA_RQ_C1_SOURCE_ON_HOST + /* source buffer is on the host */
+ DMA_RQ_C1_SOURCE_MOD1024 + /* source buffer is 1024 dwords (4096 bytes) */
+ DMA_RQ_C1_DEST_MOD32 + /* dest buffer(PCMreaderBuf) is 32 dwords*/
+ DMA_RQ_C1_WRITEBACK_SRC_FLAG + /* ?? */
+ DMA_RQ_C1_WRITEBACK_DEST_FLAG + /* ?? */
+ 15, /* DwordCount-1: picked 16 for DwordCount because Jim */
+ /* Barnette said that is what we should use since */
+ /* we are not running in optimized mode? */
+ DMA_RQ_C2_AC_NONE +
+ DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG + /* set play interrupt (bit0) in HISR when source */
+ /* buffer (on host) crosses half-way point */
+ virtual_channel, /* Play DMA channel arbitrarily set to 0 */
+ playback_hw_addr, /* HostBuffAddr (source) */
+ DMA_RQ_SD_SP_SAMPLE_ADDR + /* destination buffer is in SP Sample Memory */
+ sample_buffer_addr /* SP Buffer Address (destination) */
+ },
+ /* Scatter/gather DMA requestor extension (5 ints) */
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ /* Sublist pointer & next stream control block (SCB) link. */
+ NULL_SCB_ADDR,NULL_SCB_ADDR,
+ /* Pointer to this tasks parameter block & stream function pointer */
+ 0,NULL_SCB_ADDR,
+ /* rsConfig register for stream buffer (rsDMA reg. is loaded from basicReq.daw */
+ /* for incoming streams, or basicReq.saw, for outgoing streams) */
+ RSCONFIG_DMA_ENABLE + /* enable DMA */
+ (19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) + /* MAX_DMA_SIZE picked to be 19 since SPUD */
+ /* uses it for some reason */
+ ((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) + /* stream number = SCBaddr/16 */
+ RSCONFIG_SAMPLE_16STEREO +
+ RSCONFIG_MODULO_32, /* dest buffer(PCMreaderBuf) is 32 dwords (256 bytes) */
+ /* Stream sample pointer & MAC-unit mode for this stream */
+ (sample_buffer_addr << 0x10),
+ /* Fractional increment per output sample in the input sample buffer */
+ 0,
+ {
+ /* Standard stereo volume control
+ default muted */
+ 0xffff,0xffff,
+ 0xffff,0xffff
+ }
+ };
+
+ if (ins->null_algorithm == NULL) {
+ ins->null_algorithm = cs46xx_dsp_lookup_symbol (chip,"NULLALGORITHM",
+ SYMBOL_CODE);
+
+ if (ins->null_algorithm == NULL) {
+ snd_printk (KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+ return NULL;
+ }
+ }
+
+ scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_reader_scb,
+ dest,ins->null_algorithm,parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+#define GOF_PER_SEC 200
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_src_task_scb(cs46xx_t * chip,char * scb_name,
+ int rate,
+ u16 src_buffer_addr,
+ u16 src_delay_buffer_addr,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type,
+ int pass_through)
+{
+
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * scb;
+ unsigned int tmp1, tmp2;
+ unsigned int phiIncr;
+ unsigned int correctionPerGOF, correctionPerSec;
+
+ snd_printdd( "dsp_spos: setting %s rate to %u\n",scb_name,rate);
+
+ /*
+ * Compute the values used to drive the actual sample rate conversion.
+ * The following formulas are being computed, using inline assembly
+ * since we need to use 64 bit arithmetic to compute the values:
+ *
+ * phiIncr = floor((Fs,in * 2^26) / Fs,out)
+ * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+ * GOF_PER_SEC)
+ * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
+ * GOF_PER_SEC * correctionPerGOF
+ *
+ * i.e.
+ *
+ * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
+ * correctionPerGOF:correctionPerSec =
+ * dividend:remainder(ulOther / GOF_PER_SEC)
+ */
+ tmp1 = rate << 16;
+ phiIncr = tmp1 / 48000;
+ tmp1 -= phiIncr * 48000;
+ tmp1 <<= 10;
+ phiIncr <<= 10;
+ tmp2 = tmp1 / 48000;
+ phiIncr += tmp2;
+ tmp1 -= tmp2 * 48000;
+ correctionPerGOF = tmp1 / GOF_PER_SEC;
+ tmp1 -= correctionPerGOF * GOF_PER_SEC;
+ correctionPerSec = tmp1;
+
+ {
+ src_task_scb_t src_task_scb = {
+ 0x0028,0x00c8,
+ 0x5555,0x0000,
+ 0x0000,0x0000,
+ src_buffer_addr,1,
+ correctionPerGOF,correctionPerSec,
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_32,
+ 0x0000,src_delay_buffer_addr,
+ 0x0,
+ 0x080,(src_delay_buffer_addr + (24 * 4)),
+ 0,0, /* next_scb, sub_list_ptr */
+ 0,0, /* entry, this_spb */
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8,
+ src_buffer_addr << 0x10,
+ phiIncr,
+ {
+ 0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left,
+ 0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left
+ }
+ };
+
+ if (ins->s16_up == NULL) {
+ ins->s16_up = cs46xx_dsp_lookup_symbol (chip,"S16_UPSRC",
+ SYMBOL_CODE);
+
+ if (ins->s16_up == NULL) {
+ snd_printk (KERN_ERR "dsp_spos: symbol S16_UPSRC not found\n");
+ return NULL;
+ }
+ }
+
+ /* clear buffers */
+ _dsp_clear_sample_buffer (chip,src_buffer_addr,8);
+ _dsp_clear_sample_buffer (chip,src_delay_buffer_addr,32);
+
+ if (pass_through) {
+ /* wont work with any other rate than
+ the native DSP rate */
+ snd_assert (rate = 48000);
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
+ dest,"DMAREADER",parent_scb,
+ scb_child_type);
+ } else {
+ scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
+ dest,ins->s16_up,parent_scb,
+ scb_child_type);
+ }
+
+
+ }
+
+ return scb;
+}
+
+#if 0 /* not used */
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_filter_scb(cs46xx_t * chip,char * scb_name,
+ u16 buffer_addr,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type) {
+ dsp_scb_descriptor_t * scb;
+
+ filter_scb_t filter_scb = {
+ .a0_right = 0x41a9,
+ .a0_left = 0x41a9,
+ .a1_right = 0xb8e4,
+ .a1_left = 0xb8e4,
+ .a2_right = 0x3e55,
+ .a2_left = 0x3e55,
+
+ .filter_unused3 = 0x0000,
+ .filter_unused2 = 0x0000,
+
+ .output_buf_ptr = buffer_addr,
+ .init = 0x000,
+
+ .prev_sample_output1 = 0x00000000,
+ .prev_sample_output2 = 0x00000000,
+
+ .prev_sample_input1 = 0x00000000,
+ .prev_sample_input2 = 0x00000000,
+
+ .next_scb_ptr = 0x0000,
+ .sub_list_ptr = 0x0000,
+
+ .entry_point = 0x0000,
+ .spb_ptr = 0x0000,
+
+ .b0_right = 0x0e38,
+ .b0_left = 0x0e38,
+ .b1_right = 0x1c71,
+ .b1_left = 0x1c71,
+ .b2_right = 0x0e38,
+ .b2_left = 0x0e38,
+ };
+
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&filter_scb,
+ dest,"FILTERTASK",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+#endif /* not used */
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_mix_only_scb(cs46xx_t * chip,char * scb_name,
+ u16 mix_buffer_addr,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_scb_descriptor_t * scb;
+
+ mix_only_scb_t master_mix_scb = {
+ /* 0 */ { 0,
+ /* 1 */ 0,
+ /* 2 */ mix_buffer_addr,
+ /* 3 */ 0
+ /* */ },
+ {
+ /* 4 */ 0,
+ /* 5 */ 0,
+ /* 6 */ 0,
+ /* 7 */ 0,
+ /* 8 */ 0x00000080
+ },
+ /* 9 */ 0,0,
+ /* A */ 0,0,
+ /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_32,
+ /* C */ (mix_buffer_addr + (16 * 4)) << 0x10,
+ /* D */ 0,
+ {
+ /* E */ 0x8000,0x8000,
+ /* F */ 0x8000,0x8000
+ }
+ };
+
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&master_mix_scb,
+ dest,"S16_MIX",parent_scb,
+ scb_child_type);
+ return scb;
+}
+
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_mix_to_ostream_scb(cs46xx_t * chip,char * scb_name,
+ u16 mix_buffer_addr,u16 writeback_spb,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_scb_descriptor_t * scb;
+
+ mix2_ostream_scb_t mix2_ostream_scb = {
+ /* Basic (non scatter/gather) DMA requestor (4 ints) */
+ {
+ DMA_RQ_C1_SOURCE_MOD64 +
+ DMA_RQ_C1_DEST_ON_HOST +
+ DMA_RQ_C1_DEST_MOD1024 +
+ DMA_RQ_C1_WRITEBACK_SRC_FLAG +
+ DMA_RQ_C1_WRITEBACK_DEST_FLAG +
+ 15,
+
+ DMA_RQ_C2_AC_NONE +
+ DMA_RQ_C2_SIGNAL_DEST_PINGPONG +
+
+ CS46XX_DSP_CAPTURE_CHANNEL,
+ DMA_RQ_SD_SP_SAMPLE_ADDR +
+ mix_buffer_addr,
+ 0x0
+ },
+
+ { 0, 0, 0, 0, 0, },
+ 0,0,
+ 0,writeback_spb,
+
+ RSCONFIG_DMA_ENABLE +
+ (19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) +
+
+ ((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) +
+ RSCONFIG_DMA_TO_HOST +
+ RSCONFIG_SAMPLE_16STEREO +
+ RSCONFIG_MODULO_64,
+ (mix_buffer_addr + (32 * 4)) << 0x10,
+ 1,0,
+ 0x0001,0x0080,
+ 0xFFFF,0
+ };
+
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&mix2_ostream_scb,
+
+ dest,"S16_MIX_TO_OSTREAM",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_vari_decimate_scb(cs46xx_t * chip,char * scb_name,
+ u16 vari_buffer_addr0,
+ u16 vari_buffer_addr1,
+ u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+
+ dsp_scb_descriptor_t * scb;
+
+ vari_decimate_scb_t vari_decimate_scb = {
+ 0x0028,0x00c8,
+ 0x5555,0x0000,
+ 0x0000,0x0000,
+ vari_buffer_addr0,vari_buffer_addr1,
+
+ 0x0028,0x00c8,
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256,
+
+ 0xFF800000,
+ 0,
+ 0x0080,vari_buffer_addr1 + (25 * 4),
+
+ 0,0,
+ 0,0,
+
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8,
+ vari_buffer_addr0 << 0x10,
+ 0x04000000,
+ {
+ 0x8000,0x8000,
+ 0xFFFF,0xFFFF
+ }
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&vari_decimate_scb,
+ dest,"VARIDECIMATE",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+
+static dsp_scb_descriptor_t *
+cs46xx_dsp_create_pcm_serial_input_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ dsp_scb_descriptor_t * input_scb,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+
+ dsp_scb_descriptor_t * scb;
+
+
+ pcm_serial_input_scb_t pcm_serial_input_scb = {
+ { 0,
+ 0,
+ 0,
+ 0
+ },
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+
+ 0,0,
+ 0,0,
+
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_16,
+ 0,
+ /* 0xD */ 0,input_scb->address,
+ {
+ /* 0xE */ 0x8000,0x8000,
+ /* 0xF */ 0x8000,0x8000
+ }
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_serial_input_scb,
+ dest,"PCMSERIALINPUTTASK",parent_scb,
+ scb_child_type);
+ return scb;
+}
+
+
+static dsp_scb_descriptor_t *
+cs46xx_dsp_create_asynch_fg_tx_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ u16 hfg_scb_address,
+ u16 asynch_buffer_address,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+
+ dsp_scb_descriptor_t * scb;
+
+ asynch_fg_tx_scb_t asynch_fg_tx_scb = {
+ 0xfc00,0x03ff, /* Prototype sample buffer size of 256 dwords */
+ 0x0058,0x0028, /* Min Delta 7 dwords == 28 bytes */
+ /* : Max delta 25 dwords == 100 bytes */
+ 0,hfg_scb_address, /* Point to HFG task SCB */
+ 0,0, /* Initialize current Delta and Consumer ptr adjustment count */
+ 0, /* Initialize accumulated Phi to 0 */
+ 0,0x2aab, /* Const 1/3 */
+
+ {
+ 0, /* Define the unused elements */
+ 0,
+ 0
+ },
+
+ 0,0,
+ 0,dest + AFGTxAccumPhi,
+
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, /* Stereo, 256 dword */
+ (asynch_buffer_address) << 0x10, /* This should be automagically synchronized
+ to the producer pointer */
+
+ /* There is no correct initial value, it will depend upon the detected
+ rate etc */
+ 0x18000000, /* Phi increment for approx 32k operation */
+ 0x8000,0x8000, /* Volume controls are unused at this time */
+ 0x8000,0x8000
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_tx_scb,
+ dest,"ASYNCHFGTXCODE",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_asynch_fg_rx_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ u16 hfg_scb_address,
+ u16 asynch_buffer_address,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * scb;
+
+ asynch_fg_rx_scb_t asynch_fg_rx_scb = {
+ 0xfe00,0x01ff, /* Prototype sample buffer size of 128 dwords */
+ 0x0064,0x001c, /* Min Delta 7 dwords == 28 bytes */
+ /* : Max delta 25 dwords == 100 bytes */
+ 0,hfg_scb_address, /* Point to HFG task SCB */
+ 0,0, /* Initialize current Delta and Consumer ptr adjustment count */
+ {
+ 0, /* Define the unused elements */
+ 0,
+ 0,
+ 0,
+ 0
+ },
+
+ 0,0,
+ 0,dest,
+
+ RSCONFIG_MODULO_128 |
+ RSCONFIG_SAMPLE_16STEREO, /* Stereo, 128 dword */
+ ( (asynch_buffer_address + (16 * 4)) << 0x10), /* This should be automagically
+ synchrinized to the producer pointer */
+
+ /* There is no correct initial value, it will depend upon the detected
+ rate etc */
+ 0x18000000,
+
+ /* Set IEC958 input volume */
+ 0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left,
+ 0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left,
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_rx_scb,
+ dest,"ASYNCHFGRXCODE",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+
+#if 0 /* not used */
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_output_snoop_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ u16 snoop_buffer_address,
+ dsp_scb_descriptor_t * snoop_scb,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+
+ dsp_scb_descriptor_t * scb;
+
+ output_snoop_scb_t output_snoop_scb = {
+ { 0, /* not used. Zero */
+ 0,
+ 0,
+ 0,
+ },
+ {
+ 0, /* not used. Zero */
+ 0,
+ 0,
+ 0,
+ 0
+ },
+
+ 0,0,
+ 0,0,
+
+ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
+ snoop_buffer_address << 0x10,
+ 0,0,
+ 0,
+ 0,snoop_scb->address
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&output_snoop_scb,
+ dest,"OUTPUTSNOOP",parent_scb,
+ scb_child_type);
+ return scb;
+}
+#endif /* not used */
+
+
+dsp_scb_descriptor_t *
+cs46xx_dsp_create_spio_write_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_scb_descriptor_t * scb;
+
+ spio_write_scb_t spio_write_scb = {
+ 0,0, /* SPIOWAddress2:SPIOWAddress1; */
+ 0, /* SPIOWData1; */
+ 0, /* SPIOWData2; */
+ 0,0, /* SPIOWAddress4:SPIOWAddress3; */
+ 0, /* SPIOWData3; */
+ 0, /* SPIOWData4; */
+ 0,0, /* SPIOWDataPtr:Unused1; */
+ { 0,0 }, /* Unused2[2]; */
+
+ 0,0, /* SPIOWChildPtr:SPIOWSiblingPtr; */
+ 0,0, /* SPIOWThisPtr:SPIOWEntryPoint; */
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0 /* Unused3[5]; */
+ }
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&spio_write_scb,
+ dest,"SPIOWRITE",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+dsp_scb_descriptor_t * cs46xx_dsp_create_magic_snoop_scb(cs46xx_t * chip,char * scb_name,u32 dest,
+ u16 snoop_buffer_address,
+ dsp_scb_descriptor_t * snoop_scb,
+ dsp_scb_descriptor_t * parent_scb,
+ int scb_child_type)
+{
+ dsp_scb_descriptor_t * scb;
+
+ magic_snoop_task_t magic_snoop_scb = {
+ /* 0 */ 0, /* i0 */
+ /* 1 */ 0, /* i1 */
+ /* 2 */ snoop_buffer_address << 0x10,
+ /* 3 */ 0,snoop_scb->address,
+ /* 4 */ 0, /* i3 */
+ /* 5 */ 0, /* i4 */
+ /* 6 */ 0, /* i5 */
+ /* 7 */ 0, /* i6 */
+ /* 8 */ 0, /* i7 */
+ /* 9 */ 0,0, /* next_scb, sub_list_ptr */
+ /* A */ 0,0, /* entry_point, this_ptr */
+ /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
+ /* C */ snoop_buffer_address << 0x10,
+ /* D */ 0,
+ /* E */ { 0x8000,0x8000,
+ /* F */ 0xffff,0xffff
+ }
+ };
+
+ scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&magic_snoop_scb,
+ dest,"MAGICSNOOPTASK",parent_scb,
+ scb_child_type);
+
+ return scb;
+}
+
+static dsp_scb_descriptor_t * find_next_free_scb (cs46xx_t * chip,dsp_scb_descriptor_t * from)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * scb = from;
+
+ while (scb->next_scb_ptr != ins->the_null_scb) {
+ snd_assert (scb->next_scb_ptr != NULL, return NULL);
+
+ scb = scb->next_scb_ptr;
+ }
+
+ return scb;
+}
+
+static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
+ 0x0600, /* 1 */
+ 0x1500, /* 2 */
+ 0x1580, /* 3 */
+ 0x1600, /* 4 */
+ 0x1680, /* 5 */
+ 0x1700, /* 6 */
+ 0x1780, /* 7 */
+ 0x1800, /* 8 */
+ 0x1880, /* 9 */
+ 0x1900, /* 10 */
+ 0x1980, /* 11 */
+ 0x1A00, /* 12 */
+ 0x1A80, /* 13 */
+ 0x1B00, /* 14 */
+ 0x1B80, /* 15 */
+ 0x1C00, /* 16 */
+ 0x1C80, /* 17 */
+ 0x1D00, /* 18 */
+ 0x1D80, /* 19 */
+ 0x1E00, /* 20 */
+ 0x1E80, /* 21 */
+ 0x1F00, /* 22 */
+ 0x1F80, /* 23 */
+ 0x2000, /* 24 */
+ 0x2080, /* 25 */
+ 0x2100, /* 26 */
+ 0x2180, /* 27 */
+ 0x2200, /* 28 */
+ 0x2280, /* 29 */
+ 0x2300, /* 30 */
+ 0x2380, /* 31 */
+ 0x2400, /* 32 */
+};
+
+static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
+ 0x2B80,
+ 0x2BA0,
+ 0x2BC0,
+ 0x2BE0,
+ 0x2D00,
+ 0x2D20,
+ 0x2D40,
+ 0x2D60,
+ 0x2D80,
+ 0x2DA0,
+ 0x2DC0,
+ 0x2DE0,
+ 0x2E00,
+ 0x2E20
+};
+
+static u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
+ 0x2480,
+ 0x2500,
+ 0x2580,
+ 0x2600,
+ 0x2680,
+ 0x2700,
+ 0x2780,
+ 0x2800,
+ 0x2880,
+ 0x2900,
+ 0x2980,
+ 0x2A00,
+ 0x2A80,
+ 0x2B00
+};
+
+pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,
+ u32 sample_rate, void * private_data,
+ u32 hw_dma_addr,
+ int pcm_channel_id)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * src_scb = NULL,* pcm_scb, * mixer_scb = NULL;
+ dsp_scb_descriptor_t * src_parent_scb = NULL;
+
+ /* dsp_scb_descriptor_t * pcm_parent_scb; */
+ char scb_name[DSP_MAX_SCB_NAME];
+ int i,pcm_index = -1, insert_point, src_index = -1,pass_through = 0;
+ unsigned long flags;
+
+ switch (pcm_channel_id) {
+ case DSP_PCM_MAIN_CHANNEL:
+ mixer_scb = ins->master_mix_scb;
+ break;
+ case DSP_PCM_REAR_CHANNEL:
+ mixer_scb = ins->rear_mix_scb;
+ break;
+ case DSP_PCM_CENTER_LFE_CHANNEL:
+ mixer_scb = ins->center_lfe_mix_scb;
+ break;
+ case DSP_PCM_S71_CHANNEL:
+ /* TODO */
+ snd_assert(0);
+ break;
+ case DSP_IEC958_CHANNEL:
+ snd_assert (ins->asynch_tx_scb != NULL, return NULL);
+ mixer_scb = ins->asynch_tx_scb;
+
+ /* if sample rate is set to 48khz we pass
+ the Sample Rate Converted (which could
+ alter the raw data stream ...) */
+ if (sample_rate == 48000) {
+ snd_printdd ("IEC958 pass through\n");
+ /* Hack to bypass creating a new SRC */
+ pass_through = 1;
+ }
+ break;
+ default:
+ snd_assert (0);
+ return NULL;
+ }
+ /* default sample rate is 44100 */
+ if (!sample_rate) sample_rate = 44100;
+
+ /* search for a already created SRC SCB with the same sample rate */
+ for (i = 0; i < DSP_MAX_PCM_CHANNELS &&
+ (pcm_index == -1 || src_scb == NULL); ++i) {
+
+ /* virtual channel reserved
+ for capture */
+ if (i == CS46XX_DSP_CAPTURE_CHANNEL) continue;
+
+ if (ins->pcm_channels[i].active) {
+ if (!src_scb &&
+ ins->pcm_channels[i].sample_rate == sample_rate &&
+ ins->pcm_channels[i].mixer_scb == mixer_scb) {
+ src_scb = ins->pcm_channels[i].src_scb;
+ ins->pcm_channels[i].src_scb->ref_count ++;
+ src_index = ins->pcm_channels[i].src_slot;
+ }
+ } else if (pcm_index == -1) {
+ pcm_index = i;
+ }
+ }
+
+ if (pcm_index == -1) {
+ snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
+ return NULL;
+ }
+
+ if (src_scb == NULL) {
+ if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
+ snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
+ return NULL;
+ }
+
+ /* find a free slot */
+ for (i = 0; i < DSP_MAX_SRC_NR; ++i) {
+ if (ins->src_scb_slots[i] == 0) {
+ src_index = i;
+ ins->src_scb_slots[i] = 1;
+ break;
+ }
+ }
+ snd_assert (src_index != -1,return NULL);
+
+ /* we need to create a new SRC SCB */
+ if (mixer_scb->sub_list_ptr == ins->the_null_scb) {
+ src_parent_scb = mixer_scb;
+ insert_point = SCB_ON_PARENT_SUBLIST_SCB;
+ } else {
+ src_parent_scb = find_next_free_scb(chip,mixer_scb->sub_list_ptr);
+ insert_point = SCB_ON_PARENT_NEXT_SCB;
+ }
+
+ snprintf (scb_name,DSP_MAX_SCB_NAME,"SrcTask_SCB%d",src_index);
+
+ snd_printdd( "dsp_spos: creating SRC \"%s\"\n",scb_name);
+ src_scb = cs46xx_dsp_create_src_task_scb(chip,scb_name,
+ sample_rate,
+ src_output_buffer_addr[src_index],
+ src_delay_buffer_addr[src_index],
+ /* 0x400 - 0x600 source SCBs */
+ 0x400 + (src_index * 0x10) ,
+ src_parent_scb,
+ insert_point,
+ pass_through);
+
+ if (!src_scb) {
+ snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
+ return NULL;
+ }
+
+ /* cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate); */
+
+ ins->nsrc_scb ++;
+ }
+
+
+ snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
+
+ snd_printdd( "dsp_spos: creating PCM \"%s\" (%d)\n",scb_name,
+ pcm_channel_id);
+
+ pcm_scb = cs46xx_dsp_create_pcm_reader_scb(chip,scb_name,
+ pcm_reader_buffer_addr[pcm_index],
+ /* 0x200 - 400 PCMreader SCBs */
+ (pcm_index * 0x10) + 0x200,
+ pcm_index, /* virtual channel 0-31 */
+ hw_dma_addr, /* pcm hw addr */
+ NULL, /* parent SCB ptr */
+ 0 /* insert point */
+ );
+
+ if (!pcm_scb) {
+ snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
+ return NULL;
+ }
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ ins->pcm_channels[pcm_index].sample_rate = sample_rate;
+ ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
+ ins->pcm_channels[pcm_index].src_scb = src_scb;
+ ins->pcm_channels[pcm_index].unlinked = 1;
+ ins->pcm_channels[pcm_index].private_data = private_data;
+ ins->pcm_channels[pcm_index].src_slot = src_index;
+ ins->pcm_channels[pcm_index].active = 1;
+ ins->pcm_channels[pcm_index].pcm_slot = pcm_index;
+ ins->pcm_channels[pcm_index].mixer_scb = mixer_scb;
+ ins->npcm_channels ++;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ return (ins->pcm_channels + pcm_index);
+}
+
+int cs46xx_dsp_pcm_channel_set_period (cs46xx_t * chip,
+ pcm_channel_descriptor_t * pcm_channel,
+ int period_size)
+{
+ u32 temp = snd_cs46xx_peek (chip,pcm_channel->pcm_reader_scb->address << 2);
+ temp &= ~DMA_RQ_C1_SOURCE_SIZE_MASK;
+
+ switch (period_size) {
+ case 2048:
+ temp |= DMA_RQ_C1_SOURCE_MOD1024;
+ break;
+ case 1024:
+ temp |= DMA_RQ_C1_SOURCE_MOD512;
+ break;
+ case 512:
+ temp |= DMA_RQ_C1_SOURCE_MOD256;
+ break;
+ case 256:
+ temp |= DMA_RQ_C1_SOURCE_MOD128;
+ break;
+ case 128:
+ temp |= DMA_RQ_C1_SOURCE_MOD64;
+ break;
+ case 64:
+ temp |= DMA_RQ_C1_SOURCE_MOD32;
+ break;
+ case 32:
+ temp |= DMA_RQ_C1_SOURCE_MOD16;
+ break;
+ default:
+ snd_printdd ("period size (%d) not supported by HW\n", period_size);
+ return -EINVAL;
+ }
+
+ snd_cs46xx_poke (chip,pcm_channel->pcm_reader_scb->address << 2,temp);
+
+ return 0;
+}
+
+int cs46xx_dsp_pcm_ostream_set_period (cs46xx_t * chip,
+ int period_size)
+{
+ u32 temp = snd_cs46xx_peek (chip,WRITEBACK_SCB_ADDR << 2);
+ temp &= ~DMA_RQ_C1_DEST_SIZE_MASK;
+
+ switch (period_size) {
+ case 2048:
+ temp |= DMA_RQ_C1_DEST_MOD1024;
+ break;
+ case 1024:
+ temp |= DMA_RQ_C1_DEST_MOD512;
+ break;
+ case 512:
+ temp |= DMA_RQ_C1_DEST_MOD256;
+ break;
+ case 256:
+ temp |= DMA_RQ_C1_DEST_MOD128;
+ break;
+ case 128:
+ temp |= DMA_RQ_C1_DEST_MOD64;
+ break;
+ case 64:
+ temp |= DMA_RQ_C1_DEST_MOD32;
+ break;
+ case 32:
+ temp |= DMA_RQ_C1_DEST_MOD16;
+ break;
+ default:
+ snd_printdd ("period size (%d) not supported by HW\n", period_size);
+ return -EINVAL;
+ }
+
+ snd_cs46xx_poke (chip,WRITEBACK_SCB_ADDR << 2,temp);
+
+ return 0;
+}
+
+void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ unsigned long flags;
+
+ snd_assert(pcm_channel->active, return );
+ snd_assert(ins->npcm_channels > 0, return );
+ snd_assert(pcm_channel->src_scb->ref_count > 0, return );
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ pcm_channel->unlinked = 1;
+ pcm_channel->active = 0;
+ pcm_channel->private_data = NULL;
+ pcm_channel->src_scb->ref_count --;
+ ins->npcm_channels --;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ cs46xx_dsp_remove_scb(chip,pcm_channel->pcm_reader_scb);
+
+ if (!pcm_channel->src_scb->ref_count) {
+ cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
+
+ snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR,
+ return );
+
+ ins->src_scb_slots[pcm_channel->src_slot] = 0;
+ ins->nsrc_scb --;
+ }
+}
+
+int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ unsigned long flags;
+
+ snd_assert(pcm_channel->active,return -EIO);
+ snd_assert(ins->npcm_channels > 0,return -EIO);
+
+ spin_lock(&pcm_channel->src_scb->lock);
+
+ if (pcm_channel->unlinked) {
+ spin_unlock(&pcm_channel->src_scb->lock);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ pcm_channel->unlinked = 1;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ _dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
+
+ spin_unlock(&pcm_channel->src_scb->lock);
+ return 0;
+}
+
+int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * parent_scb;
+ dsp_scb_descriptor_t * src_scb = pcm_channel->src_scb;
+ unsigned long flags;
+
+ spin_lock(&pcm_channel->src_scb->lock);
+
+ if (pcm_channel->unlinked == 0) {
+ spin_unlock(&pcm_channel->src_scb->lock);
+ return -EIO;
+ }
+
+ parent_scb = src_scb;
+
+ if (src_scb->sub_list_ptr != ins->the_null_scb) {
+ src_scb->sub_list_ptr->parent_scb_ptr = pcm_channel->pcm_reader_scb;
+ pcm_channel->pcm_reader_scb->next_scb_ptr = src_scb->sub_list_ptr;
+ }
+
+ src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
+
+ snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
+ pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+
+ /* update SCB entry in DSP RAM */
+ cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb);
+
+ /* update parent SCB entry */
+ cs46xx_dsp_spos_update_scb(chip,parent_scb);
+
+ pcm_channel->unlinked = 0;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ spin_unlock(&pcm_channel->src_scb->lock);
+ return 0;
+}
+
+dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
+ u16 addr,char * scb_name)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * parent;
+ dsp_scb_descriptor_t * pcm_input;
+ int insert_point;
+
+ snd_assert (ins->record_mixer_scb != NULL,return NULL);
+
+ if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) {
+ parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr);
+ insert_point = SCB_ON_PARENT_NEXT_SCB;
+ } else {
+ parent = ins->record_mixer_scb;
+ insert_point = SCB_ON_PARENT_SUBLIST_SCB;
+ }
+
+ pcm_input = cs46xx_dsp_create_pcm_serial_input_scb(chip,scb_name,addr,
+ source, parent,
+ insert_point);
+
+ return pcm_input;
+}
+
+int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src)
+{
+ snd_assert (src->parent_scb_ptr != NULL, return -EINVAL );
+
+ /* mute SCB */
+ cs46xx_dsp_scb_set_volume (chip,src,0,0);
+
+ _dsp_unlink_scb (chip,src);
+
+ return 0;
+}
+
+int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+ dsp_scb_descriptor_t * parent_scb;
+
+ snd_assert (src->parent_scb_ptr == NULL, return -EINVAL );
+ snd_assert(ins->master_mix_scb !=NULL, return -EINVAL );
+
+ if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
+ parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
+ parent_scb->next_scb_ptr = src;
+ } else {
+ parent_scb = ins->master_mix_scb;
+ parent_scb->sub_list_ptr = src;
+ }
+
+ src->parent_scb_ptr = parent_scb;
+
+ /* update entry in DSP RAM */
+ cs46xx_dsp_spos_update_scb(chip,parent_scb);
+
+ return 0;
+}
+
+int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) {
+ cs46xx_dsp_enable_spdif_hw (chip);
+ }
+
+ /* dont touch anything if SPDIF is open */
+ if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
+ /* when cs46xx_iec958_post_close(...) is called it
+ will call this function if necessary depending on
+ this bit */
+ ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+
+ return -EBUSY;
+ }
+
+ snd_assert (ins->asynch_tx_scb == NULL, return -EINVAL);
+ snd_assert (ins->master_mix_scb->next_scb_ptr == ins->the_null_scb, return -EINVAL);
+
+ /* reset output snooper sample buffer pointer */
+ snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2,
+ (OUTPUT_SNOOP_BUFFER + 0x10) << 0x10 );
+
+ /* The asynch. transfer task */
+ ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
+ SPDIFO_SCB_INST,
+ SPDIFO_IP_OUTPUT_BUFFER1,
+ ins->master_mix_scb,
+ SCB_ON_PARENT_NEXT_SCB);
+ if (!ins->asynch_tx_scb) return -ENOMEM;
+
+ ins->spdif_pcm_input_scb = cs46xx_dsp_create_pcm_serial_input_scb(chip,"PCMSerialInput_II",
+ PCMSERIALINII_SCB_ADDR,
+ ins->ref_snoop_scb,
+ ins->asynch_tx_scb,
+ SCB_ON_PARENT_SUBLIST_SCB);
+
+
+ if (!ins->spdif_pcm_input_scb) return -ENOMEM;
+
+ /* monitor state */
+ ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+
+ return 0;
+}
+
+int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ /* dont touch anything if SPDIF is open */
+ if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
+ ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+ return -EBUSY;
+ }
+
+ /* check integrety */
+ snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
+ snd_assert (ins->spdif_pcm_input_scb != NULL,return -EINVAL);
+ snd_assert (ins->master_mix_scb->next_scb_ptr == ins->asynch_tx_scb, return -EINVAL);
+ snd_assert (ins->asynch_tx_scb->parent_scb_ptr == ins->master_mix_scb, return -EINVAL);
+
+ cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
+ cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
+
+ ins->spdif_pcm_input_scb = NULL;
+ ins->asynch_tx_scb = NULL;
+
+ /* clear buffer to prevent any undesired noise */
+ _dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256);
+
+ /* monitor state */
+ ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+
+
+ return 0;
+}
+
+int cs46xx_iec958_pre_open (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
+ /* remove AsynchFGTxSCB and and PCMSerialInput_II */
+ cs46xx_dsp_disable_spdif_out (chip);
+
+ /* save state */
+ ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+ }
+
+ /* if not enabled already */
+ if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) {
+ cs46xx_dsp_enable_spdif_hw (chip);
+ }
+
+ /* Create the asynch. transfer task for playback */
+ ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
+ SPDIFO_SCB_INST,
+ SPDIFO_IP_OUTPUT_BUFFER1,
+ ins->master_mix_scb,
+ SCB_ON_PARENT_NEXT_SCB);
+
+
+ /* set spdif channel status value for streaming */
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_stream);
+
+ ins->spdif_status_out |= DSP_SPDIF_STATUS_PLAYBACK_OPEN;
+
+ return 0;
+}
+
+int cs46xx_iec958_post_close (cs46xx_t *chip)
+{
+ dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+ snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
+
+ ins->spdif_status_out &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN;
+
+ /* restore settings */
+ cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);
+
+ /* deallocate stuff */
+ if (ins->spdif_pcm_input_scb != NULL) {
+ cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
+ ins->spdif_pcm_input_scb = NULL;
+ }
+
+ cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
+ ins->asynch_tx_scb = NULL;
+
+ /* clear buffer to prevent any undesired noise */
+ _dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256);
+
+ /* restore state */
+ if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
+ cs46xx_dsp_enable_spdif_out (chip);
+ }
+
+ return 0;
+}
diff --git a/sound/pci/cs46xx/imgs/cwc4630.h b/sound/pci/cs46xx/imgs/cwc4630.h
new file mode 100644
index 00000000000..8bed07f9996
--- /dev/null
+++ b/sound/pci/cs46xx/imgs/cwc4630.h
@@ -0,0 +1,320 @@
+/* generated from cwc4630.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwc4630_H__
+#define __HEADER_cwc4630_H__
+
+static symbol_entry_t cwc4630_symbols[] = {
+ { 0x0000, "BEGINADDRESS",0x00 },
+ { 0x8000, "EXECCHILD",0x03 },
+ { 0x8001, "EXECCHILD_98",0x03 },
+ { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+ { 0x8008, "EXECSIBLING",0x03 },
+ { 0x800a, "EXECSIBLING_298",0x03 },
+ { 0x800b, "EXECSIBLING_2IND1",0x03 },
+ { 0x8010, "TIMINGMASTER",0x03 },
+ { 0x804f, "S16_CODECINPUTTASK",0x03 },
+ { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+ { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+ { 0x809a, "S16_MIX",0x03 },
+ { 0x80bb, "S16_UPSRC",0x03 },
+ { 0x813b, "MIX3_EXP",0x03 },
+ { 0x8164, "DECIMATEBYPOW2",0x03 },
+ { 0x8197, "VARIDECIMATE",0x03 },
+ { 0x81f2, "_3DINPUTTASK",0x03 },
+ { 0x820a, "_3DPRLGCINPTASK",0x03 },
+ { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+ { 0x8242, "_3DOUTPUTTASK",0x03 },
+ { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+ { 0x82c6, "WAIT4DATA",0x03 },
+ { 0x82fa, "PROLOGIC",0x03 },
+ { 0x8496, "DECORRELATOR",0x03 },
+ { 0x84a4, "STEREO2MONO",0x03 },
+ { 0x0070, "SPOSCB",0x02 },
+ { 0x0107, "TASKTREETHREAD",0x03 },
+ { 0x013c, "TASKTREEHEADERCODE",0x03 },
+ { 0x0145, "FGTASKTREEHEADERCODE",0x03 },
+ { 0x0169, "NULLALGORITHM",0x03 },
+ { 0x016d, "HFGEXECCHILD",0x03 },
+ { 0x016e, "HFGEXECCHILD_98",0x03 },
+ { 0x0170, "HFGEXECCHILD_PUSH1IND",0x03 },
+ { 0x0173, "HFGEXECSIBLING",0x03 },
+ { 0x0175, "HFGEXECSIBLING_298",0x03 },
+ { 0x0176, "HFGEXECSIBLING_2IND1",0x03 },
+ { 0x0179, "S16_CODECOUTPUTTASK",0x03 },
+ { 0x0194, "#CODE_END",0x00 },
+}; /* cwc4630 symbols */
+
+static u32 cwc4630_code[] = {
+/* BEGINADDRESS */
+/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003,
+/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003,
+/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003,
+/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003,
+/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003,
+/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003,
+/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003,
+/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003,
+/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003,
+/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040,
+/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003,
+/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003,
+/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003,
+/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003,
+/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003,
+/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003,
+/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003,
+/* 0040 */ 0x0001a730,0x00001008,0x000e2730,0x00001002,
+/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003,
+/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000,
+/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000,
+/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000,
+/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003,
+/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003,
+/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003,
+/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000,
+/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000,
+/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000,
+/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140,
+/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00,
+/* 007C */ 0x0000002e,0x0009d6c0,0x0002ef8a,0x00000000,
+/* 007E */ 0x00040630,0x00001004,0x0004ef0a,0x000eb785,
+/* 0080 */ 0x0003fc8a,0x00000000,0x00000000,0x000c70e0,
+/* 0082 */ 0x0007d182,0x0002c640,0x00008630,0x00001004,
+/* 0084 */ 0x000799b8,0x0002c6c0,0x00031705,0x00092240,
+/* 0086 */ 0x00039f05,0x000932c0,0x0003520a,0x00000000,
+/* 0088 */ 0x00070731,0x0000100b,0x00010705,0x000b20c0,
+/* 008A */ 0x00000000,0x000eba44,0x00032108,0x000c60c4,
+/* 008C */ 0x00065208,0x000c2917,0x000486b0,0x00001007,
+/* 008E */ 0x00012f05,0x00036880,0x0002818e,0x000c0000,
+/* 0090 */ 0x0004410a,0x00000000,0x00048630,0x00001007,
+/* 0092 */ 0x00029705,0x000c0000,0x00000000,0x00000000,
+/* 0094 */ 0x00003fc1,0x0003fc40,0x000037c1,0x00091b40,
+/* 0096 */ 0x00003fc1,0x000911c0,0x000037c1,0x000957c0,
+/* 0098 */ 0x00003fc1,0x000951c0,0x000037c1,0x00000000,
+/* 009A */ 0x00003fc1,0x000991c0,0x000037c1,0x00000000,
+/* 009C */ 0x00003fc1,0x0009d1c0,0x000037c1,0x00000000,
+/* 009E */ 0x0001ccc1,0x000915c0,0x0001c441,0x0009d800,
+/* 00A0 */ 0x0009cdc1,0x00091240,0x0001c541,0x00091d00,
+/* 00A2 */ 0x0009cfc1,0x00095240,0x0001c741,0x00095c80,
+/* 00A4 */ 0x000e8ca9,0x00099240,0x000e85ad,0x00095640,
+/* 00A6 */ 0x00069ca9,0x00099d80,0x000e952d,0x00099640,
+/* 00A8 */ 0x000eaca9,0x0009d6c0,0x000ea5ad,0x00091a40,
+/* 00AA */ 0x0006bca9,0x0009de80,0x000eb52d,0x00095a40,
+/* 00AC */ 0x000ecca9,0x00099ac0,0x000ec5ad,0x0009da40,
+/* 00AE */ 0x000edca9,0x0009d300,0x000a6e0a,0x00001000,
+/* 00B0 */ 0x000ed52d,0x00091e40,0x000eeca9,0x00095ec0,
+/* 00B2 */ 0x000ee5ad,0x00099e40,0x0006fca9,0x00002500,
+/* 00B4 */ 0x000fb208,0x000c59a0,0x000ef52d,0x0009de40,
+/* 00B6 */ 0x00068ca9,0x000912c1,0x000683ad,0x00095241,
+/* 00B8 */ 0x00020f05,0x000991c1,0x00000000,0x00000000,
+/* 00BA */ 0x00086f88,0x00001000,0x0009cf81,0x000b5340,
+/* 00BC */ 0x0009c701,0x000b92c0,0x0009de81,0x000bd300,
+/* 00BE */ 0x0009d601,0x000b1700,0x0001fd81,0x000b9d80,
+/* 00C0 */ 0x0009f501,0x000b57c0,0x000a0f81,0x000bd740,
+/* 00C2 */ 0x00020701,0x000b5c80,0x000a1681,0x000b97c0,
+/* 00C4 */ 0x00021601,0x00002500,0x000a0701,0x000b9b40,
+/* 00C6 */ 0x000a0f81,0x000b1bc0,0x00021681,0x00002d00,
+/* 00C8 */ 0x00020f81,0x000bd800,0x000a0701,0x000b5bc0,
+/* 00CA */ 0x00021601,0x00003500,0x000a0f81,0x000b5f40,
+/* 00CC */ 0x000a0701,0x000bdbc0,0x00021681,0x00003d00,
+/* 00CE */ 0x00020f81,0x000b1d00,0x000a0701,0x000b1fc0,
+/* 00D0 */ 0x00021601,0x00020500,0x00020f81,0x000b1341,
+/* 00D2 */ 0x000a0701,0x000b9fc0,0x00021681,0x00020d00,
+/* 00D4 */ 0x00020f81,0x000bde80,0x000a0701,0x000bdfc0,
+/* 00D6 */ 0x00021601,0x00021500,0x00020f81,0x000b9341,
+/* 00D8 */ 0x00020701,0x000b53c1,0x00021681,0x00021d00,
+/* 00DA */ 0x000a0f81,0x000d0380,0x0000b601,0x000b15c0,
+/* 00DC */ 0x00007b01,0x00000000,0x00007b81,0x000bd1c0,
+/* 00DE */ 0x00007b01,0x00000000,0x00007b81,0x000b91c0,
+/* 00E0 */ 0x00007b01,0x000b57c0,0x00007b81,0x000b51c0,
+/* 00E2 */ 0x00007b01,0x000b1b40,0x00007b81,0x000b11c0,
+/* 00E4 */ 0x00087b01,0x000c3dc0,0x0007e488,0x000d7e45,
+/* 00E6 */ 0x00000000,0x000d7a44,0x0007e48a,0x00000000,
+/* 00E8 */ 0x00011f05,0x00084080,0x00000000,0x00000000,
+/* 00EA */ 0x00001705,0x000b3540,0x00008a01,0x000bf040,
+/* 00EC */ 0x00007081,0x000bb5c0,0x00055488,0x00000000,
+/* 00EE */ 0x0000d482,0x0003fc40,0x0003fc88,0x00000000,
+/* 00F0 */ 0x0001e401,0x000b3a00,0x0001ec81,0x000bd6c0,
+/* 00F2 */ 0x0002ef88,0x000e7784,0x00056f08,0x00000000,
+/* 00F4 */ 0x000d86b0,0x00001007,0x00008281,0x000bb240,
+/* 00F6 */ 0x0000b801,0x000b7140,0x00007888,0x00000000,
+/* 00F8 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000,
+/* 00FA */ 0x00000000,0x00000000,0x00055288,0x000c555c,
+/* 00FC */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+/* 00FE */ 0x0000fa88,0x00000000,0x00000032,0x00001000,
+/* 0100 */ 0x0000073d,0x00001000,0x0007f188,0x000c0000,
+/* 0102 */ 0x00000000,0x00000000,0x0008c01c,0x00001003,
+/* 0104 */ 0x00002705,0x00001008,0x0008b201,0x000c1392,
+/* 0106 */ 0x0000ba01,0x00000000,
+/* TASKTREETHREAD */
+/* 0107 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4,
+/* 0109 */ 0x00057488,0x00000000,0x000a6388,0x00001001,
+/* 010B */ 0x0008b334,0x000bc141,0x0003020e,0x00000000,
+/* 010D */ 0x000986b0,0x00001008,0x00003625,0x000c5dfa,
+/* 010F */ 0x000a638a,0x00001001,0x0008020e,0x00001002,
+/* 0111 */ 0x0009a6b0,0x00001008,0x0007f301,0x00000000,
+/* 0113 */ 0x00000000,0x00000000,0x00002725,0x000a8c40,
+/* 0115 */ 0x000000ae,0x00000000,0x000e8630,0x00001008,
+/* 0117 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640,
+/* 0119 */ 0x000b8630,0x00001008,0x000799b8,0x0002d6c0,
+/* 011B */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000,
+/* 011D */ 0x00062208,0x000c4117,0x000a0630,0x00001009,
+/* 011F */ 0x00000000,0x000c0000,0x0001022e,0x00000000,
+/* 0121 */ 0x0006a630,0x00001009,0x00000032,0x00001000,
+/* 0123 */ 0x000ca21c,0x00001003,0x00005a02,0x00000000,
+/* 0125 */ 0x0001a630,0x00001009,0x00000000,0x000c0000,
+/* 0127 */ 0x00000036,0x00001000,0x00000000,0x00000000,
+/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012B */ 0x00000000,0x00000000,0x0003a730,0x00001008,
+/* 012D */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 012F */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0131 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0133 */ 0x0003a730,0x00001008,0x00000033,0x00001000,
+/* 0135 */ 0x0003a705,0x00001008,0x00007a01,0x000c0000,
+/* 0137 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000,
+/* 0139 */ 0x00090730,0x0000100a,0x00000000,0x000c0000,
+/* 013B */ 0x00000000,0x00000000,
+/* TASKTREEHEADERCODE */
+/* 013C */ 0x0007aab0,0x00034880,0x000a8fb0,0x0000100b,
+/* 013E */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 0140 */ 0x000183ae,0x00000000,0x000a86b0,0x0000100b,
+/* 0142 */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+/* 0144 */ 0x00042731,0x00001003,
+/* FGTASKTREEHEADERCODE */
+/* 0145 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100a,
+/* 0147 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 0149 */ 0x000183ae,0x00000000,0x000b06b0,0x0000100b,
+/* 014B */ 0x00022f05,0x00000000,0x00007401,0x00091140,
+/* 014D */ 0x00048f05,0x000951c0,0x00042731,0x00001003,
+/* 014F */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 0151 */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003,
+/* 0153 */ 0x00000000,0x00000000,0x0008e19c,0x00001003,
+/* 0155 */ 0x000083c1,0x00093040,0x00000f41,0x00097140,
+/* 0157 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0159 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 015B */ 0x00000000,0x000fdc44,0x00055208,0x00000000,
+/* 015D */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00,
+/* 015F */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000,
+/* 0161 */ 0x00012f05,0x00036880,0x00065308,0x000c2997,
+/* 0163 */ 0x000086b0,0x0000100b,0x0004410a,0x000d40c7,
+/* 0165 */ 0x00000000,0x00000000,0x00088730,0x00001004,
+/* 0167 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000,
+/* NULLALGORITHM */
+/* 0169 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 016B */ 0x00080000,0x000bffc7,0x0000273d,0x00001000,
+/* HFGEXECCHILD */
+/* 016D */ 0x00000000,0x000eba44,
+/* HFGEXECCHILD_98 */
+/* 016E */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+/* HFGEXECCHILD_PUSH1IND */
+/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 0172 */ 0x00006a88,0x000c75c4,
+/* HFGEXECSIBLING */
+/* 0173 */ 0x00000000,0x000e5084,0x00000000,0x000eba44,
+/* HFGEXECSIBLING_298 */
+/* 0175 */ 0x00087401,0x000e4782,
+/* HFGEXECSIBLING_2IND1 */
+/* 0176 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 0178 */ 0x00006a88,0x000c75c4,
+/* S16_CODECOUTPUTTASK */
+/* 0179 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40,
+/* 017B */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80,
+/* 017D */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0,
+/* 017F */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0,
+/* 0181 */ 0x00073fb0,0x00074c80,0x000583a0,0x0000100c,
+/* 0183 */ 0x000ee388,0x00042970,0x00008301,0x00021ef2,
+/* 0185 */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b,
+/* 0187 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916,
+/* 0189 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000,
+/* 018B */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b,
+/* 018D */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956,
+/* 018F */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40,
+/* 0191 */ 0x00058730,0x00001400,0x000d7488,0x000c3a00,
+/* 0193 */ 0x00048f05,0x00000000
+};
+/* #CODE_END */
+
+static u32 cwc4630_parameter[] = {
+/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000
+}; /* #PARAMETER_END */
+
+
+static segment_desc_t cwc4630_segments[] = {
+ { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000328, cwc4630_code },
+ { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000080, cwc4630_parameter },
+};
+
+static dsp_module_desc_t cwc4630_module = {
+ "cwc4630",
+ {
+ 38,
+ cwc4630_symbols
+ },
+ 2,
+ cwc4630_segments,
+};
+
+#endif /* __HEADER_cwc4630_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcasync.h b/sound/pci/cs46xx/imgs/cwcasync.h
new file mode 100644
index 00000000000..e01a7b66c4f
--- /dev/null
+++ b/sound/pci/cs46xx/imgs/cwcasync.h
@@ -0,0 +1,176 @@
+/* generated from cwcasync.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcasync_H__
+#define __HEADER_cwcasync_H__
+
+static symbol_entry_t cwcasync_symbols[] = {
+ { 0x8000, "EXECCHILD",0x03 },
+ { 0x8001, "EXECCHILD_98",0x03 },
+ { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+ { 0x8008, "EXECSIBLING",0x03 },
+ { 0x800a, "EXECSIBLING_298",0x03 },
+ { 0x800b, "EXECSIBLING_2IND1",0x03 },
+ { 0x8010, "TIMINGMASTER",0x03 },
+ { 0x804f, "S16_CODECINPUTTASK",0x03 },
+ { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+ { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+ { 0x809a, "S16_MIX",0x03 },
+ { 0x80bb, "S16_UPSRC",0x03 },
+ { 0x813b, "MIX3_EXP",0x03 },
+ { 0x8164, "DECIMATEBYPOW2",0x03 },
+ { 0x8197, "VARIDECIMATE",0x03 },
+ { 0x81f2, "_3DINPUTTASK",0x03 },
+ { 0x820a, "_3DPRLGCINPTASK",0x03 },
+ { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+ { 0x8242, "_3DOUTPUTTASK",0x03 },
+ { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+ { 0x82c6, "WAIT4DATA",0x03 },
+ { 0x82fa, "PROLOGIC",0x03 },
+ { 0x8496, "DECORRELATOR",0x03 },
+ { 0x84a4, "STEREO2MONO",0x03 },
+ { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
+ { 0x0000, "SPIOWRITE",0x03 },
+ { 0x000d, "S16_ASYNCCODECINPUTTASK",0x03 },
+ { 0x0043, "SPDIFITASK",0x03 },
+ { 0x007b, "SPDIFOTASK",0x03 },
+ { 0x0097, "ASYNCHFGTXCODE",0x03 },
+ { 0x00be, "ASYNCHFGRXCODE",0x03 },
+ { 0x00db, "#CODE_END",0x00 },
+}; /* cwcasync symbols */
+
+static u32 cwcasync_code[] = {
+/* OVERLAYBEGINADDRESS */
+/* 0000 */ 0x00002731,0x00001400,0x00003725,0x000a8440,
+/* 0002 */ 0x000000ae,0x00000000,0x00060630,0x00001000,
+/* 0004 */ 0x00000000,0x000c7560,0x00075282,0x0002d640,
+/* 0006 */ 0x00021705,0x00000000,0x00072ab8,0x0002d6c0,
+/* 0008 */ 0x00020630,0x00001000,0x000c74c2,0x000d4b82,
+/* 000A */ 0x000475c2,0x00000000,0x0003430a,0x000c0000,
+/* 000C */ 0x00042730,0x00001400,
+/* S16_ASYNCCODECINPUTTASK */
+/* 000D */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x00000000,
+/* 000F */ 0x000fa418,0x0000101f,0x0005d402,0x0001c500,
+/* 0011 */ 0x000f0630,0x00001000,0x00004418,0x00001380,
+/* 0013 */ 0x000e243d,0x000d394a,0x00049705,0x00000000,
+/* 0015 */ 0x0007d530,0x000b4240,0x000e00f2,0x00001000,
+/* 0017 */ 0x00009134,0x000ca20a,0x00004c90,0x00001000,
+/* 0019 */ 0x0005d705,0x00000000,0x00004f25,0x00098240,
+/* 001B */ 0x00004725,0x00000000,0x0000e48a,0x00000000,
+/* 001D */ 0x00027295,0x0009c2c0,0x0003df25,0x00000000,
+/* 001F */ 0x000e8030,0x00001001,0x0005f718,0x000ac600,
+/* 0021 */ 0x0007cf30,0x000c2a01,0x00082630,0x00001001,
+/* 0023 */ 0x000504a0,0x00001001,0x00029314,0x000bcb80,
+/* 0025 */ 0x0003cf25,0x000b0e00,0x0004f5c0,0x00000000,
+/* 0027 */ 0x00049118,0x000d888a,0x0007dd02,0x000c6efa,
+/* 0029 */ 0x00000000,0x00000000,0x0004f5c0,0x00069c80,
+/* 002B */ 0x0000d402,0x00000000,0x000e8630,0x00001001,
+/* 002D */ 0x00079130,0x00000000,0x00049118,0x00090e00,
+/* 002F */ 0x0006c10a,0x00000000,0x00000000,0x000c0000,
+/* 0031 */ 0x0007cf30,0x00030580,0x00005725,0x00000000,
+/* 0033 */ 0x000d84a0,0x00001001,0x00029314,0x000b4780,
+/* 0035 */ 0x0003cf25,0x000b8600,0x00000000,0x00000000,
+/* 0037 */ 0x00000000,0x000c0000,0x00000000,0x00042c80,
+/* 0039 */ 0x0001dec1,0x000e488c,0x00031114,0x00000000,
+/* 003B */ 0x0004f5c2,0x00000000,0x0003640a,0x00000000,
+/* 003D */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
+/* 003F */ 0x00007001,0x00000000,0x00000734,0x00001000,
+/* 0041 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
+/* SPDIFITASK */
+/* 0043 */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x000d5384,
+/* 0045 */ 0x0007e48a,0x00000000,0x00067718,0x00001000,
+/* 0047 */ 0x0007a418,0x00001000,0x0007221a,0x00000000,
+/* 0049 */ 0x0005d402,0x00014500,0x000b8630,0x00001002,
+/* 004B */ 0x00004418,0x00001780,0x000e243d,0x000d394a,
+/* 004D */ 0x00049705,0x00000000,0x0007d530,0x000b4240,
+/* 004F */ 0x000ac0f2,0x00001002,0x00014414,0x00000000,
+/* 0051 */ 0x00004c90,0x00001000,0x0005d705,0x00000000,
+/* 0053 */ 0x00004f25,0x00098240,0x00004725,0x00000000,
+/* 0055 */ 0x0000e48a,0x00000000,0x00027295,0x0009c2c0,
+/* 0057 */ 0x0007df25,0x00000000,0x000ac030,0x00001003,
+/* 0059 */ 0x0005f718,0x000fe798,0x00029314,0x000bcb80,
+/* 005B */ 0x00000930,0x000b0e00,0x0004f5c0,0x000de204,
+/* 005D */ 0x000884a0,0x00001003,0x0007cf25,0x000e3560,
+/* 005F */ 0x00049118,0x00000000,0x00049118,0x000d888a,
+/* 0061 */ 0x0007dd02,0x000c6efa,0x0000c434,0x00030040,
+/* 0063 */ 0x000fda82,0x000c2312,0x000fdc0e,0x00001001,
+/* 0065 */ 0x00083402,0x000c2b92,0x000706b0,0x00001003,
+/* 0067 */ 0x00075a82,0x00000000,0x0000d625,0x000b0940,
+/* 0069 */ 0x0000840e,0x00001002,0x0000aabc,0x000c511e,
+/* 006B */ 0x00078730,0x00001003,0x0000aaf4,0x000e910a,
+/* 006D */ 0x0004628a,0x00000000,0x00006aca,0x00000000,
+/* 006F */ 0x00000930,0x00000000,0x0004f5c0,0x00069c80,
+/* 0071 */ 0x00046ac0,0x00000000,0x0003c40a,0x000fc898,
+/* 0073 */ 0x00049118,0x00090e00,0x0006c10a,0x00000000,
+/* 0075 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
+/* 0077 */ 0x00007001,0x00000000,0x00000734,0x00001000,
+/* 0079 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
+/* SPDIFOTASK */
+/* 007B */ 0x0006a108,0x000c0000,0x0004f4c0,0x000c3245,
+/* 007D */ 0x0000a418,0x00001000,0x0003a20a,0x00000000,
+/* 007F */ 0x00004418,0x00001380,0x000e243d,0x000d394a,
+/* 0081 */ 0x000c9705,0x000def92,0x0008c030,0x00001004,
+/* 0083 */ 0x0005f718,0x000fe798,0x00000000,0x000c0000,
+/* 0085 */ 0x00005725,0x00000000,0x000704a0,0x00001004,
+/* 0087 */ 0x00029314,0x000b4780,0x0003cf25,0x000b8600,
+/* 0089 */ 0x00000000,0x00000000,0x00000000,0x000c0000,
+/* 008B */ 0x00000000,0x00042c80,0x0001dec1,0x000e488c,
+/* 008D */ 0x00031114,0x00000000,0x0004f5c2,0x00000000,
+/* 008F */ 0x0004a918,0x00098600,0x0006c28a,0x00000000,
+/* 0091 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
+/* 0093 */ 0x00007001,0x00000000,0x00000734,0x00001000,
+/* 0095 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
+/* ASYNCHFGTXCODE */
+/* 0097 */ 0x0002a880,0x000b4e40,0x00042214,0x000e5548,
+/* 0099 */ 0x000542bf,0x00000000,0x00000000,0x000481c0,
+/* 009B */ 0x00000000,0x00000000,0x00000000,0x00000030,
+/* 009D */ 0x0000072d,0x000fbf8a,0x00077f94,0x000ea7df,
+/* 009F */ 0x0002ac95,0x000d3145,0x00002731,0x00001400,
+/* 00A1 */ 0x00006288,0x000c71c4,0x00014108,0x000e6044,
+/* 00A3 */ 0x00035408,0x00000000,0x00025418,0x000a0ec0,
+/* 00A5 */ 0x0001443d,0x000ca21e,0x00046595,0x000d730c,
+/* 00A7 */ 0x0006538e,0x00000000,0x00064630,0x00001005,
+/* 00A9 */ 0x000e7b0e,0x000df782,0x000746b0,0x00001005,
+/* 00AB */ 0x00036f05,0x000c0000,0x00043695,0x000d598c,
+/* 00AD */ 0x0005331a,0x000f2185,0x00000000,0x00000000,
+/* 00AF */ 0x000007ae,0x000bdb00,0x00040630,0x00001400,
+/* 00B1 */ 0x0005e708,0x000c0000,0x0007ef30,0x000b1c00,
+/* 00B3 */ 0x000d86a0,0x00001005,0x00066408,0x000c0000,
+/* 00B5 */ 0x00000000,0x00000000,0x00021843,0x00000000,
+/* 00B7 */ 0x00000cac,0x00062c00,0x00001dac,0x00063400,
+/* 00B9 */ 0x00002cac,0x0006cc80,0x000db943,0x000e5ca1,
+/* 00BB */ 0x00000000,0x00000000,0x0006680a,0x000f3205,
+/* 00BD */ 0x00042730,0x00001400,
+/* ASYNCHFGRXCODE */
+/* 00BE */ 0x00014108,0x000f2204,0x00025418,0x000a2ec0,
+/* 00C0 */ 0x00015dbd,0x00038100,0x00015dbc,0x00000000,
+/* 00C2 */ 0x0005e415,0x00034880,0x0001258a,0x000d730c,
+/* 00C4 */ 0x0006538e,0x000baa40,0x00060630,0x00001006,
+/* 00C6 */ 0x00067b0e,0x000ac380,0x0003ef05,0x00000000,
+/* 00C8 */ 0x0000f734,0x0001c300,0x000586b0,0x00001400,
+/* 00CA */ 0x000b6f05,0x000c3a00,0x00048f05,0x00000000,
+/* 00CC */ 0x0005b695,0x0008c380,0x0002058e,0x00000000,
+/* 00CE */ 0x000500b0,0x00001400,0x0002b318,0x000e998d,
+/* 00D0 */ 0x0006430a,0x00000000,0x00000000,0x000ef384,
+/* 00D2 */ 0x00004725,0x000c0000,0x00000000,0x000f3204,
+/* 00D4 */ 0x00004f25,0x000c0000,0x00080000,0x000e5ca1,
+/* 00D6 */ 0x000cb943,0x000e5ca1,0x0004b943,0x00000000,
+/* 00D8 */ 0x00040730,0x00001400,0x000cb943,0x000e5ca1,
+/* 00DA */ 0x0004b943,0x00000000
+};
+/* #CODE_END */
+
+static segment_desc_t cwcasync_segments[] = {
+ { SEGTYPE_SP_PROGRAM, 0x00000000, 0x000001b6, cwcasync_code },
+};
+
+static dsp_module_desc_t cwcasync_module = {
+ "cwcasync",
+ {
+ 32,
+ cwcasync_symbols
+ },
+ 1,
+ cwcasync_segments,
+};
+
+#endif /* __HEADER_cwcasync_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcbinhack.h b/sound/pci/cs46xx/imgs/cwcbinhack.h
new file mode 100644
index 00000000000..436b38bd246
--- /dev/null
+++ b/sound/pci/cs46xx/imgs/cwcbinhack.h
@@ -0,0 +1,48 @@
+/* generated by Benny
+ MODIFY ON YOUR OWN RISK */
+
+#ifndef __HEADER_cwcbinhack_H__
+#define __HEADER_cwcbinhack_H__
+
+static symbol_entry_t cwcbinhack_symbols[] = {
+ { 0x02c8, "OVERLAYBEGINADDRESS",0x00 },
+ { 0x02c8, "MAGICSNOOPTASK",0x03 },
+ { 0x0308, "#CODE_END",0x00 },
+}; /* cwcbinhack symbols */
+
+static u32 cwcbinhack_code[] = {
+ /* 0x02c8 */
+ 0x0007bfb0,0x000bc240,0x00000c2e,0x000c6084, /* 1 */
+ 0x000b8630,0x00001016,0x00006408,0x000efb84, /* 2 */
+ 0x00016008,0x00000000,0x0001c088,0x000c0000, /* 3 */
+ 0x000fc908,0x000e3392,0x0005f488,0x000efb84, /* 4 */
+ 0x0001d402,0x000b2e00,0x0003d418,0x00001000, /* 5 */
+ 0x0008d574,0x000c4293,0x00065625,0x000ea30e, /* 6 */
+ 0x00096c01,0x000c6f92,0x0001a58a,0x000c6085, /* 7 */
+ 0x00002f43,0x00000000,0x000e03a0,0x00001016, /* 8 */
+ 0x0005e608,0x000c0000,0x00000000,0x00000000, /* 9 */
+ 0x000ca108,0x000dcca1,0x00003bac,0x000c3205, /* 10 */
+ 0x00073843,0x00000000,0x00010730,0x00001017, /* 11 */
+ 0x0001600a,0x000c0000,0x00057488,0x00000000, /* 12 */
+ 0x00000000,0x000e5084,0x00000000,0x000eba44, /* 13 */
+ 0x00087401,0x000e4782,0x00000734,0x00001000, /* 14 */
+ 0x00010705,0x000a6880,0x00006a88,0x000c75c4, /* 15 */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* 16 */
+};
+/* #CODE_END */
+
+static segment_desc_t cwcbinhack_segments[] = {
+ { SEGTYPE_SP_PROGRAM, 0x00000000, 64, cwcbinhack_code },
+};
+
+static dsp_module_desc_t cwcbinhack_module = {
+ "cwcbinhack",
+ {
+ 3,
+ cwcbinhack_symbols
+ },
+ 1,
+ cwcbinhack_segments,
+};
+
+#endif /* __HEADER_cwcbinhack_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcdma.asp b/sound/pci/cs46xx/imgs/cwcdma.asp
new file mode 100644
index 00000000000..09d24c76f03
--- /dev/null
+++ b/sound/pci/cs46xx/imgs/cwcdma.asp
@@ -0,0 +1,169 @@
+//
+// Copyright(c) by Benny Sjostrand (benny@hostmobility.com)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+
+
+//
+// This code runs inside the DSP (cs4610, cs4612, cs4624, or cs4630),
+// to compile it you need a tool named SPASM 3.0 and DSP code owned by
+// Cirrus Logic(R). The SPASM program will generate a object file (cwcdma.osp),
+// the "ospparser" tool will genereate the cwcdma.h file it's included from
+// the cs46xx_lib.c file.
+//
+//
+// The purpose of this code is very simple: make it possible to tranfser
+// the samples 'as they are' with no alteration from a PCMreader SCB (DMA from host)
+// to any other SCB. This is useful for AC3 throug SPDIF. SRC (source rate converters)
+// task always alters the samples in some how, however it's from 48khz -> 48khz. The
+// alterations are not audible, but AC3 wont work.
+//
+// ...
+// |
+// +---------------+
+// | AsynchFGTxSCB |
+// +---------------+
+// |
+// subListPtr
+// |
+// +--------------+
+// | DMAReader |
+// +--------------+
+// |
+// subListPtr
+// |
+// +-------------+
+// | PCMReader |
+// +-------------+
+// (DMA from host)
+//
+
+struct dmaSCB
+ {
+ long dma_reserved1[3];
+
+ short dma_reserved2:dma_outBufPtr;
+
+ short dma_unused1:dma_unused2;
+
+ long dma_reserved3[4];
+
+ short dma_subListPtr:dma_nextSCB;
+ short dma_SPBptr:dma_entryPoint;
+
+ long dma_strmRsConfig;
+ long dma_strmBufPtr;
+
+ long dma_reserved4;
+
+ VolumeControl s2m_volume;
+ };
+
+#export DMAReader
+void DMAReader()
+{
+ execChild();
+ r2 = r0->dma_subListPtr;
+ r1 = r0->nextSCB;
+
+ rsConfig01 = r2->strmRsConfig;
+ // Load rsConfig for input buffer
+
+ rsDMA01 = r2->basicReq.daw, , tb = Z(0 - rf);
+ // Load rsDMA in case input buffer is a DMA buffer Test to see if there is any data to transfer
+
+ if (tb) goto execSibling_2ind1 after {
+ r5 = rf + (-1);
+ r6 = r1->dma_entryPoint; // r6 = entry point of sibling task
+ r1 = r1->dma_SPBptr, // r1 = pointer to sibling task's SPB
+ , ind = r6; // Load entry point of sibling task
+ }
+
+ rsConfig23 = r0->dma_strmRsConfig;
+ // Load rsConfig for output buffer (never a DMA buffer)
+
+ r4 = r0->dma_outBufPtr;
+
+ rsa0 = r2->strmBufPtr;
+ // rsa0 = input buffer pointer
+
+ for (i = r5; i >= 0; --i)
+ after {
+ rsa2 = r4;
+ // rsa2 = output buffer pointer
+
+ nop;
+ nop;
+ }
+ //*****************************
+ // TODO: cycles to this point *
+ //*****************************
+ {
+ acc0 = (rsd0 = *rsa0++1);
+ // get sample
+
+ nop; // Those "nop"'s are really uggly, but there's
+ nop; // something with DSP's pipelines which I don't
+ nop; // understand, resulting this code to fail without
+ // having those "nop"'s (Benny)
+
+ rsa0?reqDMA = r2;
+ // Trigger DMA transfer on input stream,
+ // if needed to replenish input buffer
+
+ nop;
+ // Yet another magic "nop" to make stuff work
+
+ ,,r98 = acc0 $+>> 0;
+ // store sample in ALU
+
+ nop;
+ // latency on load register.
+ // (this one is understandable)
+
+ *rsa2++1 = r98;
+ // store sample in output buffer
+
+ nop; // The same story
+ nop; // as above again ...
+ nop;
+ }
+ // TODO: cycles per loop iteration
+
+ r2->strmBufPtr = rsa0,, ;
+ // Update the modified buffer pointers
+
+ r4 = rsa2;
+ // Load output pointer position into r4
+
+ r2 = r0->nextSCB;
+ // Sibling task
+
+ goto execSibling_2ind1 // takes 6 cycles
+ after {
+ r98 = r2->thisSPB:entryPoint;
+ // Load child routine entry and data address
+
+ r1 = r9;
+ // r9 is r2->thisSPB
+
+ r0->dma_outBufPtr = r4,,
+ // Store updated output buffer pointer
+
+ ind = r8;
+ // r8 is r2->entryPoint
+ }
+}
diff --git a/sound/pci/cs46xx/imgs/cwcdma.h b/sound/pci/cs46xx/imgs/cwcdma.h
new file mode 100644
index 00000000000..92860435bee
--- /dev/null
+++ b/sound/pci/cs46xx/imgs/cwcdma.h
@@ -0,0 +1,68 @@
+/* generated from cwcdma.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcdma_H__
+#define __HEADER_cwcdma_H__
+
+static symbol_entry_t cwcdma_symbols[] = {
+ { 0x8000, "EXECCHILD",0x03 },
+ { 0x8001, "EXECCHILD_98",0x03 },
+ { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+ { 0x8008, "EXECSIBLING",0x03 },
+ { 0x800a, "EXECSIBLING_298",0x03 },
+ { 0x800b, "EXECSIBLING_2IND1",0x03 },
+ { 0x8010, "TIMINGMASTER",0x03 },
+ { 0x804f, "S16_CODECINPUTTASK",0x03 },
+ { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+ { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+ { 0x809a, "S16_MIX",0x03 },
+ { 0x80bb, "S16_UPSRC",0x03 },
+ { 0x813b, "MIX3_EXP",0x03 },
+ { 0x8164, "DECIMATEBYPOW2",0x03 },
+ { 0x8197, "VARIDECIMATE",0x03 },
+ { 0x81f2, "_3DINPUTTASK",0x03 },
+ { 0x820a, "_3DPRLGCINPTASK",0x03 },
+ { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+ { 0x8242, "_3DOUTPUTTASK",0x03 },
+ { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+ { 0x82c6, "WAIT4DATA",0x03 },
+ { 0x82fa, "PROLOGIC",0x03 },
+ { 0x8496, "DECORRELATOR",0x03 },
+ { 0x84a4, "STEREO2MONO",0x03 },
+ { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
+ { 0x0000, "DMAREADER",0x03 },
+ { 0x0018, "#CODE_END",0x00 },
+}; /* cwcdma symbols */
+
+static u32 cwcdma_code[] = {
+/* OVERLAYBEGINADDRESS */
+/* 0000 */ 0x00002731,0x00001400,0x0004c108,0x000e5044,
+/* 0002 */ 0x0005f608,0x00000000,0x000007ae,0x000be300,
+/* 0004 */ 0x00058630,0x00001400,0x0007afb0,0x000e9584,
+/* 0006 */ 0x00007301,0x000a9840,0x0005e708,0x000cd104,
+/* 0008 */ 0x00067008,0x00000000,0x000902a0,0x00001000,
+/* 000A */ 0x00012a01,0x000c0000,0x00000000,0x00000000,
+/* 000C */ 0x00021843,0x000c0000,0x00000000,0x000c0000,
+/* 000E */ 0x0000e101,0x000c0000,0x00000cac,0x00000000,
+/* 0010 */ 0x00080000,0x000e5ca1,0x00000000,0x000c0000,
+/* 0012 */ 0x00000000,0x00000000,0x00000000,0x00092c00,
+/* 0014 */ 0x000122c1,0x000e5084,0x00058730,0x00001400,
+/* 0016 */ 0x000d7488,0x000e4782,0x00007401,0x0001c100
+};
+
+/* #CODE_END */
+
+static segment_desc_t cwcdma_segments[] = {
+ { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000030, cwcdma_code },
+};
+
+static dsp_module_desc_t cwcdma_module = {
+ "cwcdma",
+ {
+ 27,
+ cwcdma_symbols
+ },
+ 1,
+ cwcdma_segments,
+};
+
+#endif /* __HEADER_cwcdma_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcemb80.h b/sound/pci/cs46xx/imgs/cwcemb80.h
new file mode 100644
index 00000000000..4b13551eae4
--- /dev/null
+++ b/sound/pci/cs46xx/imgs/cwcemb80.h
@@ -0,0 +1,1607 @@
+/* generated from cwcemb80.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcemb80_H__
+#define __HEADER_cwcemb80_H__
+
+static symbol_entry_t cwcemb80_symbols[] = {
+ { 0x0000, "BEGINADDRESS",0x00 },
+ { 0x8000, "EXECCHILD",0x03 },
+ { 0x8001, "EXECCHILD_98",0x03 },
+ { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+ { 0x8008, "EXECSIBLING",0x03 },
+ { 0x800a, "EXECSIBLING_298",0x03 },
+ { 0x800b, "EXECSIBLING_2IND1",0x03 },
+ { 0x8010, "TIMINGMASTER",0x03 },
+ { 0x804f, "S16_CODECINPUTTASK",0x03 },
+ { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+ { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+ { 0x809a, "S16_MIX",0x03 },
+ { 0x80bb, "S16_UPSRC",0x03 },
+ { 0x813b, "MIX3_EXP",0x03 },
+ { 0x8164, "DECIMATEBYPOW2",0x03 },
+ { 0x8197, "VARIDECIMATE",0x03 },
+ { 0x81f2, "_3DINPUTTASK",0x03 },
+ { 0x820a, "_3DPRLGCINPTASK",0x03 },
+ { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+ { 0x8242, "_3DOUTPUTTASK",0x03 },
+ { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+ { 0x82c6, "WAIT4DATA",0x03 },
+ { 0x82fa, "PROLOGIC",0x03 },
+ { 0x8496, "DECORRELATOR",0x03 },
+ { 0x84a4, "STEREO2MONO",0x03 },
+ { 0x0070, "SPOSCB",0x02 },
+ { 0x0105, "TASKTREETHREAD",0x03 },
+ { 0x0136, "TASKTREEHEADERCODE",0x03 },
+ { 0x013f, "FGTASKTREEHEADERCODE",0x03 },
+ { 0x0163, "NULLALGORITHM",0x03 },
+ { 0x0167, "HFGEXECCHILD",0x03 },
+ { 0x0168, "HFGEXECCHILD_98",0x03 },
+ { 0x016a, "HFGEXECCHILD_PUSH1IND",0x03 },
+ { 0x016d, "HFGEXECSIBLING",0x03 },
+ { 0x016f, "HFGEXECSIBLING_298",0x03 },
+ { 0x0170, "HFGEXECSIBLING_2IND1",0x03 },
+ { 0x0173, "S16_CODECOUTPUTTASK",0x03 },
+ { 0x018e, "#CODE_END",0x00 },
+}; /* cwcemb80 symbols */
+
+static u32 cwcemb80_code[] = {
+/* BEGINADDRESS */
+/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003,
+/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003,
+/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003,
+/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003,
+/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003,
+/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003,
+/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003,
+/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003,
+/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003,
+/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040,
+/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003,
+/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003,
+/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003,
+/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003,
+/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003,
+/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003,
+/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003,
+/* 0040 */ 0x0000a730,0x00001008,0x000e2730,0x00001002,
+/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003,
+/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000,
+/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000,
+/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000,
+/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003,
+/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003,
+/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003,
+/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000,
+/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000,
+/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000,
+/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140,
+/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00,
+/* 007C */ 0x0000002e,0x0009d6c0,0x00038630,0x00001004,
+/* 007E */ 0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
+/* 0080 */ 0x00000000,0x000c70e0,0x0007d182,0x0002c640,
+/* 0082 */ 0x00000630,0x00001004,0x000799b8,0x0002c6c0,
+/* 0084 */ 0x00031705,0x00092240,0x00039f05,0x000932c0,
+/* 0086 */ 0x0003520a,0x00000000,0x00040731,0x0000100b,
+/* 0088 */ 0x00010705,0x000b20c0,0x00000000,0x000eba44,
+/* 008A */ 0x00032108,0x000c60c4,0x00065208,0x000c2917,
+/* 008C */ 0x000406b0,0x00001007,0x00012f05,0x00036880,
+/* 008E */ 0x0002818e,0x000c0000,0x0004410a,0x00000000,
+/* 0090 */ 0x00040630,0x00001007,0x00029705,0x000c0000,
+/* 0092 */ 0x00000000,0x00000000,0x00003fc1,0x0003fc40,
+/* 0094 */ 0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
+/* 0096 */ 0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
+/* 0098 */ 0x000037c1,0x00000000,0x00003fc1,0x000991c0,
+/* 009A */ 0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
+/* 009C */ 0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
+/* 009E */ 0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
+/* 00A0 */ 0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
+/* 00A2 */ 0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
+/* 00A4 */ 0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
+/* 00A6 */ 0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
+/* 00A8 */ 0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
+/* 00AA */ 0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
+/* 00AC */ 0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
+/* 00AE */ 0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
+/* 00B0 */ 0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
+/* 00B2 */ 0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
+/* 00B4 */ 0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
+/* 00B6 */ 0x000683ad,0x00095241,0x00020f05,0x000991c1,
+/* 00B8 */ 0x00000000,0x00000000,0x00086f88,0x00001000,
+/* 00BA */ 0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
+/* 00BC */ 0x0009de81,0x000bd300,0x0009d601,0x000b1700,
+/* 00BE */ 0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
+/* 00C0 */ 0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
+/* 00C2 */ 0x000a1681,0x000b97c0,0x00021601,0x00002500,
+/* 00C4 */ 0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
+/* 00C6 */ 0x00021681,0x00002d00,0x00020f81,0x000bd800,
+/* 00C8 */ 0x000a0701,0x000b5bc0,0x00021601,0x00003500,
+/* 00CA */ 0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
+/* 00CC */ 0x00021681,0x00003d00,0x00020f81,0x000b1d00,
+/* 00CE */ 0x000a0701,0x000b1fc0,0x00021601,0x00020500,
+/* 00D0 */ 0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
+/* 00D2 */ 0x00021681,0x00020d00,0x00020f81,0x000bde80,
+/* 00D4 */ 0x000a0701,0x000bdfc0,0x00021601,0x00021500,
+/* 00D6 */ 0x00020f81,0x000b9341,0x00020701,0x000b53c1,
+/* 00D8 */ 0x00021681,0x00021d00,0x000a0f81,0x000d0380,
+/* 00DA */ 0x0000b601,0x000b15c0,0x00007b01,0x00000000,
+/* 00DC */ 0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
+/* 00DE */ 0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
+/* 00E0 */ 0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
+/* 00E2 */ 0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
+/* 00E4 */ 0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
+/* 00E6 */ 0x0007e48a,0x00000000,0x00011f05,0x00084080,
+/* 00E8 */ 0x00000000,0x00000000,0x00001705,0x000b3540,
+/* 00EA */ 0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
+/* 00EC */ 0x00055488,0x00000000,0x0000d482,0x0003fc40,
+/* 00EE */ 0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
+/* 00F0 */ 0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
+/* 00F2 */ 0x000c86b0,0x00001007,0x00008281,0x000bb240,
+/* 00F4 */ 0x0000b801,0x000b7140,0x00007888,0x00000000,
+/* 00F6 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000,
+/* 00F8 */ 0x00000000,0x00000000,0x00055288,0x000c555c,
+/* 00FA */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+/* 00FC */ 0x0000fa88,0x00000000,0x00000032,0x00001000,
+/* 00FE */ 0x0000073d,0x00001000,0x0007f188,0x000c0000,
+/* 0100 */ 0x00000000,0x00000000,0x0008c01c,0x00001003,
+/* 0102 */ 0x00002705,0x00001008,0x0008b201,0x000c1392,
+/* 0104 */ 0x0000ba01,0x00000000,
+/* TASKTREETHREAD */
+/* 0105 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4,
+/* 0107 */ 0x00057488,0x00000000,0x000a6388,0x00001001,
+/* 0109 */ 0x0008b334,0x000bc141,0x0003020e,0x00000000,
+/* 010B */ 0x000886b0,0x00001008,0x00003625,0x000c5dfa,
+/* 010D */ 0x000a638a,0x00001001,0x0008020e,0x00001002,
+/* 010F */ 0x0008a6b0,0x00001008,0x0007f301,0x00000000,
+/* 0111 */ 0x00000000,0x00000000,0x00002725,0x000a8c40,
+/* 0113 */ 0x000000ae,0x00000000,0x000d8630,0x00001008,
+/* 0115 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640,
+/* 0117 */ 0x000a8630,0x00001008,0x000799b8,0x0002d6c0,
+/* 0119 */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000,
+/* 011B */ 0x00062208,0x000c4117,0x00070630,0x00001009,
+/* 011D */ 0x00000000,0x000c0000,0x0001022e,0x00000000,
+/* 011F */ 0x0003a630,0x00001009,0x00000000,0x000c0000,
+/* 0121 */ 0x00000036,0x00001000,0x00000000,0x00000000,
+/* 0123 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0125 */ 0x00000000,0x00000000,0x0002a730,0x00001008,
+/* 0127 */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012B */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012D */ 0x0002a730,0x00001008,0x00000033,0x00001000,
+/* 012F */ 0x0002a705,0x00001008,0x00007a01,0x000c0000,
+/* 0131 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000,
+/* 0133 */ 0x00060730,0x0000100a,0x00000000,0x000c0000,
+/* 0135 */ 0x00000000,0x00000000,
+/* TASKTREEHEADERCODE */
+/* 0136 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
+/* 0138 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 013A */ 0x000183ae,0x00000000,0x000786b0,0x0000100b,
+/* 013C */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+/* 013E */ 0x00042731,0x00001003,
+/* FGTASKTREEHEADERCODE */
+/* 013F */ 0x0007aab0,0x00034880,0x00048fb0,0x0000100a,
+/* 0141 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 0143 */ 0x000183ae,0x00000000,0x000806b0,0x0000100b,
+/* 0145 */ 0x00022f05,0x00000000,0x00007401,0x00091140,
+/* 0147 */ 0x00048f05,0x000951c0,0x00042731,0x00001003,
+/* 0149 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 014B */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003,
+/* 014D */ 0x00000000,0x00000000,0x0008e19c,0x00001003,
+/* 014F */ 0x000083c1,0x00093040,0x00000f41,0x00097140,
+/* 0151 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0153 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 0155 */ 0x00000000,0x000fdc44,0x00055208,0x00000000,
+/* 0157 */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00,
+/* 0159 */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000,
+/* 015B */ 0x00012f05,0x00036880,0x00065308,0x000c2997,
+/* 015D */ 0x000d86b0,0x0000100a,0x0004410a,0x000d40c7,
+/* 015F */ 0x00000000,0x00000000,0x00080730,0x00001004,
+/* 0161 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000,
+/* NULLALGORITHM */
+/* 0163 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 0165 */ 0x00080000,0x000bffc7,0x0000273d,0x00001000,
+/* HFGEXECCHILD */
+/* 0167 */ 0x00000000,0x000eba44,
+/* HFGEXECCHILD_98 */
+/* 0168 */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+/* HFGEXECCHILD_PUSH1IND */
+/* 016A */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 016C */ 0x00006a88,0x000c75c4,
+/* HFGEXECSIBLING */
+/* 016D */ 0x00000000,0x000e5084,0x00000000,0x000eba44,
+/* HFGEXECSIBLING_298 */
+/* 016F */ 0x00087401,0x000e4782,
+/* HFGEXECSIBLING_2IND1 */
+/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 0172 */ 0x00006a88,0x000c75c4,
+/* S16_CODECOUTPUTTASK */
+/* 0173 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40,
+/* 0175 */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80,
+/* 0177 */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0,
+/* 0179 */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0,
+/* 017B */ 0x00073fb0,0x00074c80,0x000283a0,0x0000100c,
+/* 017D */ 0x000ee388,0x00042970,0x00008301,0x00021ef2,
+/* 017F */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b,
+/* 0181 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916,
+/* 0183 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000,
+/* 0185 */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b,
+/* 0187 */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956,
+/* 0189 */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40,
+/* 018B */ 0x00058730,0x00001400,0x000d7488,0x000c3a00,
+/* 018D */ 0x00048f05,0x00000000
+};
+/* #CODE_END */
+
+static u32 cwcemb80_parameter[] = {
+/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0008 */ 0x00000000,0x00000000,0x00000163,0x00000000,
+/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0018 */ 0x00000000,0x00200040,0x00008010,0x00000000,
+/* 001C */ 0x00000000,0x80000001,0x00000001,0x00060000,
+/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0028 */ 0x00000000,0x00900080,0x00000173,0x00000000,
+/* 002C */ 0x00000000,0x00000010,0x00800000,0x00900000,
+/* 0030 */ 0xf2c0000f,0x00000200,0x00000000,0x00010600,
+/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0038 */ 0x00000000,0x00000000,0x00000163,0x330300c2,
+/* 003C */ 0x06000000,0x00000000,0x80008000,0x80008000,
+/* 0040 */ 0x3fc0000f,0x00000301,0x00010400,0x00000000,
+/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0048 */ 0x00000000,0x00b00000,0x00d0806d,0x330480c3,
+/* 004C */ 0x04800000,0x00000001,0x00800001,0x0000ffff,
+/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0070 */ 0x066a0600,0x06350070,0x0000929d,0x929d929d,
+/* 0074 */ 0x00000000,0x0000735a,0x00000600,0x00000000,
+/* 0078 */ 0x929d735a,0x00000000,0x00010000,0x735a735a,
+/* 007C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0080 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0084 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0088 */ 0x00000000,0x00000000,0x0000804f,0x000000c3,
+/* 008C */ 0x05000000,0x00a00010,0x00000000,0x80008000,
+/* 0090 */ 0x00000000,0x00000000,0x00000700,0x00000000,
+/* 0094 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0098 */ 0x00000080,0x00a00000,0x0000809a,0x000000c2,
+/* 009C */ 0x07400000,0x00000000,0x80008000,0xffffffff,
+/* 00A0 */ 0x00c80028,0x00005555,0x00000000,0x000107a0,
+/* 00A4 */ 0x00c80028,0x000000c2,0x06800000,0x00000000,
+/* 00A8 */ 0x06e00080,0x00300000,0x000080bb,0x000000c9,
+/* 00AC */ 0x07a00000,0x04000000,0x80008000,0xffffffff,
+/* 00B0 */ 0x00c80028,0x00005555,0x00000000,0x00000780,
+/* 00B4 */ 0x00c80028,0x000000c5,0xff800000,0x00000000,
+/* 00B8 */ 0x00640080,0x00c00000,0x00008197,0x000000c9,
+/* 00BC */ 0x07800000,0x04000000,0x80008000,0xffffffff,
+/* 00C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C8 */ 0x00000000,0x00000000,0x0000805e,0x000000c1,
+/* 00CC */ 0x00000000,0x00800000,0x80008000,0x80008000,
+/* 00D0 */ 0x00020000,0x0000ffff,0x00000000,0x00000000,
+/* 00D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0100 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0104 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0108 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 010C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0110 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0114 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0118 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 011C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0120 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0124 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0128 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0130 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0134 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0138 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 013C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0140 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0144 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0148 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 014C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0150 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0154 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0158 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 015C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0160 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0164 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0168 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 016C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0170 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0174 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0178 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 017C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0180 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0184 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0188 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 018C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0190 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0194 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0198 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 019C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0200 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0204 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0208 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 020C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0210 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0214 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0218 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 021C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0220 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0224 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0228 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 022C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0230 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0234 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0238 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 023C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0240 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0244 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0248 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 024C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0250 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0254 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0258 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 025C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0260 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0264 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0268 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 026C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0270 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0274 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0278 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 027C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0280 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0284 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0288 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 028C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0290 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0294 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0298 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 029C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0300 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0304 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0308 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 030C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0310 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0314 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0318 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 031C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0320 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0324 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0328 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 032C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0330 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0334 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0338 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 033C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0340 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0344 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0348 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 034C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0350 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0354 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0358 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 035C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0360 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0364 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0368 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 036C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0370 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0374 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0378 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 037C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0380 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0384 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0388 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 038C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0390 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0394 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0398 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 039C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0400 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0404 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0408 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 040C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0410 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0414 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0418 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 041C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0420 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0424 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0428 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 042C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0430 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0434 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0438 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 043C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0440 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0444 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0448 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 044C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0450 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0454 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0458 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 045C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0460 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0464 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0468 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 046C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0470 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0474 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0478 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 047C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0480 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0484 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0488 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 048C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0490 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0494 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0498 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 049C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0500 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0504 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0508 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 050C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0510 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0514 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0518 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 051C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0520 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0524 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0528 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 052C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0530 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0534 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0538 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 053C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0540 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0544 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0548 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 054C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0550 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0554 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0558 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 055C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0560 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0564 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0568 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 056C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0570 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0574 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0578 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 057C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0580 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0584 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0588 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 058C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0590 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0594 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0598 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 059C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0600 */ 0x929d0600,0x929d929d,0x929d929d,0x929d0000,
+/* 0604 */ 0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+/* 0608 */ 0x929d929d,0x00100635,0x060b013f,0x00000004,
+/* 060C */ 0x00000001,0x007a0002,0x00000000,0x066e0610,
+/* 0610 */ 0x0105929d,0x929d929d,0x929d929d,0x929d929d,
+/* 0614 */ 0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
+/* 0618 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 061C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0620 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0624 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0628 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 062C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0630 */ 0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
+/* 0634 */ 0x00000000,0x929d929d,0x929d929d,0x929d929d,
+/* 0638 */ 0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+/* 063C */ 0x929d929d,0x929d929d,0x00000000,0x06400136,
+/* 0640 */ 0x0000270f,0x00010000,0x007a0000,0x00000000,
+/* 0644 */ 0x068e0645,0x0105929d,0x929d929d,0x929d929d,
+/* 0648 */ 0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
+/* 064C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0650 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0654 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0658 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 065C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0660 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0664 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0668 */ 0x735a0100,0x00000000,0x00000000,0x00000000,
+/* 066C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0670 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0674 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0678 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 067C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0680 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0684 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0688 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 068C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0690 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0694 */ 0x00000000,0x00000000,0x00000000
+}; /* #PARAMETER_END */
+
+static u32 cwcemb80_sample[] = {
+/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0080 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0084 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0088 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 008C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0090 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0094 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0098 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 009C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0100 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0104 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0108 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 010C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0110 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0114 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0118 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 011C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0120 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0124 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0128 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0130 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0134 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0138 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 013C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0140 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0144 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0148 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 014C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0150 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0154 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0158 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 015C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0160 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0164 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0168 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 016C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0170 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0174 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0178 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 017C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0180 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0184 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0188 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 018C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0190 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0194 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0198 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 019C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0200 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0204 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0208 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 020C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0210 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0214 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0218 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 021C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0220 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0224 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0228 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 022C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0230 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0234 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0238 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 023C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0240 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0244 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0248 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 024C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0250 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0254 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0258 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 025C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0260 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0264 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0268 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 026C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0270 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0274 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0278 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 027C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0280 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0284 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0288 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 028C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0290 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0294 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0298 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 029C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0300 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0304 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0308 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 030C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0310 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0314 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0318 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 031C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0320 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0324 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0328 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 032C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0330 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0334 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0338 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 033C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0340 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0344 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0348 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 034C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0350 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0354 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0358 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 035C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0360 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0364 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0368 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 036C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0370 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0374 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0378 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 037C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0380 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0384 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0388 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 038C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0390 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0394 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0398 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 039C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0400 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0404 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0408 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 040C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0410 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0414 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0418 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 041C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0420 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0424 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0428 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 042C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0430 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0434 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0438 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 043C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0440 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0444 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0448 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 044C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0450 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0454 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0458 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 045C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0460 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0464 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0468 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 046C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0470 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0474 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0478 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 047C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0480 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0484 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0488 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 048C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0490 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0494 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0498 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 049C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0500 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0504 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0508 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 050C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0510 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0514 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0518 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 051C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0520 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0524 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0528 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 052C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0530 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0534 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0538 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 053C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0540 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0544 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0548 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 054C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0550 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0554 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0558 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 055C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0560 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0564 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0568 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 056C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0570 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0574 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0578 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 057C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0580 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0584 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0588 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 058C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0590 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0594 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0598 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 059C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0600 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0604 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0608 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 060C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0610 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0614 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0618 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 061C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0620 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0624 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0628 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 062C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0630 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0634 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0638 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 063C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0640 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0644 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0648 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 064C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0650 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0654 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0658 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 065C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0660 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0664 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0668 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 066C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0670 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0674 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0678 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 067C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0680 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0684 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0688 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 068C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0690 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0694 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0698 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 069C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0700 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0704 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0708 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 070C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0710 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0714 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0718 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 071C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0720 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0724 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0728 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 072C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0730 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0734 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0738 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 073C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0740 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0744 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0748 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 074C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0750 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0754 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0758 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 075C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0760 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0764 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0768 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 076C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0770 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0774 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0778 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 077C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0780 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0784 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0788 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 078C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0790 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0794 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0798 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 079C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0800 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0804 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0808 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 080C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0810 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0814 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0818 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 081C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0820 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0824 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0828 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 082C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0830 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0834 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0838 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 083C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0840 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0844 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0848 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 084C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0850 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0854 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0858 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 085C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0860 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0864 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0868 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 086C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0870 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0874 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0878 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 087C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0880 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0884 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0888 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 088C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0890 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0894 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0898 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 089C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0900 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0904 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0908 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 090C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0910 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0914 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0918 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 091C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0920 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0924 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0928 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 092C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0930 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0934 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0938 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 093C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0940 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0944 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0948 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 094C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0950 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0954 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0958 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 095C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0960 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0964 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0968 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 096C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0970 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0974 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0978 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 097C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0980 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0984 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0988 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 098C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0990 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0994 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0998 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 099C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0ABC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0ACC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0ADC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AFC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BBC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BCC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BDC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BFC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CBC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CCC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CDC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CFC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DBC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DCC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DDC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DFC */ 0x00000000,0x00000000,0x00000000,0x00010004
+}; /* #SAMPLE_END */
+
+
+static segment_desc_t cwcemb80_segments[] = {
+ { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000031c, cwcemb80_code },
+ { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000697, cwcemb80_parameter },
+ { SEGTYPE_SP_SAMPLE, 0x00000000, 0x00000e00, cwcemb80_sample },
+};
+
+static dsp_module_desc_t cwcemb80_module = {
+ "cwcemb80",
+ {
+ 38,
+ cwcemb80_symbols
+ },
+ 3,
+ cwcemb80_segments,
+};
+
+#endif /* __HEADER_cwcemb80_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcsnoop.h b/sound/pci/cs46xx/imgs/cwcsnoop.h
new file mode 100644
index 00000000000..be1162bbcb4
--- /dev/null
+++ b/sound/pci/cs46xx/imgs/cwcsnoop.h
@@ -0,0 +1,46 @@
+/* generated from cwcsnoop.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcsnoop_H__
+#define __HEADER_cwcsnoop_H__
+
+static symbol_entry_t cwcsnoop_symbols[] = {
+ { 0x0500, "OVERLAYBEGINADDRESS",0x00 },
+ { 0x0500, "OUTPUTSNOOP",0x03 },
+ { 0x051f, "#CODE_END",0x00 },
+}; /* cwcsnoop symbols */
+
+static u32 cwcsnoop_code[] = {
+/* 0000 */ 0x0007bfb0,0x000b4e40,0x0007c088,0x000c0617,
+/* 0002 */ 0x00049705,0x00000000,0x00080630,0x00001028,
+/* 0004 */ 0x00076408,0x000efb84,0x00066008,0x00000000,
+/* 0006 */ 0x0007c908,0x000c0000,0x00046725,0x000efa44,
+/* 0008 */ 0x0005f708,0x00000000,0x0001d402,0x000b2e00,
+/* 000A */ 0x0003d418,0x00001000,0x0008d574,0x000c4293,
+/* 000C */ 0x00065625,0x000ea30e,0x00096c01,0x000c6f92,
+/* 000E */ 0x0006a58a,0x000f6085,0x00002f43,0x00000000,
+/* 0010 */ 0x000a83a0,0x00001028,0x0005e608,0x000c0000,
+/* 0012 */ 0x00000000,0x00000000,0x000ca108,0x000dcca1,
+/* 0014 */ 0x00003bac,0x000fb205,0x00073843,0x00000000,
+/* 0016 */ 0x000d8730,0x00001028,0x0006600a,0x000c0000,
+/* 0018 */ 0x00057488,0x00000000,0x00000000,0x000e5084,
+/* 001A */ 0x00000000,0x000eba44,0x00087401,0x000e4782,
+/* 001C */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 001E */ 0x00006a88,0x000c75c4
+};
+/* #CODE_END */
+
+static segment_desc_t cwcsnoop_segments[] = {
+ { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000003e, cwcsnoop_code },
+};
+
+static dsp_module_desc_t cwcsnoop_module = {
+ "cwcsnoop",
+ {
+ 3,
+ cwcsnoop_symbols
+ },
+ 1,
+ cwcsnoop_segments,
+};
+
+#endif /* __HEADER_cwcsnoop_H__ */
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile
new file mode 100644
index 00000000000..e521c38cef4
--- /dev/null
+++ b/sound/pci/emu10k1/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
+ irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
+ emuproc.o emumixer.o emufx.o timer.o p16v.o
+snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
+snd-emu10k1x-objs := emu10k1x.o
+
+#
+# this function returns:
+# "m" - CONFIG_SND_SEQUENCER is m
+# <empty string> - CONFIG_SND_SEQUENCER is undefined
+# otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
+obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emu10k1-synth.o
+obj-$(CONFIG_SND_EMU10K1X) += snd-emu10k1x.o
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
new file mode 100644
index 00000000000..6446afe19d8
--- /dev/null
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -0,0 +1,240 @@
+/*
+ * The driver for the EMU10K1 (SB Live!) based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Added support for Audigy 2 Value.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("EMU10K1");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
+ "{Creative Labs,SB Audigy}}");
+
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#define ENABLE_SYNTH
+#include <sound/emu10k1_synth.h>
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
+static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
+static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the EMU10K1 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the EMU10K1 soundcard.");
+module_param_array(extin, int, NULL, 0444);
+MODULE_PARM_DESC(extin, "Available external inputs for FX8010. Zero=default.");
+module_param_array(extout, int, NULL, 0444);
+MODULE_PARM_DESC(extout, "Available external outputs for FX8010. Zero=default.");
+module_param_array(seq_ports, int, NULL, 0444);
+MODULE_PARM_DESC(seq_ports, "Allocated sequencer ports for internal synthesizer.");
+module_param_array(max_synth_voices, int, NULL, 0444);
+MODULE_PARM_DESC(max_synth_voices, "Maximum number of voices for WaveTable.");
+module_param_array(max_buffer_size, int, NULL, 0444);
+MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB.");
+module_param_array(enable_ir, bool, NULL, 0444);
+MODULE_PARM_DESC(enable_ir, "Enable IR.");
+
+/*
+ * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400
+ */
+static struct pci_device_id snd_emu10k1_ids[] = {
+ { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */
+ { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */
+ { 0x1102, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy 2 Value SB0400 */
+ { 0, }
+};
+
+/*
+ * Audigy 2 Value notes:
+ * A_IOCFG Input (GPIO)
+ * 0x400 = Front analog jack plugged in. (Green socket)
+ * 0x1000 = Read analog jack plugged in. (Black socket)
+ * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
+ * A_IOCFG Output (GPIO)
+ * 0x60 = Sound out of front Left.
+ * Win sets it to 0xXX61
+ */
+
+MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
+
+static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ emu10k1_t *emu;
+#ifdef ENABLE_SYNTH
+ snd_seq_device_t *wave = NULL;
+#endif
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+ if (max_buffer_size[dev] < 32)
+ max_buffer_size[dev] = 32;
+ else if (max_buffer_size[dev] > 1024)
+ max_buffer_size[dev] = 1024;
+ if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
+ (long)max_buffer_size[dev] * 1024 * 1024,
+ enable_ir[dev],
+ &emu)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ /* This stores the periods table. */
+ if (emu->audigy && emu->revision == 4) { /* P16V */
+ if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &emu->p16v_buffer) < 0) {
+ snd_p16v_free(emu);
+ return -ENOMEM;
+ }
+ }
+
+ if ((err = snd_emu10k1_mixer(emu)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_emu10k1_timer(emu, 0)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (emu->audigy && emu->revision == 4) { /* P16V */
+ if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ if (emu->audigy) {
+ if ((err = snd_emu10k1_audigy_midi(emu)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ } else {
+ if ((err = snd_emu10k1_midi(emu)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#ifdef ENABLE_SYNTH
+ if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
+ sizeof(snd_emu10k1_synth_arg_t), &wave) < 0 ||
+ wave == NULL) {
+ snd_printk("can't initialize Emu10k1 wavetable synth\n");
+ } else {
+ snd_emu10k1_synth_arg_t *arg;
+ arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+ strcpy(wave->name, "Emu-10k1 Synth");
+ arg->hwptr = emu;
+ arg->index = 1;
+ arg->seq_ports = seq_ports[dev];
+ arg->max_voices = max_synth_voices[dev];
+ }
+#endif
+
+ strcpy(card->driver, emu->card_capabilities->driver);
+ strcpy(card->shortname, emu->card_capabilities->name);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s (rev.%d, serial:0x%x) at 0x%lx, irq %i",
+ card->shortname, emu->revision, emu->serial, emu->port, emu->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "EMU10K1_Audigy",
+ .id_table = snd_emu10k1_ids,
+ .probe = snd_card_emu10k1_probe,
+ .remove = __devexit_p(snd_card_emu10k1_remove),
+};
+
+static int __init alsa_card_emu10k1_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_emu10k1_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_emu10k1_init)
+module_exit(alsa_card_emu10k1_exit)
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
new file mode 100644
index 00000000000..7cf2f908eed
--- /dev/null
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -0,0 +1,540 @@
+/*
+ * synth callback routines for Emu10k1
+ *
+ * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "emu10k1_synth_local.h"
+#include <sound/asoundef.h>
+
+/* voice status */
+enum {
+ V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
+};
+
+/* Keeps track of what we are finding */
+typedef struct best_voice {
+ unsigned int time;
+ int voice;
+} best_voice_t;
+
+/*
+ * prototypes
+ */
+static void lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only);
+static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port);
+static int start_voice(snd_emux_voice_t *vp);
+static void trigger_voice(snd_emux_voice_t *vp);
+static void release_voice(snd_emux_voice_t *vp);
+static void update_voice(snd_emux_voice_t *vp, int update);
+static void terminate_voice(snd_emux_voice_t *vp);
+static void free_voice(snd_emux_voice_t *vp);
+
+static void set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp);
+static void set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp);
+static void set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp);
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+
+/*
+ * set up operators
+ */
+static snd_emux_operators_t emu10k1_ops = {
+ .owner = THIS_MODULE,
+ .get_voice = get_voice,
+ .prepare = start_voice,
+ .trigger = trigger_voice,
+ .release = release_voice,
+ .update = update_voice,
+ .terminate = terminate_voice,
+ .free_voice = free_voice,
+ .sample_new = snd_emu10k1_sample_new,
+ .sample_free = snd_emu10k1_sample_free,
+};
+
+void
+snd_emu10k1_ops_setup(snd_emux_t *emu)
+{
+ emu->ops = emu10k1_ops;
+}
+
+
+/*
+ * get more voice for pcm
+ *
+ * terminate most inactive voice and give it as a pcm voice.
+ */
+int
+snd_emu10k1_synth_get_voice(emu10k1_t *hw)
+{
+ snd_emux_t *emu;
+ snd_emux_voice_t *vp;
+ best_voice_t best[V_END];
+ unsigned long flags;
+ int i;
+
+ emu = hw->synth;
+
+ spin_lock_irqsave(&emu->voice_lock, flags);
+ lookup_voices(emu, hw, best, 1); /* no OFF voices */
+ for (i = 0; i < V_END; i++) {
+ if (best[i].voice >= 0) {
+ int ch;
+ vp = &emu->voices[best[i].voice];
+ if ((ch = vp->ch) < 0) {
+ //printk("synth_get_voice: ch < 0 (%d) ??", i);
+ continue;
+ }
+ vp->emu->num_voices--;
+ vp->ch = -1;
+ vp->state = SNDRV_EMUX_ST_OFF;
+ spin_unlock_irqrestore(&emu->voice_lock, flags);
+ return ch;
+ }
+ }
+ spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+ /* not found */
+ return -ENOMEM;
+}
+
+
+/*
+ * turn off the voice (not terminated)
+ */
+static void
+release_voice(snd_emux_voice_t *vp)
+{
+ int dcysusv;
+ emu10k1_t *hw;
+
+ hw = vp->hw;
+ dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+ snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
+ dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
+ snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
+}
+
+
+/*
+ * terminate the voice
+ */
+static void
+terminate_voice(snd_emux_voice_t *vp)
+{
+ emu10k1_t *hw;
+
+ snd_assert(vp, return);
+ hw = vp->hw;
+ snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+ if (vp->block) {
+ emu10k1_memblk_t *emem;
+ emem = (emu10k1_memblk_t *)vp->block;
+ if (emem->map_locked > 0)
+ emem->map_locked--;
+ }
+}
+
+/*
+ * release the voice to system
+ */
+static void
+free_voice(snd_emux_voice_t *vp)
+{
+ emu10k1_t *hw;
+
+ hw = vp->hw;
+ if (vp->ch >= 0) {
+ snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
+ snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+ // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
+ snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
+ snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
+ snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
+ vp->emu->num_voices--;
+ vp->ch = -1;
+ }
+}
+
+
+/*
+ * update registers
+ */
+static void
+update_voice(snd_emux_voice_t *vp, int update)
+{
+ emu10k1_t *hw;
+
+ hw = vp->hw;
+ if (update & SNDRV_EMUX_UPDATE_VOLUME)
+ snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
+ if (update & SNDRV_EMUX_UPDATE_PITCH)
+ snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+ if (update & SNDRV_EMUX_UPDATE_PAN) {
+ snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
+ snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
+ }
+ if (update & SNDRV_EMUX_UPDATE_FMMOD)
+ set_fmmod(hw, vp);
+ if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
+ snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+ if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
+ set_fm2frq2(hw, vp);
+ if (update & SNDRV_EMUX_UPDATE_Q)
+ set_filterQ(hw, vp);
+}
+
+
+/*
+ * look up voice table - get the best voice in order of preference
+ */
+/* spinlock held! */
+static void
+lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only)
+{
+ snd_emux_voice_t *vp;
+ best_voice_t *bp;
+ int i;
+
+ for (i = 0; i < V_END; i++) {
+ best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */;
+ best[i].voice = -1;
+ }
+
+ /*
+ * Go through them all and get a best one to use.
+ * NOTE: could also look at volume and pick the quietest one.
+ */
+ for (i = 0; i < emu->max_voices; i++) {
+ int state, val;
+
+ vp = &emu->voices[i];
+ state = vp->state;
+ if (state == SNDRV_EMUX_ST_OFF) {
+ if (vp->ch < 0) {
+ if (active_only)
+ continue;
+ bp = best + V_FREE;
+ } else
+ bp = best + V_OFF;
+ }
+ else if (state == SNDRV_EMUX_ST_RELEASED ||
+ state == SNDRV_EMUX_ST_PENDING) {
+ bp = best + V_RELEASED;
+#if 0
+ val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
+ if (! val)
+ bp = best + V_OFF;
+#endif
+ }
+ else if (state == SNDRV_EMUX_ST_STANDBY)
+ continue;
+ else if (state & SNDRV_EMUX_ST_ON)
+ bp = best + V_PLAYING;
+ else
+ continue;
+
+ /* check if sample is finished playing (non-looping only) */
+ if (bp != best + V_OFF && bp != best + V_FREE &&
+ (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
+ val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
+ if (val >= vp->reg.loopstart)
+ bp = best + V_OFF;
+ }
+
+ if (vp->time < bp->time) {
+ bp->time = vp->time;
+ bp->voice = i;
+ }
+ }
+}
+
+/*
+ * get an empty voice
+ *
+ * emu->voice_lock is already held.
+ */
+static snd_emux_voice_t *
+get_voice(snd_emux_t *emu, snd_emux_port_t *port)
+{
+ emu10k1_t *hw;
+ snd_emux_voice_t *vp;
+ best_voice_t best[V_END];
+ int i;
+
+ hw = emu->hw;
+
+ lookup_voices(emu, hw, best, 0);
+ for (i = 0; i < V_END; i++) {
+ if (best[i].voice >= 0) {
+ vp = &emu->voices[best[i].voice];
+ if (vp->ch < 0) {
+ /* allocate a voice */
+ emu10k1_voice_t *hwvoice;
+ if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
+ continue;
+ vp->ch = hwvoice->number;
+ emu->num_voices++;
+ }
+ return vp;
+ }
+ }
+
+ /* not found */
+ return NULL;
+}
+
+/*
+ * prepare envelopes and LFOs
+ */
+static int
+start_voice(snd_emux_voice_t *vp)
+{
+ unsigned int temp;
+ int ch;
+ unsigned int addr, mapped_offset;
+ snd_midi_channel_t *chan;
+ emu10k1_t *hw;
+ emu10k1_memblk_t *emem;
+
+ hw = vp->hw;
+ ch = vp->ch;
+ snd_assert(ch >= 0, return -EINVAL);
+ chan = vp->chan;
+
+ emem = (emu10k1_memblk_t *)vp->block;
+ if (emem == NULL)
+ return -EINVAL;
+ emem->map_locked++;
+ if (snd_emu10k1_memblk_map(hw, emem) < 0) {
+ // printk("emu: cannot map!\n");
+ return -ENOMEM;
+ }
+ mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
+ vp->reg.start += mapped_offset;
+ vp->reg.end += mapped_offset;
+ vp->reg.loopstart += mapped_offset;
+ vp->reg.loopend += mapped_offset;
+
+ /* set channel routing */
+ /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
+ if (hw->audigy) {
+ temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) |
+ (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
+ snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
+ } else {
+ temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) |
+ (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
+ snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
+ }
+
+ /* channel to be silent and idle */
+ snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0080);
+ snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
+ snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
+ snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
+ snd_emu10k1_ptr_write(hw, CPF, ch, 0);
+
+ /* set pitch offset */
+ snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+
+ /* set envelope parameters */
+ snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
+ snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
+ snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
+ snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
+ snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
+ /* decay/sustain parameter for volume envelope is used
+ for triggerg the voice */
+
+ /* cutoff and volume */
+ temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
+ snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
+
+ /* modulation envelope heights */
+ snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
+
+ /* lfo1/2 delay */
+ snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
+ snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
+
+ /* lfo1 pitch & cutoff shift */
+ set_fmmod(hw, vp);
+ /* lfo1 volume & freq */
+ snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+ /* lfo2 pitch & freq */
+ set_fm2frq2(hw, vp);
+
+ /* reverb and loop start (reverb 8bit, MSB) */
+ temp = vp->reg.parm.reverb;
+ temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
+ LIMITMAX(temp, 255);
+ addr = vp->reg.loopstart;
+ snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
+
+ /* chorus & loop end (chorus 8bit, MSB) */
+ addr = vp->reg.loopend;
+ temp = vp->reg.parm.chorus;
+ temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
+ LIMITMAX(temp, 255);
+ temp = (temp <<24) | addr;
+ snd_emu10k1_ptr_write(hw, DSL, ch, temp);
+
+ /* clear filter delay memory */
+ snd_emu10k1_ptr_write(hw, Z1, ch, 0);
+ snd_emu10k1_ptr_write(hw, Z2, ch, 0);
+
+ /* invalidate maps */
+ temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK;
+ snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+ snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+#if 0
+ /* cache */
+ {
+ unsigned int val, sample;
+ val = 32;
+ if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+ sample = 0x80808080;
+ else {
+ sample = 0;
+ val *= 2;
+ }
+
+ /* cache */
+ snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
+ snd_emu10k1_ptr_write(hw, CDE, ch, sample);
+ snd_emu10k1_ptr_write(hw, CDF, ch, sample);
+
+ /* invalidate maps */
+ temp = ((unsigned int)hw->silent_page.addr << 1) | MAP_PTI_MASK;
+ snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+ snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+
+ /* fill cache */
+ val -= 4;
+ val <<= 25;
+ val |= 0x1c << 16;
+ snd_emu10k1_ptr_write(hw, CCR, ch, val);
+ }
+#endif
+
+ /* Q & current address (Q 4bit value, MSB) */
+ addr = vp->reg.start;
+ temp = vp->reg.parm.filterQ;
+ temp = (temp<<28) | addr;
+ if (vp->apitch < 0xe400)
+ temp |= CCCA_INTERPROM_0;
+ else {
+ unsigned int shift = (vp->apitch - 0xe000) >> 10;
+ temp |= shift << 25;
+ }
+ if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+ temp |= CCCA_8BITSELECT;
+ snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
+
+ /* reset volume */
+ temp = (unsigned int)vp->vtarget << 16;
+ snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
+ snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
+ return 0;
+}
+
+/*
+ * Start envelope
+ */
+static void
+trigger_voice(snd_emux_voice_t *vp)
+{
+ unsigned int temp, ptarget;
+ emu10k1_t *hw;
+ emu10k1_memblk_t *emem;
+
+ hw = vp->hw;
+
+ emem = (emu10k1_memblk_t *)vp->block;
+ if (! emem || emem->mapped_page < 0)
+ return; /* not mapped */
+
+#if 0
+ ptarget = (unsigned int)vp->ptarget << 16;
+#else
+ ptarget = IP_TO_CP(vp->apitch);
+#endif
+ /* set pitch target and pan (volume) */
+ temp = ptarget | (vp->apan << 8) | vp->aaux;
+ snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
+
+ /* pitch target */
+ snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
+
+ /* trigger voice */
+ snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
+}
+
+#define MOD_SENSE 18
+
+/* set lfo1 modulation height and cutoff */
+static void
+set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp)
+{
+ unsigned short fmmod;
+ short pitch;
+ unsigned char cutoff;
+ int modulation;
+
+ pitch = (char)(vp->reg.parm.fmmod>>8);
+ cutoff = (vp->reg.parm.fmmod & 0xff);
+ modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+ pitch += (MOD_SENSE * modulation) / 1200;
+ LIMITVALUE(pitch, -128, 127);
+ fmmod = ((unsigned char)pitch<<8) | cutoff;
+ snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
+}
+
+/* set lfo2 pitch & frequency */
+static void
+set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp)
+{
+ unsigned short fm2frq2;
+ short pitch;
+ unsigned char freq;
+ int modulation;
+
+ pitch = (char)(vp->reg.parm.fm2frq2>>8);
+ freq = vp->reg.parm.fm2frq2 & 0xff;
+ modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+ pitch += (MOD_SENSE * modulation) / 1200;
+ LIMITVALUE(pitch, -128, 127);
+ fm2frq2 = ((unsigned char)pitch<<8) | freq;
+ snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
+}
+
+/* set filterQ */
+static void
+set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp)
+{
+ unsigned int val;
+ val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
+ val |= (vp->reg.parm.filterQ << 28);
+ snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
+}
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
new file mode 100644
index 00000000000..c3c96f9f2c7
--- /dev/null
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -0,0 +1,875 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Creative Labs, Inc.
+ * Routines for control of EMU10K1 chips
+ *
+ * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Added support for Audigy 2 Value.
+ *
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include "p16v.h"
+
+#if 0
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Creative Labs, Inc.");
+MODULE_DESCRIPTION("Routines for control of EMU10K1 chips");
+MODULE_LICENSE("GPL");
+#endif
+
+/*************************************************************************
+ * EMU10K1 init / done
+ *************************************************************************/
+
+void snd_emu10k1_voice_init(emu10k1_t * emu, int ch)
+{
+ snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+ snd_emu10k1_ptr_write(emu, IP, ch, 0);
+ snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff);
+ snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff);
+ snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+ snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+ snd_emu10k1_ptr_write(emu, CCR, ch, 0);
+
+ snd_emu10k1_ptr_write(emu, PSST, ch, 0);
+ snd_emu10k1_ptr_write(emu, DSL, ch, 0x10);
+ snd_emu10k1_ptr_write(emu, CCCA, ch, 0);
+ snd_emu10k1_ptr_write(emu, Z1, ch, 0);
+ snd_emu10k1_ptr_write(emu, Z2, ch, 0);
+ snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000);
+
+ snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0);
+ snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0);
+ snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff);
+ snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
+ snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
+ snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */
+ snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */
+ snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0);
+
+ /*** these are last so OFF prevents writing ***/
+ snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0);
+ snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0);
+ snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0);
+ snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0);
+ snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0);
+
+ /* Audigy extra stuffs */
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */
+ snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */
+ snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */
+ snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */
+ snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
+ snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
+ snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+ }
+}
+
+static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir)
+{
+ int ch, idx, err;
+ unsigned int silent_page;
+
+ emu->fx8010.itram_size = (16 * 1024)/2;
+ emu->fx8010.etram_pages.area = NULL;
+ emu->fx8010.etram_pages.bytes = 0;
+
+ /* disable audio and lock cache */
+ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+
+ /* reset recording buffers */
+ snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
+ snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+ snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE);
+ snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+ snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+ snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+
+ /* disable channel interrupt */
+ outl(0, emu->port + INTE);
+ snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+ snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+ snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+ snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+ if (emu->audigy){
+ /* set SPDIF bypass mode */
+ snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT);
+ /* enable rear left + rear right AC97 slots */
+ snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_REAR_RIGHT | AC97SLOT_REAR_LEFT);
+ }
+
+ /* init envelope engine */
+ for (ch = 0; ch < NUM_G; ch++) {
+ emu->voices[ch].emu = emu;
+ emu->voices[ch].number = ch;
+ snd_emu10k1_voice_init(emu, ch);
+ }
+
+ /*
+ * Init to 0x02109204 :
+ * Clock accuracy = 0 (1000ppm)
+ * Sample Rate = 2 (48kHz)
+ * Audio Channel = 1 (Left of 2)
+ * Source Number = 0 (Unspecified)
+ * Generation Status = 1 (Original for Cat Code 12)
+ * Cat Code = 12 (Digital Signal Mixer)
+ * Mode = 0 (Mode 0)
+ * Emphasis = 0 (None)
+ * CP = 1 (Copyright unasserted)
+ * AN = 0 (Audio data)
+ * P = 0 (Consumer)
+ */
+ snd_emu10k1_ptr_write(emu, SPCS0, 0,
+ emu->spdif_bits[0] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ snd_emu10k1_ptr_write(emu, SPCS1, 0,
+ emu->spdif_bits[1] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ snd_emu10k1_ptr_write(emu, SPCS2, 0,
+ emu->spdif_bits[2] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+ if (emu->audigy && emu->revision == 4) { /* audigy2 */
+ /* Hacks for Alice3 to work independent of haP16V driver */
+ u32 tmp;
+
+ //Setup SRCMulti_I2S SamplingRate
+ tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+ tmp &= 0xfffff1ff;
+ tmp |= (0x2<<9);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+
+ /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+ snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
+ /* Setup SRCMulti Input Audio Enable */
+ /* Use 0xFFFFFFFF to enable P16V sounds. */
+ snd_emu10k1_ptr20_write(emu, SRCMULTI_ENABLE, 0, 0xFFFFFFFF);
+
+ /* Enabled Phased (8-channel) P16V playback */
+ outl(0x0201, emu->port + HCFG2);
+ /* Set playback routing. */
+ snd_emu10k1_ptr_write(emu, CAPTURE_P16V_SOURCE, 0, 78e4);
+ }
+ if (emu->audigy && (emu->serial == 0x10011102) ) { /* audigy2 Value */
+ /* Hacks for Alice3 to work independent of haP16V driver */
+ u32 tmp;
+
+ snd_printk(KERN_ERR "Audigy2 value:Special config.\n");
+ //Setup SRCMulti_I2S SamplingRate
+ tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+ tmp &= 0xfffff1ff;
+ tmp |= (0x2<<9);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+
+ /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+ outl(0x600000, emu->port + 0x20);
+ outl(0x14, emu->port + 0x24);
+
+ /* Setup SRCMulti Input Audio Enable */
+ outl(0x7b0000, emu->port + 0x20);
+ outl(0xFF000000, emu->port + 0x24);
+
+ /* Setup SPDIF Out Audio Enable */
+ /* The Audigy 2 Value has a separate SPDIF out,
+ * so no need for a mixer switch
+ */
+ outl(0x7a0000, emu->port + 0x20);
+ outl(0xFF000000, emu->port + 0x24);
+ tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
+ outl(tmp, emu->port + A_IOCFG);
+ }
+
+
+ /*
+ * Clear page with silence & setup all pointers to this page
+ */
+ memset(emu->silent_page.area, 0, PAGE_SIZE);
+ silent_page = emu->silent_page.addr << 1;
+ for (idx = 0; idx < MAXPAGES; idx++)
+ ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
+ snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
+ snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */
+ snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */
+
+ silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK;
+ for (ch = 0; ch < NUM_G; ch++) {
+ snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page);
+ snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
+ }
+
+ /*
+ * Hokay, setup HCFG
+ * Mute Disable Audio = 0
+ * Lock Tank Memory = 1
+ * Lock Sound Memory = 0
+ * Auto Mute = 1
+ */
+ if (emu->audigy) {
+ if (emu->revision == 4) /* audigy2 */
+ outl(HCFG_AUDIOENABLE |
+ HCFG_AC3ENABLE_CDSPDIF |
+ HCFG_AC3ENABLE_GPSPDIF |
+ HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+ else
+ outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+ } else if (emu->model == 0x20 ||
+ emu->model == 0xc400 ||
+ (emu->model == 0x21 && emu->revision < 6))
+ outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG);
+ else
+ // With on-chip joystick
+ outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+
+ if (enable_ir) { /* enable IR for SB Live */
+ if (emu->audigy) {
+ unsigned int reg = inl(emu->port + A_IOCFG);
+ outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+ udelay(500);
+ outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+ udelay(100);
+ outl(reg, emu->port + A_IOCFG);
+ } else {
+ unsigned int reg = inl(emu->port + HCFG);
+ outl(reg | HCFG_GPOUT2, emu->port + HCFG);
+ udelay(500);
+ outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG);
+ udelay(100);
+ outl(reg, emu->port + HCFG);
+ }
+ }
+
+ if (emu->audigy) { /* enable analog output */
+ unsigned int reg = inl(emu->port + A_IOCFG);
+ outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+ }
+
+ /*
+ * Initialize the effect engine
+ */
+ if ((err = snd_emu10k1_init_efx(emu)) < 0)
+ return err;
+
+ /*
+ * Enable the audio bit
+ */
+ outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
+
+ /* Enable analog/digital outs on audigy */
+ if (emu->audigy) {
+ outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+
+ if (emu->revision == 4) { /* audigy2 */
+ /* Unmute Analog now. Set GPO6 to 1 for Apollo.
+ * This has to be done after init ALice3 I2SOut beyond 48KHz.
+ * So, sequence is important. */
+ outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
+ } else if (emu->serial == 0x10011102) { /* audigy2 value */
+ /* Unmute Analog now. */
+ outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
+ } else {
+ /* Disable routing from AC97 line out to Front speakers */
+ outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
+ }
+ }
+
+#if 0
+ {
+ unsigned int tmp;
+ /* FIXME: the following routine disables LiveDrive-II !! */
+ // TOSLink detection
+ emu->tos_link = 0;
+ tmp = inl(emu->port + HCFG);
+ if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
+ outl(tmp|0x800, emu->port + HCFG);
+ udelay(50);
+ if (tmp != (inl(emu->port + HCFG) & ~0x800)) {
+ emu->tos_link = 1;
+ outl(tmp, emu->port + HCFG);
+ }
+ }
+ }
+#endif
+
+ snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+
+ emu->reserved_page = (emu10k1_memblk_t *)snd_emu10k1_synth_alloc(emu, 4096);
+ if (emu->reserved_page)
+ emu->reserved_page->map_locked = 1;
+
+ return 0;
+}
+
+static int snd_emu10k1_done(emu10k1_t * emu)
+{
+ int ch;
+
+ outl(0, emu->port + INTE);
+
+ /*
+ * Shutdown the chip
+ */
+ for (ch = 0; ch < NUM_G; ch++)
+ snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+ for (ch = 0; ch < NUM_G; ch++) {
+ snd_emu10k1_ptr_write(emu, VTFT, ch, 0);
+ snd_emu10k1_ptr_write(emu, CVCF, ch, 0);
+ snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+ snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+ }
+
+ /* reset recording buffers */
+ snd_emu10k1_ptr_write(emu, MICBS, 0, 0);
+ snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+ snd_emu10k1_ptr_write(emu, FXBS, 0, 0);
+ snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+ snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+ snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+ snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+ snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
+ snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP);
+
+ /* disable channel interrupt */
+ snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+ snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+ snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+ snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+ /* remove reserved page */
+ if (emu->reserved_page != NULL) {
+ snd_emu10k1_synth_free(emu, (snd_util_memblk_t *)emu->reserved_page);
+ emu->reserved_page = NULL;
+ }
+
+ /* disable audio and lock cache */
+ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+ snd_emu10k1_ptr_write(emu, PTB, 0, 0);
+
+ snd_emu10k1_free_efx(emu);
+
+ return 0;
+}
+
+/*************************************************************************
+ * ECARD functional implementation
+ *************************************************************************/
+
+/* In A1 Silicon, these bits are in the HC register */
+#define HOOKN_BIT (1L << 12)
+#define HANDN_BIT (1L << 11)
+#define PULSEN_BIT (1L << 10)
+
+#define EC_GDI1 (1 << 13)
+#define EC_GDI0 (1 << 14)
+
+#define EC_NUM_CONTROL_BITS 20
+
+#define EC_AC3_DATA_SELN 0x0001L
+#define EC_EE_DATA_SEL 0x0002L
+#define EC_EE_CNTRL_SELN 0x0004L
+#define EC_EECLK 0x0008L
+#define EC_EECS 0x0010L
+#define EC_EESDO 0x0020L
+#define EC_TRIM_CSN 0x0040L
+#define EC_TRIM_SCLK 0x0080L
+#define EC_TRIM_SDATA 0x0100L
+#define EC_TRIM_MUTEN 0x0200L
+#define EC_ADCCAL 0x0400L
+#define EC_ADCRSTN 0x0800L
+#define EC_DACCAL 0x1000L
+#define EC_DACMUTEN 0x2000L
+#define EC_LEDN 0x4000L
+
+#define EC_SPDIF0_SEL_SHIFT 15
+#define EC_SPDIF1_SEL_SHIFT 17
+#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT)
+#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT)
+#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
+#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
+#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should
+ * be incremented any time the EEPROM's
+ * format is changed. */
+
+#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */
+
+/* Addresses for special values stored in to EEPROM */
+#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */
+#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */
+#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */
+
+#define EC_LAST_PROMFILE_ADDR 0x2f
+
+#define EC_SERIALNUM_ADDR 0x30 /* First word of serial number. The
+ * can be up to 30 characters in length
+ * and is stored as a NULL-terminated
+ * ASCII string. Any unused bytes must be
+ * filled with zeros */
+#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */
+
+
+/* Most of this stuff is pretty self-evident. According to the hardware
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC
+ * offset problem. Weird.
+ */
+#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \
+ EC_TRIM_CSN)
+
+
+#define EC_DEFAULT_ADC_GAIN 0xC4C4
+#define EC_DEFAULT_SPDIF0_SEL 0x0
+#define EC_DEFAULT_SPDIF1_SEL 0x4
+
+/**************************************************************************
+ * @func Clock bits into the Ecard's control latch. The Ecard uses a
+ * control latch will is loaded bit-serially by toggling the Modem control
+ * lines from function 2 on the E8010. This function hides these details
+ * and presents the illusion that we are actually writing to a distinct
+ * register.
+ */
+
+static void snd_emu10k1_ecard_write(emu10k1_t * emu, unsigned int value)
+{
+ unsigned short count;
+ unsigned int data;
+ unsigned long hc_port;
+ unsigned int hc_value;
+
+ hc_port = emu->port + HCFG;
+ hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT);
+ outl(hc_value, hc_port);
+
+ for (count = 0; count < EC_NUM_CONTROL_BITS; count++) {
+
+ /* Set up the value */
+ data = ((value & 0x1) ? PULSEN_BIT : 0);
+ value >>= 1;
+
+ outl(hc_value | data, hc_port);
+
+ /* Clock the shift register */
+ outl(hc_value | data | HANDN_BIT, hc_port);
+ outl(hc_value | data, hc_port);
+ }
+
+ /* Latch the bits */
+ outl(hc_value | HOOKN_BIT, hc_port);
+ outl(hc_value, hc_port);
+}
+
+/**************************************************************************
+ * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The
+ * trim value consists of a 16bit value which is composed of two
+ * 8 bit gain/trim values, one for the left channel and one for the
+ * right channel. The following table maps from the Gain/Attenuation
+ * value in decibels into the corresponding bit pattern for a single
+ * channel.
+ */
+
+static void snd_emu10k1_ecard_setadcgain(emu10k1_t * emu,
+ unsigned short gain)
+{
+ unsigned int bit;
+
+ /* Enable writing to the TRIM registers */
+ snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+ /* Do it again to insure that we meet hold time requirements */
+ snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+ for (bit = (1 << 15); bit; bit >>= 1) {
+ unsigned int value;
+
+ value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA);
+
+ if (gain & bit)
+ value |= EC_TRIM_SDATA;
+
+ /* Clock the bit */
+ snd_emu10k1_ecard_write(emu, value);
+ snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK);
+ snd_emu10k1_ecard_write(emu, value);
+ }
+
+ snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+}
+
+static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu)
+{
+ unsigned int hc_value;
+
+ /* Set up the initial settings */
+ emu->ecard_ctrl = EC_RAW_RUN_MODE |
+ EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) |
+ EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL);
+
+ /* Step 0: Set the codec type in the hardware control register
+ * and enable audio output */
+ hc_value = inl(emu->port + HCFG);
+ outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG);
+ inl(emu->port + HCFG);
+
+ /* Step 1: Turn off the led and deassert TRIM_CS */
+ snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+ /* Step 2: Calibrate the ADC and DAC */
+ snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
+
+ /* Step 3: Wait for awhile; XXX We can't get away with this
+ * under a real operating system; we'll need to block and wait that
+ * way. */
+ snd_emu10k1_wait(emu, 48000);
+
+ /* Step 4: Switch off the DAC and ADC calibration. Note
+ * That ADC_CAL is actually an inverted signal, so we assert
+ * it here to stop calibration. */
+ snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+ /* Step 4: Switch into run mode */
+ snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+
+ /* Step 5: Set the analog input gain */
+ snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN);
+
+ return 0;
+}
+
+/*
+ * Create the EMU10K1 instance
+ */
+
+static int snd_emu10k1_free(emu10k1_t *emu)
+{
+ if (emu->port) { /* avoid access to already used hardware */
+ snd_emu10k1_fx8010_tram_setup(emu, 0);
+ snd_emu10k1_done(emu);
+ }
+ if (emu->memhdr)
+ snd_util_memhdr_free(emu->memhdr);
+ if (emu->silent_page.area)
+ snd_dma_free_pages(&emu->silent_page);
+ if (emu->ptb_pages.area)
+ snd_dma_free_pages(&emu->ptb_pages);
+ vfree(emu->page_ptr_table);
+ vfree(emu->page_addr_table);
+ if (emu->irq >= 0)
+ free_irq(emu->irq, (void *)emu);
+ if (emu->port)
+ pci_release_regions(emu->pci);
+ pci_disable_device(emu->pci);
+ if (emu->audigy && emu->revision == 4) /* P16V */
+ snd_p16v_free(emu);
+ kfree(emu);
+ return 0;
+}
+
+static int snd_emu10k1_dev_free(snd_device_t *device)
+{
+ emu10k1_t *emu = device->device_data;
+ return snd_emu10k1_free(emu);
+}
+
+/* vendor, device, subsystem, emu10k1_chip, emu10k2_chip, ca0102_chip, ca0108_chip, ca0151_chip, spk71, spdif_bug, ac97_chip, ecard, driver, name */
+
+static emu_chip_details_t emu_chip_details[] = {
+ /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
+ .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1} ,
+ {.vendor = 0x1102, .device = 0x0008,
+ .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1} ,
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
+ .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .ac97_chip = 1} ,
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
+ .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .ac97_chip = 1} ,
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
+ .driver = "Audigy2", .name = "Audigy 2 ZS [2001]",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .ac97_chip = 1} ,
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
+ .driver = "Audigy2", .name = "Audigy 2 [SB0240]",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .ac97_chip = 1} ,
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
+ .driver = "Audigy2", .name = "Audigy 2 EX [1005]",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spdif_bug = 1} ,
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
+ .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .ac97_chip = 1} ,
+ {.vendor = 0x1102, .device = 0x0004,
+ .driver = "Audigy", .name = "Audigy 1 or 2 [Unknown]",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .spdif_bug = 1} ,
+ {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
+ .driver = "EMU10K1", .name = "E-mu APS [4001]",
+ .emu10k1_chip = 1,
+ .ecard = 1} ,
+ {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
+ .driver = "EMU10K1", .name = "SB Live 5.1",
+ .emu10k1_chip = 1,
+ .ac97_chip = 1} ,
+ {.vendor = 0x1102, .device = 0x0002,
+ .driver = "EMU10K1", .name = "SB Live [Unknown]",
+ .emu10k1_chip = 1,
+ .ac97_chip = 1} ,
+ { } /* terminator */
+};
+
+int __devinit snd_emu10k1_create(snd_card_t * card,
+ struct pci_dev * pci,
+ unsigned short extin_mask,
+ unsigned short extout_mask,
+ long max_cache_bytes,
+ int enable_ir,
+ emu10k1_t ** remu)
+{
+ emu10k1_t *emu;
+ int err;
+ int is_audigy;
+ unsigned char revision;
+ const emu_chip_details_t *c;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_emu10k1_dev_free,
+ };
+
+ *remu = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ emu = kcalloc(1, sizeof(*emu), GFP_KERNEL);
+ if (emu == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ emu->card = card;
+ spin_lock_init(&emu->reg_lock);
+ spin_lock_init(&emu->emu_lock);
+ spin_lock_init(&emu->voice_lock);
+ spin_lock_init(&emu->synth_lock);
+ spin_lock_init(&emu->memblk_lock);
+ init_MUTEX(&emu->ptb_lock);
+ init_MUTEX(&emu->fx8010.lock);
+ INIT_LIST_HEAD(&emu->mapped_link_head);
+ INIT_LIST_HEAD(&emu->mapped_order_link_head);
+ emu->pci = pci;
+ emu->irq = -1;
+ emu->synth = NULL;
+ emu->get_synth_voice = NULL;
+ /* read revision & serial */
+ pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+ emu->revision = revision;
+ pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
+ emu->card_type = EMU10K1_CARD_CREATIVE;
+ snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model);
+
+ for (c = emu_chip_details; c->vendor; c++) {
+ if (c->vendor == pci->vendor && c->device == pci->device) {
+ if (c->subsystem == emu->serial) break;
+ if (c->subsystem == 0) break;
+ }
+ }
+ if (c->vendor == 0) {
+ snd_printk(KERN_ERR "emu10k1: Card not recognised\n");
+ kfree(emu);
+ pci_disable_device(pci);
+ return -ENOENT;
+ }
+ emu->card_capabilities = c;
+ if (c->subsystem != 0)
+ snd_printdd("Sound card name=%s\n", c->name);
+ else
+ snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x\n", c->name, pci->vendor, pci->device, emu->serial);
+
+ is_audigy = emu->audigy = c->emu10k2_chip;
+
+ /* set the DMA transfer mask */
+ emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK;
+ if (pci_set_dma_mask(pci, emu->dma_mask) < 0 ||
+ pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) {
+ snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask);
+ kfree(emu);
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+ if (is_audigy)
+ emu->gpr_base = A_FXGPREGBASE;
+ else
+ emu->gpr_base = FXGPREGBASE;
+
+ if ((err = pci_request_regions(pci, "EMU10K1")) < 0) {
+ kfree(emu);
+ pci_disable_device(pci);
+ return err;
+ }
+ emu->port = pci_resource_start(pci, 0);
+
+ if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) {
+ snd_emu10k1_free(emu);
+ return -EBUSY;
+ }
+ emu->irq = pci->irq;
+
+ emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ 32 * 1024, &emu->ptb_pages) < 0) {
+ snd_emu10k1_free(emu);
+ return -ENOMEM;
+ }
+
+ emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*));
+ emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long));
+ if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
+ snd_emu10k1_free(emu);
+ return -ENOMEM;
+ }
+
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ EMUPAGESIZE, &emu->silent_page) < 0) {
+ snd_emu10k1_free(emu);
+ return -ENOMEM;
+ }
+ emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
+ if (emu->memhdr == NULL) {
+ snd_emu10k1_free(emu);
+ return -ENOMEM;
+ }
+ emu->memhdr->block_extra_size = sizeof(emu10k1_memblk_t) - sizeof(snd_util_memblk_t);
+
+ pci_set_master(pci);
+
+ if (c->ecard) {
+ emu->card_type = EMU10K1_CARD_EMUAPS;
+ emu->APS = 1;
+ }
+ if (! c->ac97_chip)
+ emu->no_ac97 = 1;
+
+ emu->spk71 = c->spk71;
+
+ emu->fx8010.fxbus_mask = 0x303f;
+ if (extin_mask == 0)
+ extin_mask = 0x3fcf;
+ if (extout_mask == 0)
+ extout_mask = 0x7fff;
+ emu->fx8010.extin_mask = extin_mask;
+ emu->fx8010.extout_mask = extout_mask;
+
+ if (emu->APS) {
+ if ((err = snd_emu10k1_ecard_init(emu)) < 0) {
+ snd_emu10k1_free(emu);
+ return err;
+ }
+ } else {
+ /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
+ does not support this, it shouldn't do any harm */
+ snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+ }
+
+ if ((err = snd_emu10k1_init(emu, enable_ir)) < 0) {
+ snd_emu10k1_free(emu);
+ return err;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0) {
+ snd_emu10k1_free(emu);
+ return err;
+ }
+
+ snd_emu10k1_proc_init(emu);
+
+ snd_card_set_dev(card, &pci->dev);
+ *remu = emu;
+ return 0;
+}
+
+/* memory.c */
+EXPORT_SYMBOL(snd_emu10k1_synth_alloc);
+EXPORT_SYMBOL(snd_emu10k1_synth_free);
+EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
+EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user);
+EXPORT_SYMBOL(snd_emu10k1_memblk_map);
+/* voice.c */
+EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
+EXPORT_SYMBOL(snd_emu10k1_voice_free);
+/* io.c */
+EXPORT_SYMBOL(snd_emu10k1_ptr_read);
+EXPORT_SYMBOL(snd_emu10k1_ptr_write);
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
new file mode 100644
index 00000000000..4df668eb32b
--- /dev/null
+++ b/sound/pci/emu10k1/emu10k1_patch.c
@@ -0,0 +1,223 @@
+/*
+ * Patch transfer callback for Emu10k1
+ *
+ * Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * All the code for loading in a patch. There is very little that is
+ * chip specific here. Just the actual writing to the board.
+ */
+
+#include "emu10k1_synth_local.h"
+
+/*
+ */
+#define BLANK_LOOP_START 4
+#define BLANK_LOOP_END 8
+#define BLANK_LOOP_SIZE 12
+#define BLANK_HEAD_SIZE 32
+
+/*
+ * allocate a sample block and copy data from userspace
+ */
+int
+snd_emu10k1_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp,
+ snd_util_memhdr_t *hdr, const void __user *data, long count)
+{
+ int offset;
+ int truesize, size, loopsize, blocksize;
+ int loopend, sampleend;
+ unsigned int start_addr;
+ emu10k1_t *emu;
+
+ emu = rec->hw;
+ snd_assert(sp != NULL, return -EINVAL);
+ snd_assert(hdr != NULL, return -EINVAL);
+
+ if (sp->v.size == 0) {
+ snd_printd("emu: rom font for sample %d\n", sp->v.sample);
+ return 0;
+ }
+
+ /* recalculate address offset */
+ sp->v.end -= sp->v.start;
+ sp->v.loopstart -= sp->v.start;
+ sp->v.loopend -= sp->v.start;
+ sp->v.start = 0;
+
+ /* some samples have invalid data. the addresses are corrected in voice info */
+ sampleend = sp->v.end;
+ if (sampleend > sp->v.size)
+ sampleend = sp->v.size;
+ loopend = sp->v.loopend;
+ if (loopend > sampleend)
+ loopend = sampleend;
+
+ /* be sure loop points start < end */
+ if (sp->v.loopstart >= sp->v.loopend) {
+ int tmp = sp->v.loopstart;
+ sp->v.loopstart = sp->v.loopend;
+ sp->v.loopend = tmp;
+ }
+
+ /* compute true data size to be loaded */
+ truesize = sp->v.size + BLANK_HEAD_SIZE;
+ loopsize = 0;
+#if 0 /* not supported */
+ if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+ loopsize = sp->v.loopend - sp->v.loopstart;
+ truesize += loopsize;
+#endif
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+ truesize += BLANK_LOOP_SIZE;
+
+ /* try to allocate a memory block */
+ blocksize = truesize;
+ if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+ blocksize *= 2;
+ sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
+ if (sp->block == NULL) {
+ snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize);
+ /* not ENOMEM (for compatibility with OSS) */
+ return -ENOSPC;
+ }
+ /* set the total size */
+ sp->v.truesize = blocksize;
+
+ /* write blank samples at head */
+ offset = 0;
+ size = BLANK_HEAD_SIZE;
+ if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+ size *= 2;
+ snd_assert(offset + size <= blocksize, return -EINVAL);
+ snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
+ offset += size;
+
+ /* copy start->loopend */
+ size = loopend;
+ if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+ size *= 2;
+ snd_assert(offset + size <= blocksize, return -EINVAL);
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+ snd_emu10k1_synth_free(emu, sp->block);
+ sp->block = NULL;
+ return -EFAULT;
+ }
+ offset += size;
+ data += size;
+
+#if 0 /* not suppported yet */
+ /* handle reverse (or bidirectional) loop */
+ if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+ /* copy loop in reverse */
+ if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+ int woffset;
+ unsigned short *wblock = (unsigned short*)block;
+ woffset = offset / 2;
+ snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL);
+ for (i = 0; i < loopsize; i++)
+ wblock[woffset + i] = wblock[woffset - i -1];
+ offset += loopsize * 2;
+ } else {
+ snd_assert(offset + loopsize <= blocksize, return -EINVAL);
+ for (i = 0; i < loopsize; i++)
+ block[offset + i] = block[offset - i -1];
+ offset += loopsize;
+ }
+
+ /* modify loop pointers */
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+ sp->v.loopend += loopsize;
+ } else {
+ sp->v.loopstart += loopsize;
+ sp->v.loopend += loopsize;
+ }
+ /* add sample pointer */
+ sp->v.end += loopsize;
+ }
+#endif
+
+ /* loopend -> sample end */
+ size = sp->v.size - loopend;
+ snd_assert(size >= 0, return -EINVAL);
+ if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+ size *= 2;
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+ snd_emu10k1_synth_free(emu, sp->block);
+ sp->block = NULL;
+ return -EFAULT;
+ }
+ offset += size;
+
+ /* clear rest of samples (if any) */
+ if (offset < blocksize)
+ snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
+
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+ /* if no blank loop is attached in the sample, add it */
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+ sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+ sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+ }
+ }
+
+#if 0 /* not supported yet */
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
+ /* unsigned -> signed */
+ if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+ unsigned short *wblock = (unsigned short*)block;
+ for (i = 0; i < truesize; i++)
+ wblock[i] ^= 0x8000;
+ } else {
+ for (i = 0; i < truesize; i++)
+ block[i] ^= 0x80;
+ }
+ }
+#endif
+
+ /* recalculate offset */
+ start_addr = BLANK_HEAD_SIZE * 2;
+ if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+ start_addr >>= 1;
+ sp->v.start += start_addr;
+ sp->v.end += start_addr;
+ sp->v.loopstart += start_addr;
+ sp->v.loopend += start_addr;
+
+ return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu10k1_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp,
+ snd_util_memhdr_t *hdr)
+{
+ emu10k1_t *emu;
+
+ emu = rec->hw;
+ snd_assert(sp != NULL, return -EINVAL);
+ snd_assert(hdr != NULL, return -EINVAL);
+
+ if (sp->block) {
+ snd_emu10k1_synth_free(emu, sp->block);
+ sp->block = NULL;
+ }
+ return 0;
+}
+
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
new file mode 100644
index 00000000000..8bd58d1dcc2
--- /dev/null
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * Routines for control of EMU10K1 WaveTable synth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "emu10k1_synth_local.h"
+#include <linux/init.h>
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth");
+MODULE_LICENSE("GPL");
+
+/*
+ * create a new hardware dependent device for Emu10k1
+ */
+static int snd_emu10k1_synth_new_device(snd_seq_device_t *dev)
+{
+ snd_emux_t *emu;
+ emu10k1_t *hw;
+ snd_emu10k1_synth_arg_t *arg;
+ unsigned long flags;
+
+ arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+ if (arg == NULL)
+ return -EINVAL;
+
+ if (arg->seq_ports <= 0)
+ return 0; /* nothing */
+ if (arg->max_voices < 1)
+ arg->max_voices = 1;
+ else if (arg->max_voices > 64)
+ arg->max_voices = 64;
+
+ if (snd_emux_new(&emu) < 0)
+ return -ENOMEM;
+
+ snd_emu10k1_ops_setup(emu);
+ emu->hw = hw = arg->hwptr;
+ emu->max_voices = arg->max_voices;
+ emu->num_ports = arg->seq_ports;
+ emu->pitch_shift = -501;
+ emu->memhdr = hw->memhdr;
+ emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */
+ emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */
+ emu->linear_panning = 0;
+ emu->hwdep_idx = 2; /* FIXED */
+
+ if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) {
+ snd_emux_free(emu);
+ emu->hw = NULL;
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&hw->voice_lock, flags);
+ hw->synth = emu;
+ hw->get_synth_voice = snd_emu10k1_synth_get_voice;
+ spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+ dev->driver_data = emu;
+
+ return 0;
+}
+
+static int snd_emu10k1_synth_delete_device(snd_seq_device_t *dev)
+{
+ snd_emux_t *emu;
+ emu10k1_t *hw;
+ unsigned long flags;
+
+ if (dev->driver_data == NULL)
+ return 0; /* not registered actually */
+
+ emu = dev->driver_data;
+
+ hw = emu->hw;
+ spin_lock_irqsave(&hw->voice_lock, flags);
+ hw->synth = NULL;
+ hw->get_synth_voice = NULL;
+ spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+ snd_emux_free(emu);
+ return 0;
+}
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_emu10k1_synth_init(void)
+{
+
+ static snd_seq_dev_ops_t ops = {
+ snd_emu10k1_synth_new_device,
+ snd_emu10k1_synth_delete_device,
+ };
+ return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, sizeof(snd_emu10k1_synth_arg_t));
+}
+
+static void __exit alsa_emu10k1_synth_exit(void)
+{
+ snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH);
+}
+
+module_init(alsa_emu10k1_synth_init)
+module_exit(alsa_emu10k1_synth_exit)
diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h
new file mode 100644
index 00000000000..7f50b01ddb7
--- /dev/null
+++ b/sound/pci/emu10k1/emu10k1_synth_local.h
@@ -0,0 +1,38 @@
+#ifndef __EMU10K1_SYNTH_LOCAL_H
+#define __EMU10K1_SYNTH_LOCAL_H
+/*
+ * Local defininitons for Emu10k1 wavetable
+ *
+ * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1_synth.h>
+
+/* emu10k1_patch.c */
+int snd_emu10k1_sample_new(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void __user *_data, long count);
+int snd_emu10k1_sample_free(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr);
+int snd_emu10k1_memhdr_init(snd_emux_t *emu);
+
+/* emu10k1_callback.c */
+void snd_emu10k1_ops_setup(snd_emux_t *emu);
+int snd_emu10k1_synth_get_voice(emu10k1_t *hw);
+
+
+#endif /* __EMU10K1_SYNTH_LOCAL_H */
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
new file mode 100644
index 00000000000..27dfd8ddddf
--- /dev/null
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -0,0 +1,1643 @@
+/*
+ * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ * Driver EMU10K1X chips
+ *
+ * Parts of this code were adapted from audigyls.c driver which is
+ * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ *
+ * Chips (SB0200 model):
+ * - EMU10K1X-DBQ
+ * - STAC 9708T
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+
+MODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>");
+MODULE_DESCRIPTION("EMU10K1X");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}");
+
+// module parameters (see "Module Parameters")
+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;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the EMU10K1X soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
+
+
+// some definitions were borrowed from emu10k1 driver as they seem to be the same
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0 */
+/************************************************************************************************/
+
+#define PTR 0x00 /* Indexed register set pointer register */
+ /* NOTE: The CHANNELNUM and ADDRESS words can */
+ /* be modified independently of each other. */
+
+#define DATA 0x04 /* Indexed register set data register */
+
+#define IPR 0x08 /* Global interrupt pending register */
+ /* Clear pending interrupts by writing a 1 to */
+ /* the relevant bits and zero to the other bits */
+#define IPR_MIDITRANSBUFEMPTY 0x00000001 /* MIDI UART transmit buffer empty */
+#define IPR_MIDIRECVBUFEMPTY 0x00000002 /* MIDI UART receive buffer empty */
+#define IPR_CH_0_LOOP 0x00000800 /* Channel 0 loop */
+#define IPR_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */
+#define IPR_CAP_0_LOOP 0x00080000 /* Channel capture loop */
+#define IPR_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */
+
+#define INTE 0x0c /* Interrupt enable register */
+#define INTE_MIDITXENABLE 0x00000001 /* Enable MIDI transmit-buffer-empty interrupts */
+#define INTE_MIDIRXENABLE 0x00000002 /* Enable MIDI receive-buffer-empty interrupts */
+#define INTE_CH_0_LOOP 0x00000800 /* Channel 0 loop */
+#define INTE_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */
+#define INTE_CAP_0_LOOP 0x00080000 /* Channel capture loop */
+#define INTE_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */
+
+#define HCFG 0x14 /* Hardware config register */
+
+#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */
+ /* NOTE: This should generally never be used. */
+#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */
+ /* Should be set to 1 when the EMU10K1 is */
+ /* completely initialized. */
+#define GPIO 0x18 /* Defaults: 00001080-Analog, 00001000-SPDIF. */
+
+
+#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */
+
+#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
+
+/********************************************************************************************************/
+/* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers */
+/********************************************************************************************************/
+#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
+ /* One list entry: 4 bytes for DMA address,
+ * 4 bytes for period_size << 16.
+ * One list entry is 8 bytes long.
+ * One list entry for each period in the buffer.
+ */
+#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
+#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size */
+#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Sample currently in DAC */
+#define PLAYBACK_UNKNOWN1 0x07
+#define PLAYBACK_UNKNOWN2 0x08
+
+/* Only one capture channel supported */
+#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */
+#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */
+#define CAPTURE_UNKNOWN 0x13
+
+/* From 0x20 - 0x3f, last samples played on each channel */
+
+#define TRIGGER_CHANNEL 0x40 /* Trigger channel playback */
+#define TRIGGER_CHANNEL_0 0x00000001 /* Trigger channel 0 */
+#define TRIGGER_CHANNEL_1 0x00000002 /* Trigger channel 1 */
+#define TRIGGER_CHANNEL_2 0x00000004 /* Trigger channel 2 */
+#define TRIGGER_CAPTURE 0x00000100 /* Trigger capture channel */
+
+#define ROUTING 0x41 /* Setup sound routing ? */
+#define ROUTING_FRONT_LEFT 0x00000001
+#define ROUTING_FRONT_RIGHT 0x00000002
+#define ROUTING_REAR_LEFT 0x00000004
+#define ROUTING_REAR_RIGHT 0x00000008
+#define ROUTING_CENTER_LFE 0x00010000
+
+#define SPCS0 0x42 /* SPDIF output Channel Status 0 register */
+
+#define SPCS1 0x43 /* SPDIF output Channel Status 1 register */
+
+#define SPCS2 0x44 /* SPDIF output Channel Status 2 register */
+
+#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */
+#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */
+#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */
+#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */
+#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */
+#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */
+#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */
+#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */
+#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */
+#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */
+#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */
+#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */
+#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */
+#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */
+#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */
+#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */
+#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */
+#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */
+#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */
+#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */
+#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */
+#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */
+#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */
+
+#define SPDIF_SELECT 0x45 /* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF */
+
+/* This is the MPU port on the card */
+#define MUDATA 0x47
+#define MUCMD 0x48
+#define MUSTAT MUCMD
+
+/* From 0x50 - 0x5f, last samples captured */
+
+/**
+ * The hardware has 3 channels for playback and 1 for capture.
+ * - channel 0 is the front channel
+ * - channel 1 is the rear channel
+ * - channel 2 is the center/lfe chanel
+ * Volume is controlled by the AC97 for the front and rear channels by
+ * the PCM Playback Volume, Sigmatel Surround Playback Volume and
+ * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects
+ * the front/rear channel mixing in the REAR OUT jack. When using the
+ * 4-Speaker Stereo, both front and rear channels will be mixed in the
+ * REAR OUT.
+ * The center/lfe channel has no volume control and cannot be muted during
+ * playback.
+ */
+
+typedef struct snd_emu10k1x_voice emu10k1x_voice_t;
+typedef struct snd_emu10k1x emu10k1x_t;
+typedef struct snd_emu10k1x_pcm emu10k1x_pcm_t;
+
+struct snd_emu10k1x_voice {
+ emu10k1x_t *emu;
+ int number;
+ int use;
+
+ emu10k1x_pcm_t *epcm;
+};
+
+struct snd_emu10k1x_pcm {
+ emu10k1x_t *emu;
+ snd_pcm_substream_t *substream;
+ emu10k1x_voice_t *voice;
+ unsigned short running;
+};
+
+typedef struct {
+ struct snd_emu10k1x *emu;
+ snd_rawmidi_t *rmidi;
+ snd_rawmidi_substream_t *substream_input;
+ snd_rawmidi_substream_t *substream_output;
+ unsigned int midi_mode;
+ spinlock_t input_lock;
+ spinlock_t output_lock;
+ spinlock_t open_lock;
+ int tx_enable, rx_enable;
+ int port;
+ int ipr_tx, ipr_rx;
+ void (*interrupt)(emu10k1x_t *emu, unsigned int status);
+} emu10k1x_midi_t;
+
+// definition of the chip-specific record
+struct snd_emu10k1x {
+ snd_card_t *card;
+ struct pci_dev *pci;
+
+ unsigned long port;
+ struct resource *res_port;
+ int irq;
+
+ unsigned int revision; /* chip revision */
+ unsigned int serial; /* serial number */
+ unsigned short model; /* subsystem id */
+
+ spinlock_t emu_lock;
+ spinlock_t voice_lock;
+
+ ac97_t *ac97;
+ snd_pcm_t *pcm;
+
+ emu10k1x_voice_t voices[3];
+ emu10k1x_voice_t capture_voice;
+ u32 spdif_bits[3]; // SPDIF out setup
+
+ struct snd_dma_buffer dma_buffer;
+
+ emu10k1x_midi_t midi;
+};
+
+/* hardware definition */
+static snd_pcm_hardware_t snd_emu10k1x_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = 8,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_emu10k1x_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static unsigned int snd_emu10k1x_ptr_read(emu10k1x_t * emu,
+ unsigned int reg,
+ unsigned int chn)
+{
+ unsigned long flags;
+ unsigned int regptr, val;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+}
+
+static void snd_emu10k1x_ptr_write(emu10k1x_t *emu,
+ unsigned int reg,
+ unsigned int chn,
+ unsigned int data)
+{
+ unsigned int regptr;
+ unsigned long flags;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ outl(data, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_enable(emu10k1x_t *emu, unsigned int intrenb)
+{
+ unsigned long flags;
+ unsigned int enable;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ enable = inl(emu->port + INTE) | intrenb;
+ outl(enable, emu->port + INTE);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_disable(emu10k1x_t *emu, unsigned int intrenb)
+{
+ unsigned long flags;
+ unsigned int enable;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ enable = inl(emu->port + INTE) & ~intrenb;
+ outl(enable, emu->port + INTE);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_gpio_write(emu10k1x_t *emu, unsigned int value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(value, emu->port + GPIO);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+
+ if (epcm)
+ kfree(epcm);
+}
+
+static void snd_emu10k1x_pcm_interrupt(emu10k1x_t *emu, emu10k1x_voice_t *voice)
+{
+ emu10k1x_pcm_t *epcm;
+
+ if ((epcm = voice->epcm) == NULL)
+ return;
+ if (epcm->substream == NULL)
+ return;
+#if 0
+ snd_printk(KERN_INFO "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ epcm->substream->ops->pointer(epcm->substream),
+ snd_pcm_lib_period_bytes(epcm->substream),
+ snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+ snd_pcm_period_elapsed(epcm->substream);
+}
+
+/* open callback */
+static int snd_emu10k1x_playback_open(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *chip = snd_pcm_substream_chip(substream);
+ emu10k1x_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+ return err;
+ }
+ if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ return err;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = chip;
+ epcm->substream = substream;
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_emu10k1x_pcm_free_substream;
+
+ runtime->hw = snd_emu10k1x_playback_hw;
+
+ return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_playback_close(snd_pcm_substream_t *substream)
+{
+ return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+
+ if (! epcm->voice) {
+ epcm->voice = &epcm->emu->voices[substream->pcm->device];
+ epcm->voice->use = 1;
+ epcm->voice->epcm = epcm;
+ }
+
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm;
+
+ if (runtime->private_data == NULL)
+ return 0;
+
+ epcm = runtime->private_data;
+
+ if (epcm->voice) {
+ epcm->voice->use = 0;
+ epcm->voice->epcm = NULL;
+ epcm->voice = NULL;
+ }
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_emu10k1x_pcm_prepare(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+ int voice = epcm->voice->number;
+ u32 *table_base = (u32 *)(emu->dma_buffer.area+1024*voice);
+ u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+ int i;
+
+ for(i=0; i < runtime->periods; i++) {
+ *table_base++=runtime->dma_addr+(i*period_size_bytes);
+ *table_base++=period_size_bytes<<16;
+ }
+
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer.addr+1024*voice);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN1, voice, 0);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN2, voice, 0);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_DMA_ADDR, voice, runtime->dma_addr);
+
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_PERIOD_SIZE, voice, frames_to_bytes(runtime, runtime->period_size)<<16);
+
+ return 0;
+}
+
+/* trigger callback */
+static int snd_emu10k1x_pcm_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+ int channel = epcm->voice->number;
+ int result = 0;
+
+// snd_printk(KERN_INFO "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", (int)emu, cmd, (int)substream->ops->pointer(substream));
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if(runtime->periods == 2)
+ snd_emu10k1x_intr_enable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+ else
+ snd_emu10k1x_intr_enable(emu, INTE_CH_0_LOOP << channel);
+ epcm->running = 1;
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|(TRIGGER_CHANNEL_0<<channel));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ epcm->running = 0;
+ snd_emu10k1x_intr_disable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CHANNEL_0<<channel));
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+ int channel = epcm->voice->number;
+ snd_pcm_uframes_t ptr = 0, ptr1 = 0, ptr2= 0,ptr3 = 0,ptr4 = 0;
+
+ if (!epcm->running)
+ return 0;
+
+ ptr3 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+ ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+ ptr4 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+
+ if(ptr4 == 0 && ptr1 == frames_to_bytes(runtime, runtime->buffer_size))
+ return 0;
+
+ if (ptr3 != ptr4)
+ ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+ ptr2 = bytes_to_frames(runtime, ptr1);
+ ptr2 += (ptr4 >> 3) * runtime->period_size;
+ ptr = ptr2;
+
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+
+ return ptr;
+}
+
+/* operators */
+static snd_pcm_ops_t snd_emu10k1x_playback_ops = {
+ .open = snd_emu10k1x_playback_open,
+ .close = snd_emu10k1x_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1x_pcm_hw_params,
+ .hw_free = snd_emu10k1x_pcm_hw_free,
+ .prepare = snd_emu10k1x_pcm_prepare,
+ .trigger = snd_emu10k1x_pcm_trigger,
+ .pointer = snd_emu10k1x_pcm_pointer,
+};
+
+/* open_capture callback */
+static int snd_emu10k1x_pcm_open_capture(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *chip = snd_pcm_substream_chip(substream);
+ emu10k1x_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ return err;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL)
+ return -ENOMEM;
+
+ epcm->emu = chip;
+ epcm->substream = substream;
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_emu10k1x_pcm_free_substream;
+
+ runtime->hw = snd_emu10k1x_capture_hw;
+
+ return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_pcm_close_capture(snd_pcm_substream_t *substream)
+{
+ return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params_capture(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+
+ if (! epcm->voice) {
+ if (epcm->emu->capture_voice.use)
+ return -EBUSY;
+ epcm->voice = &epcm->emu->capture_voice;
+ epcm->voice->epcm = epcm;
+ epcm->voice->use = 1;
+ }
+
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free_capture(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ emu10k1x_pcm_t *epcm;
+
+ if (runtime->private_data == NULL)
+ return 0;
+ epcm = runtime->private_data;
+
+ if (epcm->voice) {
+ epcm->voice->use = 0;
+ epcm->voice->epcm = NULL;
+ epcm->voice = NULL;
+ }
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare capture callback */
+static int snd_emu10k1x_pcm_prepare_capture(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_emu10k1x_ptr_write(emu, CAPTURE_DMA_ADDR, 0, runtime->dma_addr);
+ snd_emu10k1x_ptr_write(emu, CAPTURE_BUFFER_SIZE, 0, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+ snd_emu10k1x_ptr_write(emu, CAPTURE_POINTER, 0, 0);
+ snd_emu10k1x_ptr_write(emu, CAPTURE_UNKNOWN, 0, 0);
+
+ return 0;
+}
+
+/* trigger_capture callback */
+static int snd_emu10k1x_pcm_trigger_capture(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+ int result = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_emu10k1x_intr_enable(emu, INTE_CAP_0_LOOP |
+ INTE_CAP_0_HALF_LOOP);
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|TRIGGER_CAPTURE);
+ epcm->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ epcm->running = 0;
+ snd_emu10k1x_intr_disable(emu, INTE_CAP_0_LOOP |
+ INTE_CAP_0_HALF_LOOP);
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CAPTURE));
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer_capture(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = runtime->private_data;
+ snd_pcm_uframes_t ptr;
+
+ if (!epcm->running)
+ return 0;
+
+ ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, CAPTURE_POINTER, 0));
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+
+ return ptr;
+}
+
+static snd_pcm_ops_t snd_emu10k1x_capture_ops = {
+ .open = snd_emu10k1x_pcm_open_capture,
+ .close = snd_emu10k1x_pcm_close_capture,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1x_pcm_hw_params_capture,
+ .hw_free = snd_emu10k1x_pcm_hw_free_capture,
+ .prepare = snd_emu10k1x_pcm_prepare_capture,
+ .trigger = snd_emu10k1x_pcm_trigger_capture,
+ .pointer = snd_emu10k1x_pcm_pointer_capture,
+};
+
+static unsigned short snd_emu10k1x_ac97_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ emu10k1x_t *emu = ac97->private_data;
+ unsigned long flags;
+ unsigned short val;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outb(reg, emu->port + AC97ADDRESS);
+ val = inw(emu->port + AC97DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+}
+
+static void snd_emu10k1x_ac97_write(ac97_t *ac97,
+ unsigned short reg, unsigned short val)
+{
+ emu10k1x_t *emu = ac97->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outb(reg, emu->port + AC97ADDRESS);
+ outw(val, emu->port + AC97DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_emu10k1x_ac97(emu10k1x_t *chip)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_emu10k1x_ac97_write,
+ .read = snd_emu10k1x_ac97_read,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+ pbus->no_vra = 1; /* we don't need VRA */
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.scaps = AC97_SCAP_NO_SPDIF;
+ return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_emu10k1x_free(emu10k1x_t *chip)
+{
+ snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0);
+ // disable interrupts
+ outl(0, chip->port + INTE);
+ // disable audio
+ outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+
+ // release the i/o port
+ if (chip->res_port) {
+ release_resource(chip->res_port);
+ kfree_nocheck(chip->res_port);
+ }
+ // release the irq
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+
+ // release the DMA
+ if (chip->dma_buffer.area) {
+ snd_dma_free_pages(&chip->dma_buffer);
+ }
+
+ pci_disable_device(chip->pci);
+
+ // release the data
+ kfree(chip);
+ return 0;
+}
+
+static int snd_emu10k1x_dev_free(snd_device_t *device)
+{
+ emu10k1x_t *chip = device->device_data;
+ return snd_emu10k1x_free(chip);
+}
+
+static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned int status;
+
+ emu10k1x_t *chip = dev_id;
+ emu10k1x_voice_t *pvoice = chip->voices;
+ int i;
+ int mask;
+
+ status = inl(chip->port + IPR);
+
+ if(status) {
+ // capture interrupt
+ if(status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) {
+ emu10k1x_voice_t *pvoice = &chip->capture_voice;
+ if(pvoice->use)
+ snd_emu10k1x_pcm_interrupt(chip, pvoice);
+ else
+ snd_emu10k1x_intr_disable(chip,
+ INTE_CAP_0_LOOP |
+ INTE_CAP_0_HALF_LOOP);
+ }
+
+ mask = IPR_CH_0_LOOP|IPR_CH_0_HALF_LOOP;
+ for(i = 0; i < 3; i++) {
+ if(status & mask) {
+ if(pvoice->use)
+ snd_emu10k1x_pcm_interrupt(chip, pvoice);
+ else
+ snd_emu10k1x_intr_disable(chip, mask);
+ }
+ pvoice++;
+ mask <<= 1;
+ }
+
+ if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+ if (chip->midi.interrupt)
+ chip->midi.interrupt(chip, status);
+ else
+ snd_emu10k1x_intr_disable(chip, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+ }
+
+ // acknowledge the interrupt if necessary
+ if(status)
+ outl(status, chip->port+IPR);
+
+// snd_printk(KERN_INFO "interrupt %08x\n", status);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void snd_emu10k1x_pcm_free(snd_pcm_t *pcm)
+{
+ emu10k1x_t *emu = pcm->private_data;
+ emu->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_emu10k1x_pcm(emu10k1x_t *emu, int device, snd_pcm_t **rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+ int capture = 0;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if (device == 0)
+ capture = 1;
+
+ if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = emu;
+ pcm->private_free = snd_emu10k1x_pcm_free;
+
+ switch(device) {
+ case 0:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops);
+ break;
+ case 1:
+ case 2:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+ break;
+ }
+
+ pcm->info_flags = 0;
+ pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+ switch(device) {
+ case 0:
+ strcpy(pcm->name, "EMU10K1X Front");
+ break;
+ case 1:
+ strcpy(pcm->name, "EMU10K1X Rear");
+ break;
+ case 2:
+ strcpy(pcm->name, "EMU10K1X Center/LFE");
+ break;
+ }
+ emu->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(emu->pci),
+ 32*1024, 32*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+static int __devinit snd_emu10k1x_create(snd_card_t *card,
+ struct pci_dev *pci,
+ emu10k1x_t **rchip)
+{
+ emu10k1x_t *chip;
+ int err;
+ int ch;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_emu10k1x_dev_free,
+ };
+
+ *rchip = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
+ snd_printk(KERN_ERR "error to set 28bit mask DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ spin_lock_init(&chip->emu_lock);
+ spin_lock_init(&chip->voice_lock);
+
+ chip->port = pci_resource_start(pci, 0);
+ if ((chip->res_port = request_region(chip->port, 8,
+ "EMU10K1X")) == NULL) {
+ snd_printk(KERN_ERR "emu10k1x: cannot allocate the port 0x%lx\n", chip->port);
+ snd_emu10k1x_free(chip);
+ return -EBUSY;
+ }
+
+ if (request_irq(pci->irq, snd_emu10k1x_interrupt,
+ SA_INTERRUPT|SA_SHIRQ, "EMU10K1X",
+ (void *)chip)) {
+ snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq);
+ snd_emu10k1x_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ 4 * 1024, &chip->dma_buffer) < 0) {
+ snd_emu10k1x_free(chip);
+ return -ENOMEM;
+ }
+
+ pci_set_master(pci);
+ /* read revision & serial */
+ pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&chip->revision);
+ pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+ snd_printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+ chip->revision, chip->serial);
+
+ outl(0, chip->port + INTE);
+
+ for(ch = 0; ch < 3; ch++) {
+ chip->voices[ch].emu = chip;
+ chip->voices[ch].number = ch;
+ }
+
+ /*
+ * Init to 0x02109204 :
+ * Clock accuracy = 0 (1000ppm)
+ * Sample Rate = 2 (48kHz)
+ * Audio Channel = 1 (Left of 2)
+ * Source Number = 0 (Unspecified)
+ * Generation Status = 1 (Original for Cat Code 12)
+ * Cat Code = 12 (Digital Signal Mixer)
+ * Mode = 0 (Mode 0)
+ * Emphasis = 0 (None)
+ * CP = 1 (Copyright unasserted)
+ * AN = 0 (Audio data)
+ * P = 0 (Consumer)
+ */
+ snd_emu10k1x_ptr_write(chip, SPCS0, 0,
+ chip->spdif_bits[0] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ snd_emu10k1x_ptr_write(chip, SPCS1, 0,
+ chip->spdif_bits[1] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ snd_emu10k1x_ptr_write(chip, SPCS2, 0,
+ chip->spdif_bits[2] =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+ snd_emu10k1x_ptr_write(chip, SPDIF_SELECT, 0, 0x700); // disable SPDIF
+ snd_emu10k1x_ptr_write(chip, ROUTING, 0, 0x1003F); // routing
+ snd_emu10k1x_gpio_write(chip, 0x1080); // analog mode
+
+ outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+ chip, &ops)) < 0) {
+ snd_emu10k1x_free(chip);
+ return err;
+ }
+ *rchip = chip;
+ return 0;
+}
+
+static void snd_emu10k1x_proc_reg_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ emu10k1x_t *emu = entry->private_data;
+ unsigned long value,value1,value2;
+ unsigned long flags;
+ int i;
+
+ snd_iprintf(buffer, "Registers:\n\n");
+ for(i = 0; i < 0x20; i+=4) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ value = inl(emu->port + i);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
+ }
+ snd_iprintf(buffer, "\nRegisters\n\n");
+ for(i = 0; i <= 0x48; i++) {
+ value = snd_emu10k1x_ptr_read(emu, i, 0);
+ if(i < 0x10 || (i >= 0x20 && i < 0x40)) {
+ value1 = snd_emu10k1x_ptr_read(emu, i, 1);
+ value2 = snd_emu10k1x_ptr_read(emu, i, 2);
+ snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2);
+ } else {
+ snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+ }
+ }
+}
+
+static void snd_emu10k1x_proc_reg_write(snd_info_entry_t *entry,
+ snd_info_buffer_t *buffer)
+{
+ emu10k1x_t *emu = entry->private_data;
+ char line[64];
+ unsigned int reg, channel_id , val;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+ continue;
+
+ if ((reg < 0x49) && (reg >=0) && (val <= 0xffffffff)
+ && (channel_id >=0) && (channel_id <= 2) )
+ snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
+ }
+}
+
+static int __devinit snd_emu10k1x_proc_init(emu10k1x_t * emu)
+{
+ snd_info_entry_t *entry;
+
+ if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
+ snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_emu10k1x_proc_reg_write;
+ entry->private_data = emu;
+ }
+
+ return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (snd_emu10k1x_ptr_read(emu, SPDIF_SELECT, 0) == 0x700) ? 0 : 1;
+
+ return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ val = ucontrol->value.integer.value[0] ;
+
+ if (val) {
+ // enable spdif output
+ snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x000);
+ snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x700);
+ snd_emu10k1x_gpio_write(emu, 0x1000);
+ } else {
+ // disable spdif output
+ snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x700);
+ snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F);
+ snd_emu10k1x_gpio_write(emu, 0x1080);
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1x_shared_spdif __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog/Digital Output Jack",
+ .info = snd_emu10k1x_shared_spdif_info,
+ .get = snd_emu10k1x_shared_spdif_get,
+ .put = snd_emu10k1x_shared_spdif_put
+};
+
+static int snd_emu10k1x_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_emu10k1x_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+ return 0;
+}
+
+static int snd_emu10k1x_spdif_get_mask(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * 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 snd_emu10k1x_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int change;
+ unsigned int val;
+
+ val = (ucontrol->value.iec958.status[0] << 0) |
+ (ucontrol->value.iec958.status[1] << 8) |
+ (ucontrol->value.iec958.status[2] << 16) |
+ (ucontrol->value.iec958.status[3] << 24);
+ change = val != emu->spdif_bits[idx];
+ if (change) {
+ snd_emu10k1x_ptr_write(emu, SPCS0 + idx, 0, val);
+ emu->spdif_bits[idx] = val;
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1x_spdif_mask_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+ .count = 3,
+ .info = snd_emu10k1x_spdif_info,
+ .get = snd_emu10k1x_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_emu10k1x_spdif_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .count = 3,
+ .info = snd_emu10k1x_spdif_info,
+ .get = snd_emu10k1x_spdif_get,
+ .put = snd_emu10k1x_spdif_put
+};
+
+static int __devinit snd_emu10k1x_mixer(emu10k1x_t *emu)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = emu->card;
+
+ if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+
+ return 0;
+}
+
+#define EMU10K1X_MIDI_MODE_INPUT (1<<0)
+#define EMU10K1X_MIDI_MODE_OUTPUT (1<<1)
+
+static inline unsigned char mpu401_read(emu10k1x_t *emu, emu10k1x_midi_t *mpu, int idx)
+{
+ return (unsigned char)snd_emu10k1x_ptr_read(emu, mpu->port + idx, 0);
+}
+
+static inline void mpu401_write(emu10k1x_t *emu, emu10k1x_midi_t *mpu, int data, int idx)
+{
+ snd_emu10k1x_ptr_write(emu, mpu->port + idx, 0, data);
+}
+
+#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET 0xff
+#define MPU401_ENTER_UART 0x3f
+#define MPU401_ACK 0xfe
+
+static void mpu401_clear_rx(emu10k1x_t *emu, emu10k1x_midi_t *mpu)
+{
+ int timeout = 100000;
+ for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+ mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+ if (timeout <= 0)
+ snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+ */
+
+static void do_emu10k1x_midi_interrupt(emu10k1x_t *emu, emu10k1x_midi_t *midi, unsigned int status)
+{
+ unsigned char byte;
+
+ if (midi->rmidi == NULL) {
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+ return;
+ }
+
+ spin_lock(&midi->input_lock);
+ if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+ mpu401_clear_rx(emu, midi);
+ } else {
+ byte = mpu401_read_data(emu, midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
+ }
+ spin_unlock(&midi->input_lock);
+
+ spin_lock(&midi->output_lock);
+ if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ mpu401_write_data(emu, midi, byte);
+ } else {
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ }
+ }
+ spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1x_midi_interrupt(emu10k1x_t *emu, unsigned int status)
+{
+ do_emu10k1x_midi_interrupt(emu, &emu->midi, status);
+}
+
+static void snd_emu10k1x_midi_cmd(emu10k1x_t * emu, emu10k1x_midi_t *midi, unsigned char cmd, int ack)
+{
+ unsigned long flags;
+ int timeout, ok;
+
+ spin_lock_irqsave(&midi->input_lock, flags);
+ mpu401_write_data(emu, midi, 0x00);
+ /* mpu401_clear_rx(emu, midi); */
+
+ mpu401_write_cmd(emu, midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (mpu401_input_avail(emu, midi)) {
+ if (mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ }
+ }
+ if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ } else {
+ ok = 1;
+ }
+ spin_unlock_irqrestore(&midi->input_lock, flags);
+ if (!ok)
+ snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+ cmd, emu->port,
+ mpu401_read_stat(emu, midi),
+ mpu401_read_data(emu, midi));
+}
+
+static int snd_emu10k1x_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+ emu10k1x_t *emu;
+ emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1);
+ snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_emu10k1x_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+ emu10k1x_t *emu;
+ emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1);
+ snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_emu10k1x_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+ emu10k1x_t *emu;
+ emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+ midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_emu10k1x_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+ emu10k1x_t *emu;
+ emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static void snd_emu10k1x_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ emu10k1x_t *emu;
+ emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+ emu = midi->emu;
+ snd_assert(emu, return);
+
+ if (up)
+ snd_emu10k1x_intr_enable(emu, midi->rx_enable);
+ else
+ snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1x_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ emu10k1x_t *emu;
+ emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return);
+
+ if (up) {
+ int max = 4;
+ unsigned char byte;
+
+ /* try to send some amount of bytes here before interrupts */
+ spin_lock_irqsave(&midi->output_lock, flags);
+ while (max > 0) {
+ if (mpu401_output_ready(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ spin_unlock_irqrestore(&midi->output_lock, flags);
+ return;
+ }
+ mpu401_write_data(emu, midi, byte);
+ max--;
+ } else {
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&midi->output_lock, flags);
+ snd_emu10k1x_intr_enable(emu, midi->tx_enable);
+ } else {
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ }
+}
+
+/*
+
+ */
+
+static snd_rawmidi_ops_t snd_emu10k1x_midi_output =
+{
+ .open = snd_emu10k1x_midi_output_open,
+ .close = snd_emu10k1x_midi_output_close,
+ .trigger = snd_emu10k1x_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_emu10k1x_midi_input =
+{
+ .open = snd_emu10k1x_midi_input_open,
+ .close = snd_emu10k1x_midi_input_close,
+ .trigger = snd_emu10k1x_midi_input_trigger,
+};
+
+static void snd_emu10k1x_midi_free(snd_rawmidi_t *rmidi)
+{
+ emu10k1x_midi_t *midi = (emu10k1x_midi_t *)rmidi->private_data;
+ midi->interrupt = NULL;
+ midi->rmidi = NULL;
+}
+
+static int __devinit emu10k1x_midi_init(emu10k1x_t *emu, emu10k1x_midi_t *midi, int device, char *name)
+{
+ snd_rawmidi_t *rmidi;
+ int err;
+
+ if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+ return err;
+ midi->emu = emu;
+ spin_lock_init(&midi->open_lock);
+ spin_lock_init(&midi->input_lock);
+ spin_lock_init(&midi->output_lock);
+ strcpy(rmidi->name, name);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = midi;
+ rmidi->private_free = snd_emu10k1x_midi_free;
+ midi->rmidi = rmidi;
+ return 0;
+}
+
+static int __devinit snd_emu10k1x_midi(emu10k1x_t *emu)
+{
+ emu10k1x_midi_t *midi = &emu->midi;
+ int err;
+
+ if ((err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)")) < 0)
+ return err;
+
+ midi->tx_enable = INTE_MIDITXENABLE;
+ midi->rx_enable = INTE_MIDIRXENABLE;
+ midi->port = MUDATA;
+ midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+ midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+ midi->interrupt = snd_emu10k1x_midi_interrupt;
+ return 0;
+}
+
+static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ emu10k1x_t *chip;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_emu10k1x_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_emu10k1x_pcm(chip, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_emu10k1x_pcm(chip, 2, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_emu10k1x_ac97(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_emu10k1x_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_emu10k1x_midi(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_emu10k1x_proc_init(chip);
+
+ strcpy(card->driver, "EMU10K1X");
+ strcpy(card->shortname, "Dell Sound Blaster Live!");
+ sprintf(card->longname, "%s at 0x%lx irq %i",
+ card->shortname, chip->port, chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_emu10k1x_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+// PCI IDs
+static struct pci_device_id snd_emu10k1x_ids[] = {
+ { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Dell OEM version (EMU10K1) */
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
+
+// pci_driver definition
+static struct pci_driver driver = {
+ .name = "EMU10K1X",
+ .id_table = snd_emu10k1x_ids,
+ .probe = snd_emu10k1x_probe,
+ .remove = __devexit_p(snd_emu10k1x_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_emu10k1x_init(void)
+{
+ int err;
+
+ if ((err = pci_module_init(&driver)) > 0)
+ return err;
+
+ return 0;
+}
+
+// clean up the module
+static void __exit alsa_card_emu10k1x_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_emu10k1x_init)
+module_exit(alsa_card_emu10k1x_exit)
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
new file mode 100644
index 00000000000..b9fa2e887fe
--- /dev/null
+++ b/sound/pci/emu10k1/emufx.c
@@ -0,0 +1,2320 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Creative Labs, Inc.
+ * Routines for effect processor FX8010
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#if 0 /* for testing purposes - digital out -> capture */
+#define EMU10K1_CAPTURE_DIGITAL_OUT
+#endif
+#if 0 /* for testing purposes - set S/PDIF to AC3 output */
+#define EMU10K1_SET_AC3_IEC958
+#endif
+#if 0 /* for testing purposes - feed the front signal to Center/LFE outputs */
+#define EMU10K1_CENTER_LFE_FROM_FRONT
+#endif
+
+/*
+ * Tables
+ */
+
+static char *fxbuses[16] = {
+ /* 0x00 */ "PCM Left",
+ /* 0x01 */ "PCM Right",
+ /* 0x02 */ "PCM Surround Left",
+ /* 0x03 */ "PCM Surround Right",
+ /* 0x04 */ "MIDI Left",
+ /* 0x05 */ "MIDI Right",
+ /* 0x06 */ "Center",
+ /* 0x07 */ "LFE",
+ /* 0x08 */ NULL,
+ /* 0x09 */ NULL,
+ /* 0x0a */ NULL,
+ /* 0x0b */ NULL,
+ /* 0x0c */ "MIDI Reverb",
+ /* 0x0d */ "MIDI Chorus",
+ /* 0x0e */ NULL,
+ /* 0x0f */ NULL
+};
+
+static char *creative_ins[16] = {
+ /* 0x00 */ "AC97 Left",
+ /* 0x01 */ "AC97 Right",
+ /* 0x02 */ "TTL IEC958 Left",
+ /* 0x03 */ "TTL IEC958 Right",
+ /* 0x04 */ "Zoom Video Left",
+ /* 0x05 */ "Zoom Video Right",
+ /* 0x06 */ "Optical IEC958 Left",
+ /* 0x07 */ "Optical IEC958 Right",
+ /* 0x08 */ "Line/Mic 1 Left",
+ /* 0x09 */ "Line/Mic 1 Right",
+ /* 0x0a */ "Coaxial IEC958 Left",
+ /* 0x0b */ "Coaxial IEC958 Right",
+ /* 0x0c */ "Line/Mic 2 Left",
+ /* 0x0d */ "Line/Mic 2 Right",
+ /* 0x0e */ NULL,
+ /* 0x0f */ NULL
+};
+
+static char *audigy_ins[16] = {
+ /* 0x00 */ "AC97 Left",
+ /* 0x01 */ "AC97 Right",
+ /* 0x02 */ "Audigy CD Left",
+ /* 0x03 */ "Audigy CD Right",
+ /* 0x04 */ "Optical IEC958 Left",
+ /* 0x05 */ "Optical IEC958 Right",
+ /* 0x06 */ NULL,
+ /* 0x07 */ NULL,
+ /* 0x08 */ "Line/Mic 2 Left",
+ /* 0x09 */ "Line/Mic 2 Right",
+ /* 0x0a */ "SPDIF Left",
+ /* 0x0b */ "SPDIF Right",
+ /* 0x0c */ "Aux2 Left",
+ /* 0x0d */ "Aux2 Right",
+ /* 0x0e */ NULL,
+ /* 0x0f */ NULL
+};
+
+static char *creative_outs[32] = {
+ /* 0x00 */ "AC97 Left",
+ /* 0x01 */ "AC97 Right",
+ /* 0x02 */ "Optical IEC958 Left",
+ /* 0x03 */ "Optical IEC958 Right",
+ /* 0x04 */ "Center",
+ /* 0x05 */ "LFE",
+ /* 0x06 */ "Headphone Left",
+ /* 0x07 */ "Headphone Right",
+ /* 0x08 */ "Surround Left",
+ /* 0x09 */ "Surround Right",
+ /* 0x0a */ "PCM Capture Left",
+ /* 0x0b */ "PCM Capture Right",
+ /* 0x0c */ "MIC Capture",
+ /* 0x0d */ "AC97 Surround Left",
+ /* 0x0e */ "AC97 Surround Right",
+ /* 0x0f */ NULL,
+ /* 0x10 */ NULL,
+ /* 0x11 */ "Analog Center",
+ /* 0x12 */ "Analog LFE",
+ /* 0x13 */ NULL,
+ /* 0x14 */ NULL,
+ /* 0x15 */ NULL,
+ /* 0x16 */ NULL,
+ /* 0x17 */ NULL,
+ /* 0x18 */ NULL,
+ /* 0x19 */ NULL,
+ /* 0x1a */ NULL,
+ /* 0x1b */ NULL,
+ /* 0x1c */ NULL,
+ /* 0x1d */ NULL,
+ /* 0x1e */ NULL,
+ /* 0x1f */ NULL,
+};
+
+static char *audigy_outs[32] = {
+ /* 0x00 */ "Digital Front Left",
+ /* 0x01 */ "Digital Front Right",
+ /* 0x02 */ "Digital Center",
+ /* 0x03 */ "Digital LEF",
+ /* 0x04 */ "Headphone Left",
+ /* 0x05 */ "Headphone Right",
+ /* 0x06 */ "Digital Rear Left",
+ /* 0x07 */ "Digital Rear Right",
+ /* 0x08 */ "Front Left",
+ /* 0x09 */ "Front Right",
+ /* 0x0a */ "Center",
+ /* 0x0b */ "LFE",
+ /* 0x0c */ NULL,
+ /* 0x0d */ NULL,
+ /* 0x0e */ "Rear Left",
+ /* 0x0f */ "Rear Right",
+ /* 0x10 */ "AC97 Front Left",
+ /* 0x11 */ "AC97 Front Right",
+ /* 0x12 */ "ADC Caputre Left",
+ /* 0x13 */ "ADC Capture Right",
+ /* 0x14 */ NULL,
+ /* 0x15 */ NULL,
+ /* 0x16 */ NULL,
+ /* 0x17 */ NULL,
+ /* 0x18 */ NULL,
+ /* 0x19 */ NULL,
+ /* 0x1a */ NULL,
+ /* 0x1b */ NULL,
+ /* 0x1c */ NULL,
+ /* 0x1d */ NULL,
+ /* 0x1e */ NULL,
+ /* 0x1f */ NULL,
+};
+
+static const u32 bass_table[41][5] = {
+ { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
+ { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
+ { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
+ { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c },
+ { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b },
+ { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 },
+ { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f },
+ { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 },
+ { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 },
+ { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 },
+ { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 },
+ { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be },
+ { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b },
+ { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c },
+ { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b },
+ { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 },
+ { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a },
+ { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 },
+ { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 },
+ { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e },
+ { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee },
+ { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 },
+ { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 },
+ { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 },
+ { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 },
+ { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e },
+ { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 },
+ { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 },
+ { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 },
+ { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 },
+ { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 },
+ { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca },
+ { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 },
+ { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 },
+ { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 },
+ { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 },
+ { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a },
+ { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f },
+ { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 },
+ { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 },
+ { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
+};
+
+static const u32 treble_table[41][5] = {
+ { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
+ { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
+ { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
+ { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca },
+ { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 },
+ { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 },
+ { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 },
+ { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 },
+ { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 },
+ { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df },
+ { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff },
+ { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 },
+ { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c },
+ { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 },
+ { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 },
+ { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 },
+ { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 },
+ { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d },
+ { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 },
+ { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 },
+ { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f },
+ { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb },
+ { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 },
+ { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 },
+ { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd },
+ { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 },
+ { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad },
+ { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 },
+ { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 },
+ { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c },
+ { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 },
+ { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 },
+ { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 },
+ { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 },
+ { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 },
+ { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 },
+ { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d },
+ { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b },
+ { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 },
+ { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd },
+ { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
+};
+
+static const u32 db_table[101] = {
+ 0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
+ 0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
+ 0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1,
+ 0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0,
+ 0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9,
+ 0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb,
+ 0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005,
+ 0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d,
+ 0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd,
+ 0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8,
+ 0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481,
+ 0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333,
+ 0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d,
+ 0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6,
+ 0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d,
+ 0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf,
+ 0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038,
+ 0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a,
+ 0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea,
+ 0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272,
+ 0x7fffffff,
+};
+
+static const u32 onoff_table[2] = {
+ 0x00000000, 0x00000001
+};
+
+/*
+ */
+
+static inline mm_segment_t snd_enter_user(void)
+{
+ mm_segment_t fs = get_fs();
+ set_fs(get_ds());
+ return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+ set_fs(fs);
+}
+
+/*
+ * controls
+ */
+
+static int snd_emu10k1_gpr_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value;
+
+ if (ctl->min == 0 && ctl->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = ctl->vcount;
+ uinfo->value.integer.min = ctl->min;
+ uinfo->value.integer.max = ctl->max;
+ return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (i = 0; i < ctl->vcount; i++)
+ ucontrol->value.integer.value[i] = ctl->value[i];
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value;
+ unsigned long flags;
+ unsigned int nval, val;
+ unsigned int i, j;
+ int change = 0;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (i = 0; i < ctl->vcount; i++) {
+ nval = ucontrol->value.integer.value[i];
+ if (nval < ctl->min)
+ nval = ctl->min;
+ if (nval > ctl->max)
+ nval = ctl->max;
+ if (nval != ctl->value[i])
+ change = 1;
+ val = ctl->value[i] = nval;
+ switch (ctl->translation) {
+ case EMU10K1_GPR_TRANSLATION_NONE:
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
+ break;
+ case EMU10K1_GPR_TRANSLATION_TABLE100:
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
+ break;
+ case EMU10K1_GPR_TRANSLATION_BASS:
+ snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error);
+ for (j = 0; j < 5; j++)
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]);
+ break;
+ case EMU10K1_GPR_TRANSLATION_TREBLE:
+ snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error);
+ for (j = 0; j < 5; j++)
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]);
+ break;
+ case EMU10K1_GPR_TRANSLATION_ONOFF:
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]);
+ break;
+ }
+ }
+ __error:
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+/*
+ * Interrupt handler
+ */
+
+static void snd_emu10k1_fx8010_interrupt(emu10k1_t *emu)
+{
+ snd_emu10k1_fx8010_irq_t *irq, *nirq;
+
+ irq = emu->fx8010.irq_handlers;
+ while (irq) {
+ nirq = irq->next; /* irq ptr can be removed from list */
+ if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) {
+ if (irq->handler)
+ irq->handler(emu, irq->private_data);
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1);
+ }
+ irq = nirq;
+ }
+}
+
+int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu,
+ snd_fx8010_irq_handler_t *handler,
+ unsigned char gpr_running,
+ void *private_data,
+ snd_emu10k1_fx8010_irq_t **r_irq)
+{
+ snd_emu10k1_fx8010_irq_t *irq;
+ unsigned long flags;
+
+ snd_runtime_check(emu, return -EINVAL);
+ snd_runtime_check(handler, return -EINVAL);
+ irq = kmalloc(sizeof(*irq), GFP_ATOMIC);
+ if (irq == NULL)
+ return -ENOMEM;
+ irq->handler = handler;
+ irq->gpr_running = gpr_running;
+ irq->private_data = private_data;
+ irq->next = NULL;
+ spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+ if (emu->fx8010.irq_handlers == NULL) {
+ emu->fx8010.irq_handlers = irq;
+ emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt;
+ snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE);
+ } else {
+ irq->next = emu->fx8010.irq_handlers;
+ emu->fx8010.irq_handlers = irq;
+ }
+ spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+ if (r_irq)
+ *r_irq = irq;
+ return 0;
+}
+
+int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu,
+ snd_emu10k1_fx8010_irq_t *irq)
+{
+ snd_emu10k1_fx8010_irq_t *tmp;
+ unsigned long flags;
+
+ snd_runtime_check(irq, return -EINVAL);
+ spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+ if ((tmp = emu->fx8010.irq_handlers) == irq) {
+ emu->fx8010.irq_handlers = tmp->next;
+ if (emu->fx8010.irq_handlers == NULL) {
+ snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+ emu->dsp_interrupt = NULL;
+ }
+ } else {
+ while (tmp && tmp->next != irq)
+ tmp = tmp->next;
+ if (tmp)
+ tmp->next = tmp->next->next;
+ }
+ spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+ kfree(irq);
+ return 0;
+}
+
+/*************************************************************************
+ * EMU10K1 effect manager
+ *************************************************************************/
+
+static void snd_emu10k1_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr,
+ u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+ u_int32_t *code;
+ snd_assert(*ptr < 512, return);
+ code = (u_int32_t *)icode->code + (*ptr) * 2;
+ set_bit(*ptr, icode->code_valid);
+ code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
+ code[1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff);
+ (*ptr)++;
+}
+
+#define OP(icode, ptr, op, r, a, x, y) \
+ snd_emu10k1_write_op(icode, ptr, op, r, a, x, y)
+
+static void snd_emu10k1_audigy_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr,
+ u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+ u_int32_t *code;
+ snd_assert(*ptr < 1024, return);
+ code = (u_int32_t *)icode->code + (*ptr) * 2;
+ set_bit(*ptr, icode->code_valid);
+ code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
+ code[1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff);
+ (*ptr)++;
+}
+
+#define A_OP(icode, ptr, op, r, a, x, y) \
+ snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y)
+
+static void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data)
+{
+ pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+ snd_emu10k1_ptr_write(emu, pc, 0, data);
+}
+
+unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc)
+{
+ pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+ return snd_emu10k1_ptr_read(emu, pc, 0);
+}
+
+static int snd_emu10k1_gpr_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ int gpr;
+ u32 val;
+
+ for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
+ if (!test_bit(gpr, icode->gpr_valid))
+ continue;
+ if (get_user(val, &icode->gpr_map[gpr]))
+ return -EFAULT;
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, val);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_gpr_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ int gpr;
+ u32 val;
+
+ for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
+ set_bit(gpr, icode->gpr_valid);
+ val = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0);
+ if (put_user(val, &icode->gpr_map[gpr]))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int snd_emu10k1_tram_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ int tram;
+ u32 addr, val;
+
+ for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
+ if (!test_bit(tram, icode->tram_valid))
+ continue;
+ if (get_user(val, &icode->tram_data_map[tram]) ||
+ get_user(addr, &icode->tram_addr_map[tram]))
+ return -EFAULT;
+ snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, val);
+ if (!emu->audigy) {
+ snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr);
+ } else {
+ snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr << 12);
+ snd_emu10k1_ptr_write(emu, A_TANKMEMCTLREGBASE + tram, 0, addr >> 20);
+ }
+ }
+ return 0;
+}
+
+static int snd_emu10k1_tram_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ int tram;
+ u32 val, addr;
+
+ memset(icode->tram_valid, 0, sizeof(icode->tram_valid));
+ for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
+ set_bit(tram, icode->tram_valid);
+ val = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0);
+ if (!emu->audigy) {
+ addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0);
+ } else {
+ addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0) >> 12;
+ addr |= snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + tram, 0) << 20;
+ }
+ if (put_user(val, &icode->tram_data_map[tram]) ||
+ put_user(addr, &icode->tram_addr_map[tram]))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int snd_emu10k1_code_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ u32 pc, lo, hi;
+
+ for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
+ if (!test_bit(pc / 2, icode->code_valid))
+ continue;
+ if (get_user(lo, &icode->code[pc + 0]) ||
+ get_user(hi, &icode->code[pc + 1]))
+ return -EFAULT;
+ snd_emu10k1_efx_write(emu, pc + 0, lo);
+ snd_emu10k1_efx_write(emu, pc + 1, hi);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_code_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ u32 pc;
+
+ memset(icode->code_valid, 0, sizeof(icode->code_valid));
+ for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
+ set_bit(pc / 2, icode->code_valid);
+ if (put_user(snd_emu10k1_efx_read(emu, pc + 0), &icode->code[pc + 0]))
+ return -EFAULT;
+ if (put_user(snd_emu10k1_efx_read(emu, pc + 1), &icode->code[pc + 1]))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static snd_emu10k1_fx8010_ctl_t *snd_emu10k1_look_for_ctl(emu10k1_t *emu, snd_ctl_elem_id_t *id)
+{
+ snd_emu10k1_fx8010_ctl_t *ctl;
+ snd_kcontrol_t *kcontrol;
+ struct list_head *list;
+
+ list_for_each(list, &emu->fx8010.gpr_ctl) {
+ ctl = emu10k1_gpr_ctl(list);
+ kcontrol = ctl->kcontrol;
+ if (kcontrol->id.iface == id->iface &&
+ !strcmp(kcontrol->id.name, id->name) &&
+ kcontrol->id.index == id->index)
+ return ctl;
+ }
+ return NULL;
+}
+
+static int snd_emu10k1_verify_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ unsigned int i;
+ snd_ctl_elem_id_t __user *_id;
+ snd_ctl_elem_id_t id;
+ emu10k1_fx8010_control_gpr_t __user *_gctl;
+ emu10k1_fx8010_control_gpr_t *gctl;
+ int err;
+
+ for (i = 0, _id = icode->gpr_del_controls;
+ i < icode->gpr_del_control_count; i++, _id++) {
+ if (copy_from_user(&id, _id, sizeof(id)))
+ return -EFAULT;
+ if (snd_emu10k1_look_for_ctl(emu, &id) == NULL)
+ return -ENOENT;
+ }
+ gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+ if (! gctl)
+ return -ENOMEM;
+ err = 0;
+ for (i = 0, _gctl = icode->gpr_add_controls;
+ i < icode->gpr_add_control_count; i++, _gctl++) {
+ if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+ err = -EFAULT;
+ goto __error;
+ }
+ if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
+ continue;
+ down_read(&emu->card->controls_rwsem);
+ if (snd_ctl_find_id(emu->card, &gctl->id) != NULL) {
+ up_read(&emu->card->controls_rwsem);
+ err = -EEXIST;
+ goto __error;
+ }
+ up_read(&emu->card->controls_rwsem);
+ if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+ gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+ err = -EINVAL;
+ goto __error;
+ }
+ }
+ for (i = 0, _gctl = icode->gpr_list_controls;
+ i < icode->gpr_list_control_count; i++, _gctl++) {
+ /* FIXME: we need to check the WRITE access */
+ if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+ err = -EFAULT;
+ goto __error;
+ }
+ }
+ __error:
+ kfree(gctl);
+ return err;
+}
+
+static void snd_emu10k1_ctl_private_free(snd_kcontrol_t *kctl)
+{
+ snd_emu10k1_fx8010_ctl_t *ctl;
+
+ ctl = (snd_emu10k1_fx8010_ctl_t *)kctl->private_value;
+ kctl->private_value = 0;
+ list_del(&ctl->list);
+ kfree(ctl);
+}
+
+static int snd_emu10k1_add_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ unsigned int i, j;
+ emu10k1_fx8010_control_gpr_t __user *_gctl;
+ emu10k1_fx8010_control_gpr_t *gctl;
+ snd_emu10k1_fx8010_ctl_t *ctl, *nctl;
+ snd_kcontrol_new_t knew;
+ snd_kcontrol_t *kctl;
+ snd_ctl_elem_value_t *val;
+ int err = 0;
+
+ val = (snd_ctl_elem_value_t *)kmalloc(sizeof(*val), GFP_KERNEL);
+ gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+ nctl = kmalloc(sizeof(*nctl), GFP_KERNEL);
+ if (!val || !gctl || !nctl) {
+ err = -ENOMEM;
+ goto __error;
+ }
+
+ for (i = 0, _gctl = icode->gpr_add_controls;
+ i < icode->gpr_add_control_count; i++, _gctl++) {
+ if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+ err = -EFAULT;
+ goto __error;
+ }
+ snd_runtime_check(gctl->id.iface == SNDRV_CTL_ELEM_IFACE_MIXER ||
+ gctl->id.iface == SNDRV_CTL_ELEM_IFACE_PCM, err = -EINVAL; goto __error);
+ snd_runtime_check(gctl->id.name[0] != '\0', err = -EINVAL; goto __error);
+ ctl = snd_emu10k1_look_for_ctl(emu, &gctl->id);
+ memset(&knew, 0, sizeof(knew));
+ knew.iface = gctl->id.iface;
+ knew.name = gctl->id.name;
+ knew.index = gctl->id.index;
+ knew.device = gctl->id.device;
+ knew.subdevice = gctl->id.subdevice;
+ knew.info = snd_emu10k1_gpr_ctl_info;
+ knew.get = snd_emu10k1_gpr_ctl_get;
+ knew.put = snd_emu10k1_gpr_ctl_put;
+ memset(nctl, 0, sizeof(*nctl));
+ nctl->vcount = gctl->vcount;
+ nctl->count = gctl->count;
+ for (j = 0; j < 32; j++) {
+ nctl->gpr[j] = gctl->gpr[j];
+ nctl->value[j] = ~gctl->value[j]; /* inverted, we want to write new value in gpr_ctl_put() */
+ val->value.integer.value[j] = gctl->value[j];
+ }
+ nctl->min = gctl->min;
+ nctl->max = gctl->max;
+ nctl->translation = gctl->translation;
+ if (ctl == NULL) {
+ ctl = (snd_emu10k1_fx8010_ctl_t *)kmalloc(sizeof(*ctl), GFP_KERNEL);
+ if (ctl == NULL) {
+ err = -ENOMEM;
+ goto __error;
+ }
+ knew.private_value = (unsigned long)ctl;
+ *ctl = *nctl;
+ if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
+ kfree(ctl);
+ goto __error;
+ }
+ kctl->private_free = snd_emu10k1_ctl_private_free;
+ ctl->kcontrol = kctl;
+ list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl);
+ } else {
+ /* overwrite */
+ nctl->list = ctl->list;
+ nctl->kcontrol = ctl->kcontrol;
+ *ctl = *nctl;
+ snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id);
+ }
+ snd_emu10k1_gpr_ctl_put(ctl->kcontrol, val);
+ }
+ __error:
+ kfree(nctl);
+ kfree(gctl);
+ kfree(val);
+ return err;
+}
+
+static int snd_emu10k1_del_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ unsigned int i;
+ snd_ctl_elem_id_t id;
+ snd_ctl_elem_id_t __user *_id;
+ snd_emu10k1_fx8010_ctl_t *ctl;
+ snd_card_t *card = emu->card;
+
+ for (i = 0, _id = icode->gpr_del_controls;
+ i < icode->gpr_del_control_count; i++, _id++) {
+ snd_runtime_check(copy_from_user(&id, _id, sizeof(id)) == 0, return -EFAULT);
+ down_write(&card->controls_rwsem);
+ ctl = snd_emu10k1_look_for_ctl(emu, &id);
+ if (ctl)
+ snd_ctl_remove(card, ctl->kcontrol);
+ up_write(&card->controls_rwsem);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_list_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ unsigned int i = 0, j;
+ unsigned int total = 0;
+ emu10k1_fx8010_control_gpr_t *gctl;
+ emu10k1_fx8010_control_gpr_t __user *_gctl;
+ snd_emu10k1_fx8010_ctl_t *ctl;
+ snd_ctl_elem_id_t *id;
+ struct list_head *list;
+
+ gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+ if (! gctl)
+ return -ENOMEM;
+
+ _gctl = icode->gpr_list_controls;
+ list_for_each(list, &emu->fx8010.gpr_ctl) {
+ ctl = emu10k1_gpr_ctl(list);
+ total++;
+ if (_gctl && i < icode->gpr_list_control_count) {
+ memset(gctl, 0, sizeof(*gctl));
+ id = &ctl->kcontrol->id;
+ gctl->id.iface = id->iface;
+ strlcpy(gctl->id.name, id->name, sizeof(gctl->id.name));
+ gctl->id.index = id->index;
+ gctl->id.device = id->device;
+ gctl->id.subdevice = id->subdevice;
+ gctl->vcount = ctl->vcount;
+ gctl->count = ctl->count;
+ for (j = 0; j < 32; j++) {
+ gctl->gpr[j] = ctl->gpr[j];
+ gctl->value[j] = ctl->value[j];
+ }
+ gctl->min = ctl->min;
+ gctl->max = ctl->max;
+ gctl->translation = ctl->translation;
+ if (copy_to_user(_gctl, gctl, sizeof(*gctl))) {
+ kfree(gctl);
+ return -EFAULT;
+ }
+ _gctl++;
+ i++;
+ }
+ }
+ icode->gpr_list_control_total = total;
+ kfree(gctl);
+ return 0;
+}
+
+static int snd_emu10k1_icode_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ int err = 0;
+
+ down(&emu->fx8010.lock);
+ if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0)
+ goto __error;
+ strlcpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
+ /* stop FX processor - this may be dangerous, but it's better to miss
+ some samples than generate wrong ones - [jk] */
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
+ /* ok, do the main job */
+ if ((err = snd_emu10k1_del_controls(emu, icode)) < 0 ||
+ (err = snd_emu10k1_gpr_poke(emu, icode)) < 0 ||
+ (err = snd_emu10k1_tram_poke(emu, icode)) < 0 ||
+ (err = snd_emu10k1_code_poke(emu, icode)) < 0 ||
+ (err = snd_emu10k1_add_controls(emu, icode)) < 0)
+ goto __error;
+ /* start FX processor when the DSP code is updated */
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+ __error:
+ up(&emu->fx8010.lock);
+ return err;
+}
+
+static int snd_emu10k1_icode_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+ int err;
+
+ down(&emu->fx8010.lock);
+ strlcpy(icode->name, emu->fx8010.name, sizeof(icode->name));
+ /* ok, do the main job */
+ err = snd_emu10k1_gpr_peek(emu, icode);
+ if (err >= 0)
+ err = snd_emu10k1_tram_peek(emu, icode);
+ if (err >= 0)
+ err = snd_emu10k1_code_peek(emu, icode);
+ if (err >= 0)
+ err = snd_emu10k1_list_controls(emu, icode);
+ up(&emu->fx8010.lock);
+ return err;
+}
+
+static int snd_emu10k1_ipcm_poke(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm)
+{
+ unsigned int i;
+ int err = 0;
+ snd_emu10k1_fx8010_pcm_t *pcm;
+
+ if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+ return -EINVAL;
+ if (ipcm->channels > 32)
+ return -EINVAL;
+ pcm = &emu->fx8010.pcm[ipcm->substream];
+ down(&emu->fx8010.lock);
+ spin_lock_irq(&emu->reg_lock);
+ if (pcm->opened) {
+ err = -EBUSY;
+ goto __error;
+ }
+ if (ipcm->channels == 0) { /* remove */
+ pcm->valid = 0;
+ } else {
+ /* FIXME: we need to add universal code to the PCM transfer routine */
+ if (ipcm->channels != 2) {
+ err = -EINVAL;
+ goto __error;
+ }
+ pcm->valid = 1;
+ pcm->opened = 0;
+ pcm->channels = ipcm->channels;
+ pcm->tram_start = ipcm->tram_start;
+ pcm->buffer_size = ipcm->buffer_size;
+ pcm->gpr_size = ipcm->gpr_size;
+ pcm->gpr_count = ipcm->gpr_count;
+ pcm->gpr_tmpcount = ipcm->gpr_tmpcount;
+ pcm->gpr_ptr = ipcm->gpr_ptr;
+ pcm->gpr_trigger = ipcm->gpr_trigger;
+ pcm->gpr_running = ipcm->gpr_running;
+ for (i = 0; i < pcm->channels; i++)
+ pcm->etram[i] = ipcm->etram[i];
+ }
+ __error:
+ spin_unlock_irq(&emu->reg_lock);
+ up(&emu->fx8010.lock);
+ return err;
+}
+
+static int snd_emu10k1_ipcm_peek(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm)
+{
+ unsigned int i;
+ int err = 0;
+ snd_emu10k1_fx8010_pcm_t *pcm;
+
+ if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+ return -EINVAL;
+ pcm = &emu->fx8010.pcm[ipcm->substream];
+ down(&emu->fx8010.lock);
+ spin_lock_irq(&emu->reg_lock);
+ ipcm->channels = pcm->channels;
+ ipcm->tram_start = pcm->tram_start;
+ ipcm->buffer_size = pcm->buffer_size;
+ ipcm->gpr_size = pcm->gpr_size;
+ ipcm->gpr_ptr = pcm->gpr_ptr;
+ ipcm->gpr_count = pcm->gpr_count;
+ ipcm->gpr_tmpcount = pcm->gpr_tmpcount;
+ ipcm->gpr_trigger = pcm->gpr_trigger;
+ ipcm->gpr_running = pcm->gpr_running;
+ for (i = 0; i < pcm->channels; i++)
+ ipcm->etram[i] = pcm->etram[i];
+ ipcm->res1 = ipcm->res2 = 0;
+ ipcm->pad = 0;
+ spin_unlock_irq(&emu->reg_lock);
+ up(&emu->fx8010.lock);
+ return err;
+}
+
+#define SND_EMU10K1_GPR_CONTROLS 41
+#define SND_EMU10K1_INPUTS 10
+#define SND_EMU10K1_PLAYBACK_CHANNELS 8
+#define SND_EMU10K1_CAPTURE_CHANNELS 4
+
+static void __devinit snd_emu10k1_init_mono_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, name);
+ ctl->vcount = ctl->count = 1;
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+ ctl->min = 0;
+ ctl->max = 100;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+}
+
+static void __devinit snd_emu10k1_init_stereo_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, name);
+ ctl->vcount = ctl->count = 2;
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+ ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+ ctl->min = 0;
+ ctl->max = 100;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+}
+
+static void __devinit snd_emu10k1_init_mono_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, name);
+ ctl->vcount = ctl->count = 1;
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+ ctl->min = 0;
+ ctl->max = 1;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+static void __devinit snd_emu10k1_init_stereo_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, name);
+ ctl->vcount = ctl->count = 2;
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+ ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+ ctl->min = 0;
+ ctl->max = 1;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+
+/*
+ * initial DSP configuration for Audigy
+ */
+
+static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu)
+{
+ int err, i, z, gpr, nctl;
+ const int playback = 10;
+ const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
+ const int stereo_mix = capture + 2;
+ const int tmp = 0x88;
+ u32 ptr;
+ emu10k1_fx8010_code_t *icode = NULL;
+ emu10k1_fx8010_control_gpr_t *controls = NULL, *ctl;
+ u32 *gpr_map;
+ mm_segment_t seg;
+
+ spin_lock_init(&emu->fx8010.irq_lock);
+ INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
+
+ if ((icode = kcalloc(1, sizeof(*icode), GFP_KERNEL)) == NULL ||
+ (icode->gpr_map = (u_int32_t __user *)kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t), GFP_KERNEL)) == NULL ||
+ (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, sizeof(*controls), GFP_KERNEL)) == NULL) {
+ err = -ENOMEM;
+ goto __err;
+ }
+ gpr_map = (u32 *)icode->gpr_map;
+
+ icode->tram_data_map = icode->gpr_map + 512;
+ icode->tram_addr_map = icode->tram_data_map + 256;
+ icode->code = icode->tram_addr_map + 256;
+
+ /* clear free GPRs */
+ for (i = 0; i < 512; i++)
+ set_bit(i, icode->gpr_valid);
+
+ /* clear TRAM data & address lines */
+ for (i = 0; i < 256; i++)
+ set_bit(i, icode->tram_valid);
+
+ strcpy(icode->name, "Audigy DSP code for ALSA");
+ ptr = 0;
+ nctl = 0;
+ gpr = stereo_mix + 10;
+
+ /* stop FX processor */
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
+
+ /* PCM front Playback Volume (independent from stereo mix) */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* PCM Surround Playback (independent from stereo mix) */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* PCM Side Playback (independent from stereo mix) */
+ if (emu->spk71) {
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
+ gpr += 2;
+ }
+
+ /* PCM Center Playback (independent from stereo mix) */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
+ snd_emu10k1_init_mono_control(&controls[nctl++], "PCM Center Playback Volume", gpr, 100);
+ gpr++;
+
+ /* PCM LFE Playback (independent from stereo mix) */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
+ snd_emu10k1_init_mono_control(&controls[nctl++], "PCM LFE Playback Volume", gpr, 100);
+ gpr++;
+
+ /*
+ * Stereo Mix
+ */
+ /* Wave (PCM) Playback Volume (will be renamed later) */
+ A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+ A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* Synth Playback */
+ A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+ A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* Wave (PCM) Capture */
+ A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+ A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Capture Volume", gpr, 0);
+ gpr += 2;
+
+ /* Synth Capture */
+ A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+ A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
+ gpr += 2;
+
+ /*
+ * inputs
+ */
+#define A_ADD_VOLUME_IN(var,vol,input) \
+A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+
+ /* AC'97 Playback Volume - used only for mic (renamed later) */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
+ gpr += 2;
+ /* AC'97 Capture Volume - used only for mic */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
+ gpr += 2;
+
+ /* mic capture buffer */
+ A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), 0xcd, A_EXTIN(A_EXTIN_AC97_R));
+
+ /* Audigy CD Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->no_ac97 ? "CD Playback Volume" : "Audigy CD Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Audigy CD Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->no_ac97 ? "CD Capture Volume" : "Audigy CD Capture Volume",
+ gpr, 0);
+ gpr += 2;
+
+ /* Optical SPDIF Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "IEC958 Optical Playback Volume", gpr, 0);
+ gpr += 2;
+ /* Optical SPDIF Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "IEC958 Optical Capture Volume", gpr, 0);
+ gpr += 2;
+
+ /* Line2 Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->no_ac97 ? "Line Playback Volume" : "Line2 Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Line2 Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->no_ac97 ? "Line Capture Volume" : "Line2 Capture Volume",
+ gpr, 0);
+ gpr += 2;
+
+ /* Philips ADC Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
+ gpr += 2;
+ /* Philips ADC Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
+ gpr += 2;
+
+ /* Aux2 Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->no_ac97 ? "Aux Playback Volume" : "Aux2 Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Aux2 Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->no_ac97 ? "Aux Capture Volume" : "Aux2 Capture Volume",
+ gpr, 0);
+ gpr += 2;
+
+ /* Stereo Mix Front Playback Volume */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Front Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* Stereo Mix Surround Playback */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* Stereo Mix Center Playback */
+ /* Center = sub = Left/2 + Right/2 */
+ A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), 0xcd, A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
+ snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0);
+ gpr++;
+
+ /* Stereo Mix LFE Playback */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
+ snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
+ gpr++;
+
+ if (emu->spk71) {
+ /* Stereo Mix Side Playback */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0);
+ gpr += 2;
+ }
+
+ /*
+ * outputs
+ */
+#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src))
+#define A_PUT_STEREO_OUTPUT(out1,out2,src) \
+ {A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);}
+
+#define _A_SWITCH(icode, ptr, dst, src, sw) \
+ A_OP((icode), ptr, iMACINT0, dst, A_C_00000000, src, sw);
+#define A_SWITCH(icode, ptr, dst, src, sw) \
+ _A_SWITCH(icode, ptr, A_GPR(dst), A_GPR(src), A_GPR(sw))
+#define _A_SWITCH_NEG(icode, ptr, dst, src) \
+ A_OP((icode), ptr, iANDXOR, dst, src, A_C_00000001, A_C_00000001);
+#define A_SWITCH_NEG(icode, ptr, dst, src) \
+ _A_SWITCH_NEG(icode, ptr, A_GPR(dst), A_GPR(src))
+
+
+ /*
+ * Process tone control
+ */
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), A_GPR(playback + 0), A_C_00000000, A_C_00000000); /* left */
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), A_GPR(playback + 1), A_C_00000000, A_C_00000000); /* right */
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), A_GPR(playback + 2), A_C_00000000, A_C_00000000); /* rear left */
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */
+ if (emu->spk71) {
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */
+ }
+
+
+ ctl = &controls[nctl + 0];
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, "Tone Control - Bass");
+ ctl->vcount = 2;
+ ctl->count = 10;
+ ctl->min = 0;
+ ctl->max = 40;
+ ctl->value[0] = ctl->value[1] = 20;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
+ ctl = &controls[nctl + 1];
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, "Tone Control - Treble");
+ ctl->vcount = 2;
+ ctl->count = 10;
+ ctl->min = 0;
+ ctl->max = 40;
+ ctl->value[0] = ctl->value[1] = 20;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
+
+#define BASS_GPR 0x8c
+#define TREBLE_GPR 0x96
+
+ for (z = 0; z < 5; z++) {
+ int j;
+ for (j = 0; j < 2; j++) {
+ controls[nctl + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
+ controls[nctl + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+ }
+ }
+ for (z = 0; z < 4; z++) { /* front/rear/center-lfe/side */
+ int j, k, l, d;
+ for (j = 0; j < 2; j++) { /* left/right */
+ k = 0xb0 + (z * 8) + (j * 4);
+ l = 0xe0 + (z * 8) + (j * 4);
+ d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+
+ A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(BASS_GPR + 0 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(BASS_GPR + 4 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(BASS_GPR + 2 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(BASS_GPR + 8 + j));
+ A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(BASS_GPR + 6 + j));
+ A_OP(icode, &ptr, iACC3, A_GPR(k+2), A_GPR(k+2), A_GPR(k+2), A_C_00000000);
+
+ A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(TREBLE_GPR + 0 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(TREBLE_GPR + 4 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(TREBLE_GPR + 2 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(TREBLE_GPR + 8 + j));
+ A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(TREBLE_GPR + 6 + j));
+ A_OP(icode, &ptr, iMACINT0, A_GPR(l+2), A_C_00000000, A_GPR(l+2), A_C_00000010);
+
+ A_OP(icode, &ptr, iACC3, A_GPR(d), A_GPR(l+2), A_C_00000000, A_C_00000000);
+
+ if (z == 2) /* center */
+ break;
+ }
+ }
+ nctl += 2;
+
+#undef BASS_GPR
+#undef TREBLE_GPR
+
+ for (z = 0; z < 8; z++) {
+ A_SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
+ A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
+ A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ }
+ snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
+ gpr += 2;
+
+ /* Master volume (will be renamed later) */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS));
+ snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* analog speakers */
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
+ A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
+ A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
+ if (emu->spk71)
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+ /* headphone */
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+ /* digital outputs */
+ /* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
+
+ /* IEC958 Optical Raw Playback Switch */
+ gpr_map[gpr++] = 0;
+ gpr_map[gpr++] = 0x1008;
+ gpr_map[gpr++] = 0xffff0000;
+ for (z = 0; z < 2; z++) {
+ A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
+ A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
+ A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
+ A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
+ A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+ A_SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+ if ((z==1) && (emu->card_capabilities->spdif_bug)) {
+ /* Due to a SPDIF output bug on some Audigy cards, this code delays the Right channel by 1 sample */
+ snd_printk("Installing spdif_bug patch: %s\n", emu->card_capabilities->name);
+ A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(gpr - 3), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 3), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ } else {
+ A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ }
+ }
+ snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "IEC958 Optical Raw Playback Switch", gpr, 0);
+ gpr += 2;
+
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
+ A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
+ A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+ /* ADC buffer */
+#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+#else
+ A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture);
+ A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
+#endif
+
+ /* EFX capture - capture the 16 EXTINs */
+ for (z = 0; z < 16; z++) {
+ A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z));
+ }
+
+ /*
+ * ok, set up done..
+ */
+
+ if (gpr > tmp) {
+ snd_BUG();
+ err = -EIO;
+ goto __err;
+ }
+ /* clear remaining instruction memory */
+ while (ptr < 0x400)
+ A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
+
+ seg = snd_enter_user();
+ icode->gpr_add_control_count = nctl;
+ icode->gpr_add_controls = (emu10k1_fx8010_control_gpr_t __user *)controls;
+ err = snd_emu10k1_icode_poke(emu, icode);
+ snd_leave_user(seg);
+
+ __err:
+ kfree(controls);
+ if (icode != NULL) {
+ kfree((void *)icode->gpr_map);
+ kfree(icode);
+ }
+ return err;
+}
+
+
+/*
+ * initial DSP configuration for Emu10k1
+ */
+
+/* when volume = max, then copy only to avoid volume modification */
+/* with iMAC0 (negative values) */
+static void __devinit _volume(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+ OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+ OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+ OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001);
+ OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+}
+static void __devinit _volume_add(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+ OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+ OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+ OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001);
+ OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+ OP(icode, ptr, iMAC0, dst, dst, src, vol);
+}
+static void __devinit _volume_out(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+ OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+ OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+ OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+ OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+ OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+}
+
+#define VOLUME(icode, ptr, dst, src, vol) \
+ _volume(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_IN(icode, ptr, dst, src, vol) \
+ _volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_ADD(icode, ptr, dst, src, vol) \
+ _volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
+ _volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_OUT(icode, ptr, dst, src, vol) \
+ _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
+#define _SWITCH(icode, ptr, dst, src, sw) \
+ OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
+#define SWITCH(icode, ptr, dst, src, sw) \
+ _SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw))
+#define SWITCH_IN(icode, ptr, dst, src, sw) \
+ _SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw))
+#define _SWITCH_NEG(icode, ptr, dst, src) \
+ OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001);
+#define SWITCH_NEG(icode, ptr, dst, src) \
+ _SWITCH_NEG(icode, ptr, GPR(dst), GPR(src))
+
+
+static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu)
+{
+ int err, i, z, gpr, tmp, playback, capture;
+ u32 ptr;
+ emu10k1_fx8010_code_t *icode;
+ emu10k1_fx8010_pcm_t *ipcm = NULL;
+ emu10k1_fx8010_control_gpr_t *controls = NULL, *ctl;
+ u32 *gpr_map;
+ mm_segment_t seg;
+
+ spin_lock_init(&emu->fx8010.irq_lock);
+ INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
+
+ if ((icode = kcalloc(1, sizeof(*icode), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ if ((icode->gpr_map = (u_int32_t __user *)kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t), GFP_KERNEL)) == NULL ||
+ (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, sizeof(emu10k1_fx8010_control_gpr_t), GFP_KERNEL)) == NULL ||
+ (ipcm = kcalloc(1, sizeof(*ipcm), GFP_KERNEL)) == NULL) {
+ err = -ENOMEM;
+ goto __err;
+ }
+ gpr_map = (u32 *)icode->gpr_map;
+
+ icode->tram_data_map = icode->gpr_map + 256;
+ icode->tram_addr_map = icode->tram_data_map + 160;
+ icode->code = icode->tram_addr_map + 160;
+
+ /* clear free GPRs */
+ for (i = 0; i < 256; i++)
+ set_bit(i, icode->gpr_valid);
+
+ /* clear TRAM data & address lines */
+ for (i = 0; i < 160; i++)
+ set_bit(i, icode->tram_valid);
+
+ strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
+ ptr = 0; i = 0;
+ /* we have 10 inputs */
+ playback = SND_EMU10K1_INPUTS;
+ /* we have 6 playback channels and tone control doubles */
+ capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2);
+ gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS;
+ tmp = 0x88; /* we need 4 temporary GPR */
+ /* from 0x8c to 0xff is the area for tone control */
+
+ /* stop FX processor */
+ snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP);
+
+ /*
+ * Process FX Buses
+ */
+ OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000); /* S/PDIF left */
+ OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000); /* S/PDIF right */
+
+ /* Raw S/PDIF PCM */
+ ipcm->substream = 0;
+ ipcm->channels = 2;
+ ipcm->tram_start = 0;
+ ipcm->buffer_size = (64 * 1024) / 2;
+ ipcm->gpr_size = gpr++;
+ ipcm->gpr_ptr = gpr++;
+ ipcm->gpr_count = gpr++;
+ ipcm->gpr_tmpcount = gpr++;
+ ipcm->gpr_trigger = gpr++;
+ ipcm->gpr_running = gpr++;
+ ipcm->etram[0] = 0;
+ ipcm->etram[1] = 1;
+
+ gpr_map[gpr + 0] = 0xfffff000;
+ gpr_map[gpr + 1] = 0xffff0000;
+ gpr_map[gpr + 2] = 0x70000000;
+ gpr_map[gpr + 3] = 0x00000007;
+ gpr_map[gpr + 4] = 0x001f << 11;
+ gpr_map[gpr + 5] = 0x001c << 11;
+ gpr_map[gpr + 6] = (0x22 - 0x01) - 1; /* skip at 01 to 22 */
+ gpr_map[gpr + 7] = (0x22 - 0x06) - 1; /* skip at 06 to 22 */
+ gpr_map[gpr + 8] = 0x2000000 + (2<<11);
+ gpr_map[gpr + 9] = 0x4000000 + (2<<11);
+ gpr_map[gpr + 10] = 1<<11;
+ gpr_map[gpr + 11] = (0x24 - 0x0a) - 1; /* skip at 0a to 24 */
+ gpr_map[gpr + 12] = 0;
+
+ /* if the trigger flag is not set, skip */
+ /* 00: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_trigger), C_00000000, C_00000000);
+ /* 01: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr + 6));
+ /* if the running flag is set, we're running */
+ /* 02: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_running), C_00000000, C_00000000);
+ /* 03: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000004);
+ /* wait until ((GPR_DBAC>>11) & 0x1f) == 0x1c) */
+ /* 04: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), GPR_DBAC, GPR(gpr + 4), C_00000000);
+ /* 05: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(gpr + 5));
+ /* 06: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 7));
+ /* 07: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000010, C_00000001, C_00000000);
+
+ /* 08: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000000, C_00000001);
+ /* 09: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), GPR(gpr + 12), C_ffffffff, C_00000000);
+ /* 0a: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 11));
+ /* 0b: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000001, C_00000000, C_00000000);
+
+ /* 0c: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[0]), GPR(gpr + 0), C_00000000);
+ /* 0d: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+ /* 0e: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+ /* 0f: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+ /* 10: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(8), GPR(gpr + 1), GPR(gpr + 2));
+
+ /* 11: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[1]), GPR(gpr + 0), C_00000000);
+ /* 12: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+ /* 13: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+ /* 14: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+ /* 15: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(9), GPR(gpr + 1), GPR(gpr + 2));
+
+ /* 16: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(ipcm->gpr_ptr), C_00000001, C_00000000);
+ /* 17: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(ipcm->gpr_size));
+ /* 18: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_MINUS, C_00000001);
+ /* 19: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), C_00000000, C_00000000, C_00000000);
+ /* 1a: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_ptr), GPR(tmp + 0), C_00000000, C_00000000);
+
+ /* 1b: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_tmpcount), C_ffffffff, C_00000000);
+ /* 1c: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+ /* 1d: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_count), C_00000000, C_00000000);
+ /* 1e: */ OP(icode, &ptr, iACC3, GPR_IRQ, C_80000000, C_00000000, C_00000000);
+ /* 1f: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000001, C_00010000);
+
+ /* 20: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00010000, C_00000001);
+ /* 21: */ OP(icode, &ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000002);
+
+ /* 22: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[0]), GPR(gpr + 8), GPR_DBAC, C_ffffffff);
+ /* 23: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[1]), GPR(gpr + 9), GPR_DBAC, C_ffffffff);
+
+ /* 24: */
+ gpr += 13;
+
+ /* Wave Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME(icode, &ptr, playback + z, z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "Wave Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* Wave Surround Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME(icode, &ptr, playback + 2 + z, z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "Wave Surround Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* Wave Center/LFE Playback Volume */
+ OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000);
+ OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002);
+ VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr);
+ snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0);
+ VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr);
+ snd_emu10k1_init_mono_control(controls + i++, "Wave LFE Playback Volume", gpr++, 0);
+
+ /* Wave Capture Volume + Switch */
+ for (z = 0; z < 2; z++) {
+ SWITCH(icode, &ptr, tmp + 0, z, gpr + 2 + z);
+ VOLUME(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "Wave Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "Wave Capture Switch", gpr + 2, 0);
+ gpr += 4;
+
+ /* Synth Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADD(icode, &ptr, playback + z, 2 + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "Synth Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* Synth Capture Volume + Switch */
+ for (z = 0; z < 2; z++) {
+ SWITCH(icode, &ptr, tmp + 0, 2 + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "Synth Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "Synth Capture Switch", gpr + 2, 0);
+ gpr += 4;
+
+ /* Surround Digital Playback Volume (renamed later without Digital) */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADD(icode, &ptr, playback + 2 + z, 4 + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Playback Volume", gpr, 100);
+ gpr += 2;
+
+ /* Surround Capture Volume + Switch */
+ for (z = 0; z < 2; z++) {
+ SWITCH(icode, &ptr, tmp + 0, 4 + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "Surround Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "Surround Capture Switch", gpr + 2, 0);
+ gpr += 4;
+
+ /* Center Playback Volume (renamed later without Digital) */
+ VOLUME_ADD(icode, &ptr, playback + 4, 6, gpr);
+ snd_emu10k1_init_mono_control(controls + i++, "Center Digital Playback Volume", gpr++, 100);
+
+ /* LFE Playback Volume + Switch (renamed later without Digital) */
+ VOLUME_ADD(icode, &ptr, playback + 5, 7, gpr);
+ snd_emu10k1_init_mono_control(controls + i++, "LFE Digital Playback Volume", gpr++, 100);
+
+ /*
+ * Process inputs
+ */
+
+ if (emu->fx8010.extin_mask & ((1<<EXTIN_AC97_L)|(1<<EXTIN_AC97_R))) {
+ /* AC'97 Playback Volume */
+ VOLUME_ADDIN(icode, &ptr, playback + 0, EXTIN_AC97_L, gpr); gpr++;
+ VOLUME_ADDIN(icode, &ptr, playback + 1, EXTIN_AC97_R, gpr); gpr++;
+ snd_emu10k1_init_stereo_control(controls + i++, "AC97 Playback Volume", gpr-2, 0);
+ /* AC'97 Capture Volume */
+ VOLUME_ADDIN(icode, &ptr, capture + 0, EXTIN_AC97_L, gpr); gpr++;
+ VOLUME_ADDIN(icode, &ptr, capture + 1, EXTIN_AC97_R, gpr); gpr++;
+ snd_emu10k1_init_stereo_control(controls + i++, "AC97 Capture Volume", gpr-2, 100);
+ }
+
+ if (emu->fx8010.extin_mask & ((1<<EXTIN_SPDIF_CD_L)|(1<<EXTIN_SPDIF_CD_R))) {
+ /* IEC958 TTL Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_SPDIF_CD_L + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "IEC958 TTL Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* IEC958 TTL Capture Volume + Switch */
+ for (z = 0; z < 2; z++) {
+ SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_SPDIF_CD_L + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "IEC958 TTL Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 TTL Capture Switch", gpr + 2, 0);
+ gpr += 4;
+ }
+
+ if (emu->fx8010.extin_mask & ((1<<EXTIN_ZOOM_L)|(1<<EXTIN_ZOOM_R))) {
+ /* Zoom Video Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_ZOOM_L + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* Zoom Video Capture Volume + Switch */
+ for (z = 0; z < 2; z++) {
+ SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_ZOOM_L + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "Zoom Video Capture Switch", gpr + 2, 0);
+ gpr += 4;
+ }
+
+ if (emu->fx8010.extin_mask & ((1<<EXTIN_TOSLINK_L)|(1<<EXTIN_TOSLINK_R))) {
+ /* IEC958 Optical Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_TOSLINK_L + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "IEC958 LiveDrive Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* IEC958 Optical Capture Volume */
+ for (z = 0; z < 2; z++) {
+ SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_TOSLINK_L + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "IEC958 LiveDrive Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 LiveDrive Capture Switch", gpr + 2, 0);
+ gpr += 4;
+ }
+
+ if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE1_L)|(1<<EXTIN_LINE1_R))) {
+ /* Line LiveDrive Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE1_L + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* Line LiveDrive Capture Volume + Switch */
+ for (z = 0; z < 2; z++) {
+ SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE1_L + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line LiveDrive Capture Switch", gpr + 2, 0);
+ gpr += 4;
+ }
+
+ if (emu->fx8010.extin_mask & ((1<<EXTIN_COAX_SPDIF_L)|(1<<EXTIN_COAX_SPDIF_R))) {
+ /* IEC958 Coax Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_COAX_SPDIF_L + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Coaxial Playback Volume", gpr, 0);
+ gpr += 2;
+
+ /* IEC958 Coax Capture Volume + Switch */
+ for (z = 0; z < 2; z++) {
+ SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_COAX_SPDIF_L + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Coaxial Capture Volume", gpr, 0);
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 Coaxial Capture Switch", gpr + 2, 0);
+ gpr += 4;
+ }
+
+ if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE2_L)|(1<<EXTIN_LINE2_R))) {
+ /* Line LiveDrive Playback Volume */
+ for (z = 0; z < 2; z++)
+ VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE2_L + z, gpr + z);
+ snd_emu10k1_init_stereo_control(controls + i++, "Line2 LiveDrive Playback Volume", gpr, 0);
+ controls[i-1].id.index = 1;
+ gpr += 2;
+
+ /* Line LiveDrive Capture Volume */
+ for (z = 0; z < 2; z++) {
+ SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE2_L + z, gpr + 2 + z);
+ VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+ }
+ snd_emu10k1_init_stereo_control(controls + i++, "Line2 LiveDrive Capture Volume", gpr, 0);
+ controls[i-1].id.index = 1;
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line2 LiveDrive Capture Switch", gpr + 2, 0);
+ controls[i-1].id.index = 1;
+ gpr += 4;
+ }
+
+ /*
+ * Process tone control
+ */
+ OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), GPR(playback + 0), C_00000000, C_00000000); /* left */
+ OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), GPR(playback + 1), C_00000000, C_00000000); /* right */
+ OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), GPR(playback + 2), C_00000000, C_00000000); /* rear left */
+ OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), GPR(playback + 3), C_00000000, C_00000000); /* rear right */
+ OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), GPR(playback + 4), C_00000000, C_00000000); /* center */
+ OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), GPR(playback + 5), C_00000000, C_00000000); /* LFE */
+
+ ctl = &controls[i + 0];
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, "Tone Control - Bass");
+ ctl->vcount = 2;
+ ctl->count = 10;
+ ctl->min = 0;
+ ctl->max = 40;
+ ctl->value[0] = ctl->value[1] = 20;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
+ ctl = &controls[i + 1];
+ ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(ctl->id.name, "Tone Control - Treble");
+ ctl->vcount = 2;
+ ctl->count = 10;
+ ctl->min = 0;
+ ctl->max = 40;
+ ctl->value[0] = ctl->value[1] = 20;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
+
+#define BASS_GPR 0x8c
+#define TREBLE_GPR 0x96
+
+ for (z = 0; z < 5; z++) {
+ int j;
+ for (j = 0; j < 2; j++) {
+ controls[i + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
+ controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+ }
+ }
+ for (z = 0; z < 3; z++) { /* front/rear/center-lfe */
+ int j, k, l, d;
+ for (j = 0; j < 2; j++) { /* left/right */
+ k = 0xa0 + (z * 8) + (j * 4);
+ l = 0xd0 + (z * 8) + (j * 4);
+ d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+
+ OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j));
+ OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j));
+ OP(icode, &ptr, iMACMV, GPR(k), GPR(d), GPR(k), GPR(BASS_GPR + 2 + j));
+ OP(icode, &ptr, iMACMV, GPR(k+3), GPR(k+2), GPR(k+3), GPR(BASS_GPR + 8 + j));
+ OP(icode, &ptr, iMAC0, GPR(k+2), GPR_ACCU, GPR(k+2), GPR(BASS_GPR + 6 + j));
+ OP(icode, &ptr, iACC3, GPR(k+2), GPR(k+2), GPR(k+2), C_00000000);
+
+ OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(k+2), GPR(TREBLE_GPR + 0 + j));
+ OP(icode, &ptr, iMACMV, GPR(l+1), GPR(l), GPR(l+1), GPR(TREBLE_GPR + 4 + j));
+ OP(icode, &ptr, iMACMV, GPR(l), GPR(k+2), GPR(l), GPR(TREBLE_GPR + 2 + j));
+ OP(icode, &ptr, iMACMV, GPR(l+3), GPR(l+2), GPR(l+3), GPR(TREBLE_GPR + 8 + j));
+ OP(icode, &ptr, iMAC0, GPR(l+2), GPR_ACCU, GPR(l+2), GPR(TREBLE_GPR + 6 + j));
+ OP(icode, &ptr, iMACINT0, GPR(l+2), C_00000000, GPR(l+2), C_00000010);
+
+ OP(icode, &ptr, iACC3, GPR(d), GPR(l+2), C_00000000, C_00000000);
+
+ if (z == 2) /* center */
+ break;
+ }
+ }
+ i += 2;
+
+#undef BASS_GPR
+#undef TREBLE_GPR
+
+ for (z = 0; z < 6; z++) {
+ SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
+ SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
+ SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+ OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+ }
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
+ gpr += 2;
+
+ /*
+ * Process outputs
+ */
+ if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_L)|(1<<EXTOUT_AC97_R))) {
+ /* AC'97 Playback Volume */
+
+ for (z = 0; z < 2; z++)
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), C_00000000, C_00000000);
+ }
+
+ if (emu->fx8010.extout_mask & ((1<<EXTOUT_TOSLINK_L)|(1<<EXTOUT_TOSLINK_R))) {
+ /* IEC958 Optical Raw Playback Switch */
+
+ for (z = 0; z < 2; z++) {
+ SWITCH(icode, &ptr, tmp + 0, 8 + z, gpr + z);
+ SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+ SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_TOSLINK_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#endif
+ }
+
+ snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 Optical Raw Playback Switch", gpr, 0);
+ gpr += 2;
+ }
+
+ if (emu->fx8010.extout_mask & ((1<<EXTOUT_HEADPHONE_L)|(1<<EXTOUT_HEADPHONE_R))) {
+ /* Headphone Playback Volume */
+
+ for (z = 0; z < 2; z++) {
+ SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4 + z, gpr + 2 + z);
+ SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 2 + z);
+ SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+ VOLUME_OUT(icode, &ptr, EXTOUT_HEADPHONE_L + z, tmp + 0, gpr + z);
+ }
+
+ snd_emu10k1_init_stereo_control(controls + i++, "Headphone Playback Volume", gpr + 0, 0);
+ controls[i-1].id.index = 1; /* AC'97 can have also Headphone control */
+ snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone Center Playback Switch", gpr + 2, 0);
+ controls[i-1].id.index = 1;
+ snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone LFE Playback Switch", gpr + 3, 0);
+ controls[i-1].id.index = 1;
+
+ gpr += 4;
+ }
+
+ if (emu->fx8010.extout_mask & ((1<<EXTOUT_REAR_L)|(1<<EXTOUT_REAR_R)))
+ for (z = 0; z < 2; z++)
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+
+ if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_REAR_L)|(1<<EXTOUT_AC97_REAR_R)))
+ for (z = 0; z < 2; z++)
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+
+ if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_CENTER)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+#else
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+#endif
+ }
+
+ if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_LFE)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+#else
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+#endif
+ }
+
+#ifndef EMU10K1_CAPTURE_DIGITAL_OUT
+ for (z = 0; z < 2; z++)
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(capture + z), C_00000000, C_00000000);
+#endif
+
+ if (emu->fx8010.extout_mask & (1<<EXTOUT_MIC_CAP))
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_MIC_CAP), GPR(capture + 2), C_00000000, C_00000000);
+
+ /* EFX capture - capture the 16 EXTINS */
+ OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
+ OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
+ OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
+ OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
+ /* Dont connect anything to FXBUS2 1 and 2. These are shared with
+ * Center/LFE on the SBLive 5.1. The kX driver only changes the
+ * routing when it detects an SBLive 5.1.
+ *
+ * Since only 14 of the 16 EXTINs are used, this is not a big problem.
+ * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture
+ * 0 and 3, then the rest of the EXTINs to the corresponding FX capture
+ * channel.
+ */
+ for (z = 4; z < 14; z++) {
+ OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+ }
+
+ if (gpr > tmp) {
+ snd_BUG();
+ err = -EIO;
+ goto __err;
+ }
+ if (i > SND_EMU10K1_GPR_CONTROLS) {
+ snd_BUG();
+ err = -EIO;
+ goto __err;
+ }
+
+ /* clear remaining instruction memory */
+ while (ptr < 0x200)
+ OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000);
+
+ if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0)
+ goto __err;
+ seg = snd_enter_user();
+ icode->gpr_add_control_count = i;
+ icode->gpr_add_controls = (emu10k1_fx8010_control_gpr_t __user *)controls;
+ err = snd_emu10k1_icode_poke(emu, icode);
+ snd_leave_user(seg);
+ if (err >= 0)
+ err = snd_emu10k1_ipcm_poke(emu, ipcm);
+ __err:
+ kfree(ipcm);
+ kfree(controls);
+ if (icode != NULL) {
+ kfree((void *)icode->gpr_map);
+ kfree(icode);
+ }
+ return err;
+}
+
+int __devinit snd_emu10k1_init_efx(emu10k1_t *emu)
+{
+ if (emu->audigy)
+ return _snd_emu10k1_audigy_init_efx(emu);
+ else
+ return _snd_emu10k1_init_efx(emu);
+}
+
+void snd_emu10k1_free_efx(emu10k1_t *emu)
+{
+ /* stop processor */
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP);
+}
+
+#if 0 // FIXME: who use them?
+int snd_emu10k1_fx8010_tone_control_activate(emu10k1_t *emu, int output)
+{
+ snd_runtime_check(output >= 0 && output < 6, return -EINVAL);
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 1);
+ return 0;
+}
+
+int snd_emu10k1_fx8010_tone_control_deactivate(emu10k1_t *emu, int output)
+{
+ snd_runtime_check(output >= 0 && output < 6, return -EINVAL);
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 0);
+ return 0;
+}
+#endif
+
+int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size)
+{
+ u8 size_reg = 0;
+
+ /* size is in samples */
+ if (size != 0) {
+ size = (size - 1) >> 13;
+
+ while (size) {
+ size >>= 1;
+ size_reg++;
+ }
+ size = 0x2000 << size_reg;
+ }
+ if ((emu->fx8010.etram_pages.bytes / 2) == size)
+ return 0;
+ spin_lock_irq(&emu->emu_lock);
+ outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+ spin_unlock_irq(&emu->emu_lock);
+ snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+ snd_emu10k1_ptr_write(emu, TCBS, 0, 0);
+ if (emu->fx8010.etram_pages.area != NULL) {
+ snd_dma_free_pages(&emu->fx8010.etram_pages);
+ emu->fx8010.etram_pages.area = NULL;
+ emu->fx8010.etram_pages.bytes = 0;
+ }
+
+ if (size > 0) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
+ size * 2, &emu->fx8010.etram_pages) < 0)
+ return -ENOMEM;
+ memset(emu->fx8010.etram_pages.area, 0, size * 2);
+ snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr);
+ snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
+ spin_lock_irq(&emu->emu_lock);
+ outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+ spin_unlock_irq(&emu->emu_lock);
+ }
+
+ return 0;
+}
+
+static int snd_emu10k1_fx8010_open(snd_hwdep_t * hw, struct file *file)
+{
+ return 0;
+}
+
+static void copy_string(char *dst, char *src, char *null, int idx)
+{
+ if (src == NULL)
+ sprintf(dst, "%s %02X", null, idx);
+ else
+ strcpy(dst, src);
+}
+
+static int snd_emu10k1_fx8010_info(emu10k1_t *emu, emu10k1_fx8010_info_t *info)
+{
+ char **fxbus, **extin, **extout;
+ unsigned short fxbus_mask, extin_mask, extout_mask;
+ int res;
+
+ memset(info, 0, sizeof(info));
+ info->card = emu->card_type;
+ info->internal_tram_size = emu->fx8010.itram_size;
+ info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
+ fxbus = fxbuses;
+ extin = emu->audigy ? audigy_ins : creative_ins;
+ extout = emu->audigy ? audigy_outs : creative_outs;
+ fxbus_mask = emu->fx8010.fxbus_mask;
+ extin_mask = emu->fx8010.extin_mask;
+ extout_mask = emu->fx8010.extout_mask;
+ for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
+ copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
+ copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
+ copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+ }
+ for (res = 16; res < 32; res++, extout++)
+ copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+ info->gpr_controls = emu->fx8010.gpr_count;
+ return 0;
+}
+
+static int snd_emu10k1_fx8010_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ emu10k1_t *emu = hw->private_data;
+ emu10k1_fx8010_info_t *info;
+ emu10k1_fx8010_code_t *icode;
+ emu10k1_fx8010_pcm_t *ipcm;
+ unsigned int addr;
+ void __user *argp = (void __user *)arg;
+ int res;
+
+ switch (cmd) {
+ case SNDRV_EMU10K1_IOCTL_INFO:
+ info = (emu10k1_fx8010_info_t *)kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ if ((res = snd_emu10k1_fx8010_info(emu, info)) < 0) {
+ kfree(info);
+ return res;
+ }
+ if (copy_to_user(argp, info, sizeof(*info))) {
+ kfree(info);
+ return -EFAULT;
+ }
+ kfree(info);
+ return 0;
+ case SNDRV_EMU10K1_IOCTL_CODE_POKE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ icode = (emu10k1_fx8010_code_t *)kmalloc(sizeof(*icode), GFP_KERNEL);
+ if (icode == NULL)
+ return -ENOMEM;
+ if (copy_from_user(icode, argp, sizeof(*icode))) {
+ kfree(icode);
+ return -EFAULT;
+ }
+ res = snd_emu10k1_icode_poke(emu, icode);
+ kfree(icode);
+ return res;
+ case SNDRV_EMU10K1_IOCTL_CODE_PEEK:
+ icode = (emu10k1_fx8010_code_t *)kmalloc(sizeof(*icode), GFP_KERNEL);
+ if (icode == NULL)
+ return -ENOMEM;
+ if (copy_from_user(icode, argp, sizeof(*icode))) {
+ kfree(icode);
+ return -EFAULT;
+ }
+ res = snd_emu10k1_icode_peek(emu, icode);
+ if (res == 0 && copy_to_user(argp, icode, sizeof(*icode))) {
+ kfree(icode);
+ return -EFAULT;
+ }
+ kfree(icode);
+ return res;
+ case SNDRV_EMU10K1_IOCTL_PCM_POKE:
+ ipcm = (emu10k1_fx8010_pcm_t *)kmalloc(sizeof(*ipcm), GFP_KERNEL);
+ if (ipcm == NULL)
+ return -ENOMEM;
+ if (copy_from_user(ipcm, argp, sizeof(*ipcm))) {
+ kfree(ipcm);
+ return -EFAULT;
+ }
+ res = snd_emu10k1_ipcm_poke(emu, ipcm);
+ kfree(ipcm);
+ return res;
+ case SNDRV_EMU10K1_IOCTL_PCM_PEEK:
+ ipcm = kcalloc(1, sizeof(*ipcm), GFP_KERNEL);
+ if (ipcm == NULL)
+ return -ENOMEM;
+ if (copy_from_user(ipcm, argp, sizeof(*ipcm))) {
+ kfree(ipcm);
+ return -EFAULT;
+ }
+ res = snd_emu10k1_ipcm_peek(emu, ipcm);
+ if (res == 0 && copy_to_user(argp, ipcm, sizeof(*ipcm))) {
+ kfree(ipcm);
+ return -EFAULT;
+ }
+ kfree(ipcm);
+ return res;
+ case SNDRV_EMU10K1_IOCTL_TRAM_SETUP:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (get_user(addr, (unsigned int __user *)argp))
+ return -EFAULT;
+ down(&emu->fx8010.lock);
+ res = snd_emu10k1_fx8010_tram_setup(emu, addr);
+ up(&emu->fx8010.lock);
+ return res;
+ case SNDRV_EMU10K1_IOCTL_STOP:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP);
+ return 0;
+ case SNDRV_EMU10K1_IOCTL_CONTINUE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = 0);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = 0);
+ return 0;
+ case SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_ZC);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_ZC);
+ udelay(10);
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+ return 0;
+ case SNDRV_EMU10K1_IOCTL_SINGLE_STEP:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (get_user(addr, (unsigned int __user *)argp))
+ return -EFAULT;
+ if (addr > 0x1ff)
+ return -EINVAL;
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr);
+ udelay(10);
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr);
+ else
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr);
+ return 0;
+ case SNDRV_EMU10K1_IOCTL_DBG_READ:
+ if (emu->audigy)
+ addr = snd_emu10k1_ptr_read(emu, A_DBG, 0);
+ else
+ addr = snd_emu10k1_ptr_read(emu, DBG, 0);
+ if (put_user(addr, (unsigned int __user *)argp))
+ return -EFAULT;
+ return 0;
+ }
+ return -ENOTTY;
+}
+
+static int snd_emu10k1_fx8010_release(snd_hwdep_t * hw, struct file *file)
+{
+ return 0;
+}
+
+int __devinit snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep)
+{
+ snd_hwdep_t *hw;
+ int err;
+
+ if (rhwdep)
+ *rhwdep = NULL;
+ if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0)
+ return err;
+ strcpy(hw->name, "EMU10K1 (FX8010)");
+ hw->iface = SNDRV_HWDEP_IFACE_EMU10K1;
+ hw->ops.open = snd_emu10k1_fx8010_open;
+ hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
+ hw->ops.release = snd_emu10k1_fx8010_release;
+ hw->private_data = emu;
+ if (rhwdep)
+ *rhwdep = hw;
+ return 0;
+}
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
new file mode 100644
index 00000000000..044663d31aa
--- /dev/null
+++ b/sound/pci/emu10k1/emumixer.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ * Takashi Iwai <tiwai@suse.de>
+ * Creative Labs, Inc.
+ * Routines for control of EMU10K1 chips / mixer routines
+ * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define AC97_ID_STAC9758 0x83847658
+
+static int snd_emu10k1_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_emu10k1_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * 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 snd_audigy_spdif_output_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"44100", "48000", "96000"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_audigy_spdif_output_rate_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int tmp;
+ unsigned long flags;
+
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+ switch (tmp & A_SPDIF_RATE_MASK) {
+ case A_SPDIF_44100:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ case A_SPDIF_48000:
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ case A_SPDIF_96000:
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 1;
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_audigy_spdif_output_rate_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int reg, val, tmp;
+ unsigned long flags;
+
+ switch(ucontrol->value.enumerated.item[0]) {
+ case 0:
+ val = A_SPDIF_44100;
+ break;
+ case 1:
+ val = A_SPDIF_48000;
+ break;
+ case 2:
+ val = A_SPDIF_96000;
+ break;
+ default:
+ val = A_SPDIF_48000;
+ break;
+ }
+
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+ tmp = reg & ~A_SPDIF_RATE_MASK;
+ tmp |= val;
+ if ((change = (tmp != reg)))
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_audigy_spdif_output_rate =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Audigy SPDIF Output Sample Rate",
+ .count = 1,
+ .info = snd_audigy_spdif_output_rate_info,
+ .get = snd_audigy_spdif_output_rate_get,
+ .put = snd_audigy_spdif_output_rate_put
+};
+
+static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int change;
+ unsigned int val;
+ unsigned long flags;
+
+ val = (ucontrol->value.iec958.status[0] << 0) |
+ (ucontrol->value.iec958.status[1] << 8) |
+ (ucontrol->value.iec958.status[2] << 16) |
+ (ucontrol->value.iec958.status[3] << 24);
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ change = val != emu->spdif_bits[idx];
+ if (change) {
+ snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
+ emu->spdif_bits[idx] = val;
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+ .count = 4,
+ .info = snd_emu10k1_spdif_info,
+ .get = snd_emu10k1_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_emu10k1_spdif_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .count = 4,
+ .info = snd_emu10k1_spdif_info,
+ .get = snd_emu10k1_spdif_get,
+ .put = snd_emu10k1_spdif_put
+};
+
+
+static void update_emu10k1_fxrt(emu10k1_t *emu, int voice, unsigned char *route)
+{
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+ snd_emu10k1_compose_audigy_fxrt1(route));
+ snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+ snd_emu10k1_compose_audigy_fxrt2(route));
+ } else {
+ snd_emu10k1_ptr_write(emu, FXRT, voice,
+ snd_emu10k1_compose_send_routing(route));
+ }
+}
+
+static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char *volume)
+{
+ snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]);
+ snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]);
+ snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
+ snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
+ if (emu->audigy) {
+ unsigned int val = ((unsigned int)volume[4] << 24) |
+ ((unsigned int)volume[5] << 16) |
+ ((unsigned int)volume[6] << 8) |
+ (unsigned int)volume[7];
+ snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val);
+ }
+}
+
+/* PCM stream controls */
+
+static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = emu->audigy ? 3*8 : 3*4;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
+ return 0;
+}
+
+static int snd_emu10k1_send_routing_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int voice, idx;
+ int num_efx = emu->audigy ? 8 : 4;
+ int mask = emu->audigy ? 0x3f : 0x0f;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (voice = 0; voice < 3; voice++)
+ for (idx = 0; idx < num_efx; idx++)
+ ucontrol->value.integer.value[(voice * num_efx) + idx] =
+ mix->send_routing[voice][idx] & mask;
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int change = 0, voice, idx, val;
+ int num_efx = emu->audigy ? 8 : 4;
+ int mask = emu->audigy ? 0x3f : 0x0f;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (voice = 0; voice < 3; voice++)
+ for (idx = 0; idx < num_efx; idx++) {
+ val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
+ if (mix->send_routing[voice][idx] != val) {
+ mix->send_routing[voice][idx] = val;
+ change = 1;
+ }
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+ &mix->send_routing[1][0]);
+ update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
+ &mix->send_routing[2][0]);
+ } else if (mix->epcm->voices[0]) {
+ update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+ &mix->send_routing[0][0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_send_routing_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EMU10K1 PCM Send Routing",
+ .count = 32,
+ .info = snd_emu10k1_send_routing_info,
+ .get = snd_emu10k1_send_routing_get,
+ .put = snd_emu10k1_send_routing_put
+};
+
+static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = emu->audigy ? 3*8 : 3*4;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_emu10k1_send_volume_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int idx;
+ int num_efx = emu->audigy ? 8 : 4;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < 3*num_efx; idx++)
+ ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int change = 0, idx, val;
+ int num_efx = emu->audigy ? 8 : 4;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < 3*num_efx; idx++) {
+ val = ucontrol->value.integer.value[idx] & 255;
+ if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
+ mix->send_volume[idx/num_efx][idx%num_efx] = val;
+ change = 1;
+ }
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+ &mix->send_volume[1][0]);
+ update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
+ &mix->send_volume[2][0]);
+ } else if (mix->epcm->voices[0]) {
+ update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+ &mix->send_volume[0][0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_send_volume_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EMU10K1 PCM Send Volume",
+ .count = 32,
+ .info = snd_emu10k1_send_volume_info,
+ .get = snd_emu10k1_send_volume_get,
+ .put = snd_emu10k1_send_volume_put
+};
+
+static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff;
+ return 0;
+}
+
+static int snd_emu10k1_attn_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ unsigned long flags;
+ int idx;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < 3; idx++)
+ ucontrol->value.integer.value[idx] = mix->attn[idx];
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int change = 0, idx, val;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < 3; idx++) {
+ val = ucontrol->value.integer.value[idx] & 0xffff;
+ if (mix->attn[idx] != val) {
+ mix->attn[idx] = val;
+ change = 1;
+ }
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
+ snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
+ } else if (mix->epcm->voices[0]) {
+ snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_attn_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "EMU10K1 PCM Volume",
+ .count = 32,
+ .info = snd_emu10k1_attn_info,
+ .get = snd_emu10k1_attn_get,
+ .put = snd_emu10k1_attn_put
+};
+
+/* Mutichannel PCM stream controls */
+
+static int snd_emu10k1_efx_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = emu->audigy ? 8 : 4;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
+ return 0;
+}
+
+static int snd_emu10k1_efx_send_routing_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int idx;
+ int num_efx = emu->audigy ? 8 : 4;
+ int mask = emu->audigy ? 0x3f : 0x0f;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < num_efx; idx++)
+ ucontrol->value.integer.value[idx] =
+ mix->send_routing[0][idx] & mask;
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_efx_send_routing_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[ch];
+ int change = 0, idx, val;
+ int num_efx = emu->audigy ? 8 : 4;
+ int mask = emu->audigy ? 0x3f : 0x0f;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < num_efx; idx++) {
+ val = ucontrol->value.integer.value[idx] & mask;
+ if (mix->send_routing[0][idx] != val) {
+ mix->send_routing[0][idx] = val;
+ change = 1;
+ }
+ }
+
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[ch]) {
+ update_emu10k1_fxrt(emu, mix->epcm->voices[ch]->number,
+ &mix->send_routing[0][0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_efx_send_routing_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Multichannel PCM Send Routing",
+ .count = 16,
+ .info = snd_emu10k1_efx_send_routing_info,
+ .get = snd_emu10k1_efx_send_routing_get,
+ .put = snd_emu10k1_efx_send_routing_put
+};
+
+static int snd_emu10k1_efx_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = emu->audigy ? 8 : 4;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_emu10k1_efx_send_volume_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int idx;
+ int num_efx = emu->audigy ? 8 : 4;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < num_efx; idx++)
+ ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_efx_send_volume_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[ch];
+ int change = 0, idx, val;
+ int num_efx = emu->audigy ? 8 : 4;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < num_efx; idx++) {
+ val = ucontrol->value.integer.value[idx] & 255;
+ if (mix->send_volume[0][idx] != val) {
+ mix->send_volume[0][idx] = val;
+ change = 1;
+ }
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[ch]) {
+ update_emu10k1_send_volume(emu, mix->epcm->voices[ch]->number,
+ &mix->send_volume[0][0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+
+static snd_kcontrol_new_t snd_emu10k1_efx_send_volume_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Multichannel PCM Send Volume",
+ .count = 16,
+ .info = snd_emu10k1_efx_send_volume_info,
+ .get = snd_emu10k1_efx_send_volume_get,
+ .put = snd_emu10k1_efx_send_volume_put
+};
+
+static int snd_emu10k1_efx_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff;
+ return 0;
+}
+
+static int snd_emu10k1_efx_attn_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ ucontrol->value.integer.value[0] = mix->attn[0];
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_efx_attn_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[ch];
+ int change = 0, val;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ val = ucontrol->value.integer.value[0] & 0xffff;
+ if (mix->attn[0] != val) {
+ mix->attn[0] = val;
+ change = 1;
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[ch]) {
+ snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_efx_attn_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Multichannel PCM Volume",
+ .count = 16,
+ .info = snd_emu10k1_efx_attn_info,
+ .get = snd_emu10k1_efx_attn_get,
+ .put = snd_emu10k1_efx_attn_put
+};
+
+static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_emu10k1_shared_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+
+ if (emu->audigy)
+ ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
+ else
+ ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
+ return 0;
+}
+
+static int snd_emu10k1_shared_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned long flags;
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int reg, val;
+ int change = 0;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ if (emu->audigy) {
+ reg = inl(emu->port + A_IOCFG);
+ val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
+ change = (reg & A_IOCFG_GPOUT0) != val;
+ if (change) {
+ reg &= ~A_IOCFG_GPOUT0;
+ reg |= val;
+ outl(reg | val, emu->port + A_IOCFG);
+ }
+ }
+ reg = inl(emu->port + HCFG);
+ val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0;
+ change |= (reg & HCFG_GPOUT0) != val;
+ if (change) {
+ reg &= ~HCFG_GPOUT0;
+ reg |= val;
+ outl(reg | val, emu->port + HCFG);
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_shared_spdif __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SB Live Analog/Digital Output Jack",
+ .info = snd_emu10k1_shared_spdif_info,
+ .get = snd_emu10k1_shared_spdif_get,
+ .put = snd_emu10k1_shared_spdif_put
+};
+
+static snd_kcontrol_new_t snd_audigy_shared_spdif __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Audigy Analog/Digital Output Jack",
+ .info = snd_emu10k1_shared_spdif_info,
+ .get = snd_emu10k1_shared_spdif_get,
+ .put = snd_emu10k1_shared_spdif_put
+};
+
+/*
+ */
+static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97)
+{
+ emu10k1_t *emu = ac97->private_data;
+ emu->ac97 = NULL;
+}
+
+/*
+ */
+static int remove_ctl(snd_card_t *card, const char *name)
+{
+ snd_ctl_elem_id_t id;
+ memset(&id, 0, sizeof(id));
+ strcpy(id.name, name);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_remove_id(card, &id);
+}
+
+static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name)
+{
+ snd_ctl_elem_id_t sid;
+ memset(&sid, 0, sizeof(sid));
+ strcpy(sid.name, name);
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_find_id(card, &sid);
+}
+
+static int rename_ctl(snd_card_t *card, const char *src, const char *dst)
+{
+ snd_kcontrol_t *kctl = ctl_find(card, src);
+ if (kctl) {
+ strcpy(kctl->id.name, dst);
+ return 0;
+ }
+ return -ENOENT;
+}
+
+int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
+{
+ int err, pcm;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = emu->card;
+ char **c;
+ static char *emu10k1_remove_ctls[] = {
+ /* no AC97 mono, surround, center/lfe */
+ "Master Mono Playback Switch",
+ "Master Mono Playback Volume",
+ "PCM Out Path & Mute",
+ "Mono Output Select",
+ "Surround Playback Switch",
+ "Surround Playback Volume",
+ "Center Playback Switch",
+ "Center Playback Volume",
+ "LFE Playback Switch",
+ "LFE Playback Volume",
+ NULL
+ };
+ static char *emu10k1_rename_ctls[] = {
+ "Surround Digital Playback Volume", "Surround Playback Volume",
+ "Center Digital Playback Volume", "Center Playback Volume",
+ "LFE Digital Playback Volume", "LFE Playback Volume",
+ NULL
+ };
+ static char *audigy_remove_ctls[] = {
+ /* Master/PCM controls on ac97 of Audigy has no effect */
+ "PCM Playback Switch",
+ "PCM Playback Volume",
+ "Master Mono Playback Switch",
+ "Master Mono Playback Volume",
+ "Master Playback Switch",
+ "Master Playback Volume",
+ "PCM Out Path & Mute",
+ "Mono Output Select",
+ /* remove unused AC97 capture controls */
+ "Capture Source",
+ "Capture Switch",
+ "Capture Volume",
+ "Mic Select",
+ "Video Playback Switch",
+ "Video Playback Volume",
+ "Mic Playback Switch",
+ "Mic Playback Volume",
+ NULL
+ };
+ static char *audigy_rename_ctls[] = {
+ /* use conventional names */
+ "Wave Playback Volume", "PCM Playback Volume",
+ /* "Wave Capture Volume", "PCM Capture Volume", */
+ "Wave Master Playback Volume", "Master Playback Volume",
+ "AMic Playback Volume", "Mic Playback Volume",
+ NULL
+ };
+
+ if (!emu->no_ac97) {
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ static ac97_bus_ops_t ops = {
+ .write = snd_emu10k1_ac97_write,
+ .read = snd_emu10k1_ac97_read,
+ };
+
+ if ((err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+ pbus->no_vra = 1; /* we don't need VRA */
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = emu;
+ ac97.private_free = snd_emu10k1_mixer_free_ac97;
+ ac97.scaps = AC97_SCAP_NO_SPDIF;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &emu->ac97)) < 0)
+ return err;
+ if (emu->audigy) {
+ /* set master volume to 0 dB */
+ snd_ac97_write(emu->ac97, AC97_MASTER, 0x0000);
+ /* set capture source to mic */
+ snd_ac97_write(emu->ac97, AC97_REC_SEL, 0x0000);
+ c = audigy_remove_ctls;
+ } else {
+ /*
+ * Credits for cards based on STAC9758:
+ * James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Voluspa <voluspa@comhem.se>
+ */
+ if (emu->ac97->id == AC97_ID_STAC9758) {
+ emu->rear_ac97 = 1;
+ snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
+ }
+ /* remove unused AC97 controls */
+ snd_ac97_write(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
+ snd_ac97_write(emu->ac97, AC97_CENTER_LFE_MASTER, 0x0202);
+ c = emu10k1_remove_ctls;
+ }
+ for (; *c; c++)
+ remove_ctl(card, *c);
+ } else {
+ if (emu->APS)
+ strcpy(emu->card->mixername, "EMU APS");
+ else if (emu->audigy)
+ strcpy(emu->card->mixername, "SB Audigy");
+ else
+ strcpy(emu->card->mixername, "Emu10k1");
+ }
+
+ if (emu->audigy)
+ c = audigy_rename_ctls;
+ else
+ c = emu10k1_rename_ctls;
+ for (; *c; c += 2)
+ rename_ctl(card, c[0], c[1]);
+
+ if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+
+ if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+
+ if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+
+ if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+
+ /* initialize the routing and volume table for each pcm playback stream */
+ for (pcm = 0; pcm < 32; pcm++) {
+ emu10k1_pcm_mixer_t *mix;
+ int v;
+
+ mix = &emu->pcm_mixer[pcm];
+ mix->epcm = NULL;
+
+ for (v = 0; v < 4; v++)
+ mix->send_routing[0][v] =
+ mix->send_routing[1][v] =
+ mix->send_routing[2][v] = v;
+
+ memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+ mix->send_volume[0][0] = mix->send_volume[0][1] =
+ mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+
+ mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+ }
+
+ /* initialize the routing and volume table for the multichannel playback stream */
+ for (pcm = 0; pcm < NUM_EFX_PLAYBACK; pcm++) {
+ emu10k1_pcm_mixer_t *mix;
+ int v;
+
+ mix = &emu->efx_pcm_mixer[pcm];
+ mix->epcm = NULL;
+
+ mix->send_routing[0][0] = pcm;
+ mix->send_routing[0][1] = (pcm == 0) ? 1 : 0;
+ for (v = 0; v < 2; v++)
+ mix->send_routing[0][2+v] = 13+v;
+ if (emu->audigy)
+ for (v = 0; v < 4; v++)
+ mix->send_routing[0][4+v] = 60+v;
+
+ memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+ mix->send_volume[0][0] = 255;
+
+ mix->attn[0] = 0xffff;
+ }
+
+ if (! emu->APS) { /* FIXME: APS has these controls? */
+ /* sb live! and audigy */
+ if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ }
+
+ if (emu->audigy) {
+ if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ } else if (! emu->APS) {
+ /* sb live! */
+ if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ }
+ if (emu->audigy && emu->revision == 4) { /* P16V */
+ if ((err = snd_p16v_mixer(emu)))
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
new file mode 100644
index 00000000000..eb57458a966
--- /dev/null
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Routines for control of EMU10K1 MPU-401 in UART mode
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define EMU10K1_MIDI_MODE_INPUT (1<<0)
+#define EMU10K1_MIDI_MODE_OUTPUT (1<<1)
+
+static inline unsigned char mpu401_read(emu10k1_t *emu, emu10k1_midi_t *mpu, int idx)
+{
+ if (emu->audigy)
+ return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
+ else
+ return inb(emu->port + mpu->port + idx);
+}
+
+static inline void mpu401_write(emu10k1_t *emu, emu10k1_midi_t *mpu, int data, int idx)
+{
+ if (emu->audigy)
+ snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
+ else
+ outb(data, emu->port + mpu->port + idx);
+}
+
+#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET 0xff
+#define MPU401_ENTER_UART 0x3f
+#define MPU401_ACK 0xfe
+
+static void mpu401_clear_rx(emu10k1_t *emu, emu10k1_midi_t *mpu)
+{
+ int timeout = 100000;
+ for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+ mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+ if (timeout <= 0)
+ snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+ */
+
+static void do_emu10k1_midi_interrupt(emu10k1_t *emu, emu10k1_midi_t *midi, unsigned int status)
+{
+ unsigned char byte;
+
+ if (midi->rmidi == NULL) {
+ snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+ return;
+ }
+
+ spin_lock(&midi->input_lock);
+ if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+ mpu401_clear_rx(emu, midi);
+ } else {
+ byte = mpu401_read_data(emu, midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
+ }
+ spin_unlock(&midi->input_lock);
+
+ spin_lock(&midi->output_lock);
+ if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ mpu401_write_data(emu, midi, byte);
+ } else {
+ snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ }
+ }
+ spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1_midi_interrupt(emu10k1_t *emu, unsigned int status)
+{
+ do_emu10k1_midi_interrupt(emu, &emu->midi, status);
+}
+
+static void snd_emu10k1_midi_interrupt2(emu10k1_t *emu, unsigned int status)
+{
+ do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
+}
+
+static void snd_emu10k1_midi_cmd(emu10k1_t * emu, emu10k1_midi_t *midi, unsigned char cmd, int ack)
+{
+ unsigned long flags;
+ int timeout, ok;
+
+ spin_lock_irqsave(&midi->input_lock, flags);
+ mpu401_write_data(emu, midi, 0x00);
+ /* mpu401_clear_rx(emu, midi); */
+
+ mpu401_write_cmd(emu, midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (mpu401_input_avail(emu, midi)) {
+ if (mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ }
+ }
+ if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ } else {
+ ok = 1;
+ }
+ spin_unlock_irqrestore(&midi->input_lock, flags);
+ if (!ok)
+ snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+ cmd, emu->port,
+ mpu401_read_stat(emu, midi),
+ mpu401_read_data(emu, midi));
+}
+
+static int snd_emu10k1_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+ emu10k1_t *emu;
+ emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1);
+ snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+ emu10k1_t *emu;
+ emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1);
+ snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+ emu10k1_t *emu;
+ emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ snd_emu10k1_intr_disable(emu, midi->rx_enable);
+ midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+ emu10k1_t *emu;
+ emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return -ENXIO);
+ spin_lock_irqsave(&midi->open_lock, flags);
+ snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+ } else {
+ spin_unlock_irqrestore(&midi->open_lock, flags);
+ }
+ return 0;
+}
+
+static void snd_emu10k1_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ emu10k1_t *emu;
+ emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+ emu = midi->emu;
+ snd_assert(emu, return);
+
+ if (up)
+ snd_emu10k1_intr_enable(emu, midi->rx_enable);
+ else
+ snd_emu10k1_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ emu10k1_t *emu;
+ emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+ unsigned long flags;
+
+ emu = midi->emu;
+ snd_assert(emu, return);
+
+ if (up) {
+ int max = 4;
+ unsigned char byte;
+
+ /* try to send some amount of bytes here before interrupts */
+ spin_lock_irqsave(&midi->output_lock, flags);
+ while (max > 0) {
+ if (mpu401_output_ready(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ spin_unlock_irqrestore(&midi->output_lock, flags);
+ return;
+ }
+ mpu401_write_data(emu, midi, byte);
+ max--;
+ } else {
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&midi->output_lock, flags);
+ snd_emu10k1_intr_enable(emu, midi->tx_enable);
+ } else {
+ snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ }
+}
+
+/*
+
+ */
+
+static snd_rawmidi_ops_t snd_emu10k1_midi_output =
+{
+ .open = snd_emu10k1_midi_output_open,
+ .close = snd_emu10k1_midi_output_close,
+ .trigger = snd_emu10k1_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_emu10k1_midi_input =
+{
+ .open = snd_emu10k1_midi_input_open,
+ .close = snd_emu10k1_midi_input_close,
+ .trigger = snd_emu10k1_midi_input_trigger,
+};
+
+static void snd_emu10k1_midi_free(snd_rawmidi_t *rmidi)
+{
+ emu10k1_midi_t *midi = (emu10k1_midi_t *)rmidi->private_data;
+ midi->interrupt = NULL;
+ midi->rmidi = NULL;
+}
+
+static int __devinit emu10k1_midi_init(emu10k1_t *emu, emu10k1_midi_t *midi, int device, char *name)
+{
+ snd_rawmidi_t *rmidi;
+ int err;
+
+ if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+ return err;
+ midi->emu = emu;
+ spin_lock_init(&midi->open_lock);
+ spin_lock_init(&midi->input_lock);
+ spin_lock_init(&midi->output_lock);
+ strcpy(rmidi->name, name);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = midi;
+ rmidi->private_free = snd_emu10k1_midi_free;
+ midi->rmidi = rmidi;
+ return 0;
+}
+
+int __devinit snd_emu10k1_midi(emu10k1_t *emu)
+{
+ emu10k1_midi_t *midi = &emu->midi;
+ int err;
+
+ if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
+ return err;
+
+ midi->tx_enable = INTE_MIDITXENABLE;
+ midi->rx_enable = INTE_MIDIRXENABLE;
+ midi->port = MUDATA;
+ midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+ midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+ midi->interrupt = snd_emu10k1_midi_interrupt;
+ return 0;
+}
+
+int __devinit snd_emu10k1_audigy_midi(emu10k1_t *emu)
+{
+ emu10k1_midi_t *midi;
+ int err;
+
+ midi = &emu->midi;
+ if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
+ return err;
+
+ midi->tx_enable = INTE_MIDITXENABLE;
+ midi->rx_enable = INTE_MIDIRXENABLE;
+ midi->port = A_MUDATA1;
+ midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+ midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+ midi->interrupt = snd_emu10k1_midi_interrupt;
+
+ midi = &emu->midi2;
+ if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
+ return err;
+
+ midi->tx_enable = INTE_A_MIDITXENABLE2;
+ midi->rx_enable = INTE_A_MIDIRXENABLE2;
+ midi->port = A_MUDATA2;
+ midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
+ midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
+ midi->interrupt = snd_emu10k1_midi_interrupt2;
+ return 0;
+}
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
new file mode 100644
index 00000000000..d1c2a02c486
--- /dev/null
+++ b/sound/pci/emu10k1/emupcm.c
@@ -0,0 +1,1724 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Creative Labs, Inc.
+ * Routines for control of EMU10K1 chips / PCM routines
+ * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice)
+{
+ emu10k1_pcm_t *epcm;
+
+ if ((epcm = voice->epcm) == NULL)
+ return;
+ if (epcm->substream == NULL)
+ return;
+#if 0
+ printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ epcm->substream->runtime->hw->pointer(emu, epcm->substream),
+ snd_pcm_lib_period_bytes(epcm->substream),
+ snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+ snd_pcm_period_elapsed(epcm->substream);
+}
+
+static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status)
+{
+#if 0
+ if (status & IPR_ADCBUFHALFFULL) {
+ if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+ return;
+ }
+#endif
+ snd_pcm_period_elapsed(emu->pcm_capture_substream);
+}
+
+static void snd_emu10k1_pcm_ac97mic_interrupt(emu10k1_t *emu, unsigned int status)
+{
+#if 0
+ if (status & IPR_MICBUFHALFFULL) {
+ if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+ return;
+ }
+#endif
+ snd_pcm_period_elapsed(emu->pcm_capture_mic_substream);
+}
+
+static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status)
+{
+#if 0
+ if (status & IPR_EFXBUFHALFFULL) {
+ if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+ return;
+ }
+#endif
+ snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
+}
+
+static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ unsigned int ptr;
+
+ if (!epcm->running)
+ return 0;
+ ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+ ptr += runtime->buffer_size;
+ ptr -= epcm->ccca_start_addr;
+ ptr %= runtime->buffer_size;
+
+ return ptr;
+}
+
+static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices)
+{
+ int err, i;
+
+ if (epcm->voices[1] != NULL && voices < 2) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+ epcm->voices[1] = NULL;
+ }
+ for (i = 0; i < voices; i++) {
+ if (epcm->voices[i] == NULL)
+ break;
+ }
+ if (i == voices)
+ return 0; /* already allocated */
+
+ for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
+ if (epcm->voices[i]) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+ epcm->voices[i] = NULL;
+ }
+ }
+ err = snd_emu10k1_voice_alloc(epcm->emu,
+ epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
+ voices,
+ &epcm->voices[0]);
+
+ if (err < 0)
+ return err;
+ epcm->voices[0]->epcm = epcm;
+ if (voices > 1) {
+ for (i = 1; i < voices; i++) {
+ epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i];
+ epcm->voices[i]->epcm = epcm;
+ }
+ }
+ if (epcm->extra == NULL) {
+ err = snd_emu10k1_voice_alloc(epcm->emu,
+ epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
+ 1,
+ &epcm->extra);
+ if (err < 0) {
+ // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame);
+ for (i = 0; i < voices; i++) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+ epcm->voices[i] = NULL;
+ }
+ return err;
+ }
+ epcm->extra->epcm = epcm;
+ epcm->extra->interrupt = snd_emu10k1_pcm_interrupt;
+ }
+ return 0;
+}
+
+static unsigned int capture_period_sizes[31] = {
+ 384, 448, 512, 640,
+ 384*2, 448*2, 512*2, 640*2,
+ 384*4, 448*4, 512*4, 640*4,
+ 384*8, 448*8, 512*8, 640*8,
+ 384*16, 448*16, 512*16, 640*16,
+ 384*32, 448*32, 512*32, 640*32,
+ 384*64, 448*64, 512*64, 640*64,
+ 384*128,448*128,512*128
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = {
+ .count = 31,
+ .list = capture_period_sizes,
+ .mask = 0
+};
+
+static unsigned int capture_rates[8] = {
+ 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = {
+ .count = 8,
+ .list = capture_rates,
+ .mask = 0
+};
+
+static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate)
+{
+ switch (rate) {
+ case 8000: return ADCCR_SAMPLERATE_8;
+ case 11025: return ADCCR_SAMPLERATE_11;
+ case 16000: return ADCCR_SAMPLERATE_16;
+ case 22050: return ADCCR_SAMPLERATE_22;
+ case 24000: return ADCCR_SAMPLERATE_24;
+ case 32000: return ADCCR_SAMPLERATE_32;
+ case 44100: return ADCCR_SAMPLERATE_44;
+ case 48000: return ADCCR_SAMPLERATE_48;
+ default:
+ snd_BUG();
+ return ADCCR_SAMPLERATE_8;
+ }
+}
+
+static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
+{
+ switch (rate) {
+ case 8000: return A_ADCCR_SAMPLERATE_8;
+ case 11025: return A_ADCCR_SAMPLERATE_11;
+ case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */
+ case 16000: return ADCCR_SAMPLERATE_16;
+ case 22050: return ADCCR_SAMPLERATE_22;
+ case 24000: return ADCCR_SAMPLERATE_24;
+ case 32000: return ADCCR_SAMPLERATE_32;
+ case 44100: return ADCCR_SAMPLERATE_44;
+ case 48000: return ADCCR_SAMPLERATE_48;
+ default:
+ snd_BUG();
+ return A_ADCCR_SAMPLERATE_8;
+ }
+}
+
+static unsigned int emu10k1_calc_pitch_target(unsigned int rate)
+{
+ unsigned int pitch_target;
+
+ pitch_target = (rate << 8) / 375;
+ pitch_target = (pitch_target >> 1) + (pitch_target & 1);
+ return pitch_target;
+}
+
+#define PITCH_48000 0x00004000
+#define PITCH_96000 0x00008000
+#define PITCH_85000 0x00007155
+#define PITCH_80726 0x00006ba2
+#define PITCH_67882 0x00005a82
+#define PITCH_57081 0x00004c1c
+
+static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
+{
+ if (pitch_target == PITCH_48000)
+ return CCCA_INTERPROM_0;
+ else if (pitch_target < PITCH_48000)
+ return CCCA_INTERPROM_1;
+ else if (pitch_target >= PITCH_96000)
+ return CCCA_INTERPROM_0;
+ else if (pitch_target >= PITCH_85000)
+ return CCCA_INTERPROM_6;
+ else if (pitch_target >= PITCH_80726)
+ return CCCA_INTERPROM_5;
+ else if (pitch_target >= PITCH_67882)
+ return CCCA_INTERPROM_4;
+ else if (pitch_target >= PITCH_57081)
+ return CCCA_INTERPROM_3;
+ else
+ return CCCA_INTERPROM_2;
+}
+
+/*
+ * calculate cache invalidate size
+ *
+ * stereo: channel is stereo
+ * w_16: using 16bit samples
+ *
+ * returns: cache invalidate size in samples
+ */
+static int inline emu10k1_ccis(int stereo, int w_16)
+{
+ if (w_16) {
+ return stereo ? 24 : 26;
+ } else {
+ return stereo ? 24*2 : 26*2;
+ }
+}
+
+static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
+ int master, int extra,
+ emu10k1_voice_t *evoice,
+ unsigned int start_addr,
+ unsigned int end_addr,
+ emu10k1_pcm_mixer_t *mix)
+{
+ snd_pcm_substream_t *substream = evoice->epcm->substream;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int silent_page, tmp;
+ int voice, stereo, w_16;
+ unsigned char attn, send_amount[8];
+ unsigned char send_routing[8];
+ unsigned long flags;
+ unsigned int pitch_target;
+ unsigned int ccis;
+
+ voice = evoice->number;
+ stereo = runtime->channels == 2;
+ w_16 = snd_pcm_format_width(runtime->format) == 16;
+
+ if (!extra && stereo) {
+ start_addr >>= 1;
+ end_addr >>= 1;
+ }
+ if (w_16) {
+ start_addr >>= 1;
+ end_addr >>= 1;
+ }
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+
+ /* volume parameters */
+ if (extra) {
+ attn = 0;
+ memset(send_routing, 0, sizeof(send_routing));
+ send_routing[0] = 0;
+ send_routing[1] = 1;
+ send_routing[2] = 2;
+ send_routing[3] = 3;
+ memset(send_amount, 0, sizeof(send_amount));
+ } else {
+ /* mono, left, right (master voice = left) */
+ tmp = stereo ? (master ? 1 : 2) : 0;
+ memcpy(send_routing, &mix->send_routing[tmp][0], 8);
+ memcpy(send_amount, &mix->send_volume[tmp][0], 8);
+ }
+
+ ccis = emu10k1_ccis(stereo, w_16);
+
+ if (master) {
+ evoice->epcm->ccca_start_addr = start_addr + ccis;
+ if (extra) {
+ start_addr += ccis;
+ end_addr += ccis;
+ }
+ if (stereo && !extra) {
+ snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
+ snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK);
+ } else {
+ snd_emu10k1_ptr_write(emu, CPF, voice, 0);
+ }
+ }
+
+ // setup routing
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+ snd_emu10k1_compose_audigy_fxrt1(send_routing));
+ snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+ snd_emu10k1_compose_audigy_fxrt2(send_routing));
+ snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
+ ((unsigned int)send_amount[4] << 24) |
+ ((unsigned int)send_amount[5] << 16) |
+ ((unsigned int)send_amount[6] << 8) |
+ (unsigned int)send_amount[7]);
+ } else
+ snd_emu10k1_ptr_write(emu, FXRT, voice,
+ snd_emu10k1_compose_send_routing(send_routing));
+ // Stop CA
+ // Assumption that PT is already 0 so no harm overwriting
+ snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
+ snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
+ snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
+ pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+ if (extra)
+ snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr |
+ emu10k1_select_interprom(pitch_target) |
+ (w_16 ? 0 : CCCA_8BITSELECT));
+ else
+ snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
+ emu10k1_select_interprom(pitch_target) |
+ (w_16 ? 0 : CCCA_8BITSELECT));
+ // Clear filter delay memory
+ snd_emu10k1_ptr_write(emu, Z1, voice, 0);
+ snd_emu10k1_ptr_write(emu, Z2, voice, 0);
+ // invalidate maps
+ silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK;
+ snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page);
+ snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page);
+ // modulation envelope
+ snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+ snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+ snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
+ snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
+ snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
+ snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000);
+ snd_emu10k1_ptr_write(emu, FMMOD, voice, 0);
+ snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0);
+ snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0);
+ snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000);
+ // volume envelope
+ snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f);
+ snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000);
+ // filter envelope
+ snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f);
+ // pitch envelope
+ snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0);
+
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ int err;
+
+ if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
+ return err;
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ if (err > 0) { /* change */
+ snd_util_memblk_t *memblk;
+ if (epcm->memblk != NULL)
+ snd_emu10k1_free_pages(emu, epcm->memblk);
+ memblk = snd_emu10k1_alloc_pages(emu, substream);
+ if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) {
+ epcm->start_addr = 0;
+ return -ENOMEM;
+ }
+ epcm->start_addr = ((emu10k1_memblk_t *)memblk)->mapped_page << PAGE_SHIFT;
+ }
+ return 0;
+}
+
+static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm;
+
+ if (runtime->private_data == NULL)
+ return 0;
+ epcm = runtime->private_data;
+ if (epcm->extra) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->extra);
+ epcm->extra = NULL;
+ }
+ if (epcm->voices[1]) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+ epcm->voices[1] = NULL;
+ }
+ if (epcm->voices[0]) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
+ epcm->voices[0] = NULL;
+ }
+ if (epcm->memblk) {
+ snd_emu10k1_free_pages(emu, epcm->memblk);
+ epcm->memblk = NULL;
+ epcm->start_addr = 0;
+ }
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_emu10k1_efx_playback_hw_free(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm;
+ int i;
+
+ if (runtime->private_data == NULL)
+ return 0;
+ epcm = runtime->private_data;
+ if (epcm->extra) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->extra);
+ epcm->extra = NULL;
+ }
+ for (i=0; i < NUM_EFX_PLAYBACK; i++) {
+ if (epcm->voices[i]) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+ epcm->voices[i] = NULL;
+ }
+ }
+ if (epcm->memblk) {
+ snd_emu10k1_free_pages(emu, epcm->memblk);
+ epcm->memblk = NULL;
+ epcm->start_addr = 0;
+ }
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ unsigned int start_addr, end_addr;
+
+ start_addr = epcm->start_addr;
+ end_addr = snd_pcm_lib_period_bytes(substream);
+ if (runtime->channels == 2) {
+ start_addr >>= 1;
+ end_addr >>= 1;
+ }
+ end_addr += start_addr;
+ snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
+ start_addr, end_addr, NULL);
+ start_addr = epcm->start_addr;
+ end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+ snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
+ start_addr, end_addr,
+ &emu->pcm_mixer[substream->number]);
+ if (epcm->voices[1])
+ snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1],
+ start_addr, end_addr,
+ &emu->pcm_mixer[substream->number]);
+ return 0;
+}
+
+static int snd_emu10k1_efx_playback_prepare(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ unsigned int start_addr, end_addr;
+ unsigned int channel_size;
+ int i;
+
+ start_addr = epcm->start_addr;
+ end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+
+ /*
+ * the kX driver leaves some space between voices
+ */
+ channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
+
+ snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
+ start_addr, start_addr + (channel_size / 2), NULL);
+
+ /* only difference with the master voice is we use it for the pointer */
+ snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
+ start_addr, start_addr + channel_size,
+ &emu->efx_pcm_mixer[0]);
+
+ start_addr += channel_size;
+ for (i = 1; i < NUM_EFX_PLAYBACK; i++) {
+ snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i],
+ start_addr, start_addr + channel_size,
+ &emu->efx_pcm_mixer[i]);
+ start_addr += channel_size;
+ }
+
+ return 0;
+}
+
+static snd_pcm_hardware_t snd_emu10k1_efx_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = NUM_EFX_PLAYBACK,
+ .channels_max = NUM_EFX_PLAYBACK,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (64*1024),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_capture_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ int idx;
+
+ /* zeroing the buffer size will stop capture */
+ snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+ switch (epcm->type) {
+ case CAPTURE_AC97ADC:
+ snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+ break;
+ case CAPTURE_EFX:
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
+ snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+ } else
+ snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+ break;
+ default:
+ break;
+ }
+ snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr);
+ epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream);
+ epcm->capture_bs_val = 0;
+ for (idx = 0; idx < 31; idx++) {
+ if (capture_period_sizes[idx] == epcm->capture_bufsize) {
+ epcm->capture_bs_val = idx + 1;
+ break;
+ }
+ }
+ if (epcm->capture_bs_val == 0) {
+ snd_BUG();
+ epcm->capture_bs_val++;
+ }
+ if (epcm->type == CAPTURE_AC97ADC) {
+ epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE;
+ if (runtime->channels > 1)
+ epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE;
+ epcm->capture_cr_val |= emu->audigy ?
+ snd_emu10k1_audigy_capture_rate_reg(runtime->rate) :
+ snd_emu10k1_capture_rate_reg(runtime->rate);
+ }
+ return 0;
+}
+
+static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, int extra, emu10k1_voice_t *evoice)
+{
+ snd_pcm_runtime_t *runtime;
+ unsigned int voice, stereo, i, ccis, cra = 64, cs, sample;
+
+ if (evoice == NULL)
+ return;
+ runtime = evoice->epcm->substream->runtime;
+ voice = evoice->number;
+ stereo = (!extra && runtime->channels == 2);
+ sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
+ ccis = emu10k1_ccis(stereo, sample == 0);
+ // set cs to 2 * number of cache registers beside the invalidated
+ cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
+ if (cs > 16) cs = 16;
+ for (i = 0; i < cs; i++) {
+ snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
+ if (stereo) {
+ snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample);
+ }
+ }
+ // reset cache
+ snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
+ snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
+ if (stereo) {
+ snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
+ snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
+ }
+ // fill cache
+ snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
+ if (stereo) {
+ snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
+ }
+}
+
+static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t *evoice,
+ int master, int extra,
+ emu10k1_pcm_mixer_t *mix)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ unsigned int attn, vattn;
+ unsigned int voice, tmp;
+
+ if (evoice == NULL) /* skip second voice for mono */
+ return;
+ substream = evoice->epcm->substream;
+ runtime = substream->runtime;
+ voice = evoice->number;
+
+ attn = extra ? 0 : 0x00ff;
+ tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
+ vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0;
+ snd_emu10k1_ptr_write(emu, IFATN, voice, attn);
+ snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff);
+ snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff);
+ snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
+ snd_emu10k1_voice_clear_loop_stop(emu, voice);
+}
+
+static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ unsigned int voice, pitch, pitch_target;
+
+ if (evoice == NULL) /* skip second voice for mono */
+ return;
+ substream = evoice->epcm->substream;
+ runtime = substream->runtime;
+ voice = evoice->number;
+
+ pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
+ pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+ snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
+ if (master || evoice->epcm->type == PLAYBACK_EFX)
+ snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target);
+ snd_emu10k1_ptr_write(emu, IP, voice, pitch);
+ if (extra)
+ snd_emu10k1_voice_intr_enable(emu, voice);
+}
+
+static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice)
+{
+ unsigned int voice;
+
+ if (evoice == NULL)
+ return;
+ voice = evoice->number;
+ snd_emu10k1_voice_intr_disable(emu, voice);
+ snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0);
+ snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0);
+ snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff);
+ snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+ snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+ snd_emu10k1_ptr_write(emu, IP, voice, 0);
+}
+
+static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ emu10k1_pcm_mixer_t *mix;
+ int result = 0;
+
+ // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream));
+ spin_lock(&emu->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* do we need this? */
+ snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]);
+ /* follow thru */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mix = &emu->pcm_mixer[substream->number];
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
+ snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+ epcm->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ epcm->running = 0;
+ snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]);
+ snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]);
+ snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ spin_unlock(&emu->reg_lock);
+ return result;
+}
+
+static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ int result = 0;
+
+ spin_lock(&emu->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ // hmm this should cause full and half full interrupt to be raised?
+ outl(epcm->capture_ipr, emu->port + IPR);
+ snd_emu10k1_intr_enable(emu, epcm->capture_inte);
+ // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs);
+ switch (epcm->type) {
+ case CAPTURE_AC97ADC:
+ snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val);
+ break;
+ case CAPTURE_EFX:
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val);
+ snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2);
+ } else
+ snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val);
+ break;
+ default:
+ break;
+ }
+ snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val);
+ epcm->running = 1;
+ epcm->first_ptr = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ epcm->running = 0;
+ snd_emu10k1_intr_disable(emu, epcm->capture_inte);
+ outl(epcm->capture_ipr, emu->port + IPR);
+ snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+ switch (epcm->type) {
+ case CAPTURE_AC97ADC:
+ snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+ break;
+ case CAPTURE_EFX:
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
+ snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+ } else
+ snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ result = -EINVAL;
+ }
+ spin_unlock(&emu->reg_lock);
+ return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ unsigned int ptr;
+
+ if (!epcm->running)
+ return 0;
+ ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+#if 0 /* Perex's code */
+ ptr += runtime->buffer_size;
+ ptr -= epcm->ccca_start_addr;
+ ptr %= runtime->buffer_size;
+#else /* EMU10K1 Open Source code from Creative */
+ if (ptr < epcm->ccca_start_addr)
+ ptr += runtime->buffer_size - epcm->ccca_start_addr;
+ else {
+ ptr -= epcm->ccca_start_addr;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ }
+#endif
+ // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size);
+ return ptr;
+}
+
+
+static int snd_emu10k1_efx_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ int i;
+ int result = 0;
+
+ spin_lock(&emu->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ // prepare voices
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+ snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
+ }
+ snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
+
+ /* follow thru */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0,
+ &emu->efx_pcm_mixer[0]);
+ for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0,
+ &emu->efx_pcm_mixer[i]);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+ for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
+ epcm->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ epcm->running = 0;
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+ snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
+ }
+ snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ spin_unlock(&emu->reg_lock);
+ return result;
+}
+
+
+static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ unsigned int ptr;
+
+ if (!epcm->running)
+ return 0;
+ if (epcm->first_ptr) {
+ udelay(50); // hack, it takes awhile until capture is started
+ epcm->first_ptr = 0;
+ }
+ ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff;
+ return bytes_to_frames(runtime, ptr);
+}
+
+/*
+ * Playback support device description
+ */
+
+static snd_pcm_hardware_t snd_emu10k1_playback =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 4000,
+ .rate_max = 96000,
+ .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,
+};
+
+/*
+ * Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_emu10k1_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 384,
+ .period_bytes_max = (64*1024),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+/*
+ *
+ */
+
+static void snd_emu10k1_pcm_mixer_notify1(emu10k1_t *emu, snd_kcontrol_t *kctl, int idx, int activate)
+{
+ snd_ctl_elem_id_t id;
+
+ snd_runtime_check(kctl != NULL, return);
+ if (activate)
+ kctl->vd[idx].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ else
+ kctl->vd[idx].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ snd_ctl_build_ioff(&id, kctl, idx));
+}
+
+static void snd_emu10k1_pcm_mixer_notify(emu10k1_t *emu, int idx, int activate)
+{
+ snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_routing, idx, activate);
+ snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_volume, idx, activate);
+ snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate);
+}
+
+static void snd_emu10k1_pcm_efx_mixer_notify(emu10k1_t *emu, int idx, int activate)
+{
+ snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_routing, idx, activate);
+ snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_volume, idx, activate);
+ snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate);
+}
+
+static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ emu10k1_pcm_t *epcm = runtime->private_data;
+
+ kfree(epcm);
+}
+
+static int snd_emu10k1_efx_playback_close(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ emu10k1_pcm_mixer_t *mix;
+ int i;
+
+ for (i=0; i < NUM_EFX_PLAYBACK; i++) {
+ mix = &emu->efx_pcm_mixer[i];
+ mix->epcm = NULL;
+ snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_efx_playback_open(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ emu10k1_pcm_t *epcm;
+ emu10k1_pcm_mixer_t *mix;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int i;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = emu;
+ epcm->type = PLAYBACK_EFX;
+ epcm->substream = substream;
+
+ emu->pcm_playback_efx_substream = substream;
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_emu10k1_pcm_free_substream;
+ runtime->hw = snd_emu10k1_efx_playback;
+
+ for (i=0; i < NUM_EFX_PLAYBACK; i++) {
+ mix = &emu->efx_pcm_mixer[i];
+ mix->send_routing[0][0] = i;
+ memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+ mix->send_volume[0][0] = 255;
+ mix->attn[0] = 0xffff;
+ mix->epcm = epcm;
+ snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1);
+ }
+ return 0;
+}
+
+static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ emu10k1_pcm_t *epcm;
+ emu10k1_pcm_mixer_t *mix;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int i, err;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = emu;
+ epcm->type = PLAYBACK_EMUVOICE;
+ epcm->substream = substream;
+ runtime->private_data = epcm;
+ runtime->private_free = snd_emu10k1_pcm_free_substream;
+ runtime->hw = snd_emu10k1_playback;
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+ kfree(epcm);
+ return err;
+ }
+ if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) {
+ kfree(epcm);
+ return err;
+ }
+ mix = &emu->pcm_mixer[substream->number];
+ for (i = 0; i < 4; i++)
+ mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i;
+ memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+ mix->send_volume[0][0] = mix->send_volume[0][1] =
+ mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+ mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+ mix->epcm = epcm;
+ snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1);
+ return 0;
+}
+
+static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number];
+
+ mix->epcm = NULL;
+ snd_emu10k1_pcm_mixer_notify(emu, substream->number, 0);
+ return 0;
+}
+
+static int snd_emu10k1_capture_open(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = emu;
+ epcm->type = CAPTURE_AC97ADC;
+ epcm->substream = substream;
+ epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL;
+ epcm->capture_inte = INTE_ADCBUFENABLE;
+ epcm->capture_ba_reg = ADCBA;
+ epcm->capture_bs_reg = ADCBS;
+ epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX;
+ runtime->private_data = epcm;
+ runtime->private_free = snd_emu10k1_pcm_free_substream;
+ runtime->hw = snd_emu10k1_capture;
+ emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt;
+ emu->pcm_capture_substream = substream;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates);
+ return 0;
+}
+
+static int snd_emu10k1_capture_close(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+
+ emu->capture_interrupt = NULL;
+ emu->pcm_capture_substream = NULL;
+ return 0;
+}
+
+static int snd_emu10k1_capture_mic_open(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ emu10k1_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = emu;
+ epcm->type = CAPTURE_AC97MIC;
+ epcm->substream = substream;
+ epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL;
+ epcm->capture_inte = INTE_MICBUFENABLE;
+ epcm->capture_ba_reg = MICBA;
+ epcm->capture_bs_reg = MICBS;
+ epcm->capture_idx_reg = emu->audigy ? A_MICIDX : MICIDX;
+ substream->runtime->private_data = epcm;
+ substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+ runtime->hw = snd_emu10k1_capture;
+ runtime->hw.rates = SNDRV_PCM_RATE_8000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 8000;
+ runtime->hw.channels_min = 1;
+ emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt;
+ emu->pcm_capture_mic_substream = substream;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+ return 0;
+}
+
+static int snd_emu10k1_capture_mic_close(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+
+ emu->capture_interrupt = NULL;
+ emu->pcm_capture_mic_substream = NULL;
+ return 0;
+}
+
+static int snd_emu10k1_capture_efx_open(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ emu10k1_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int nefx = emu->audigy ? 64 : 32;
+ int idx;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = emu;
+ epcm->type = CAPTURE_EFX;
+ epcm->substream = substream;
+ epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL;
+ epcm->capture_inte = INTE_EFXBUFENABLE;
+ epcm->capture_ba_reg = FXBA;
+ epcm->capture_bs_reg = FXBS;
+ epcm->capture_idx_reg = FXIDX;
+ substream->runtime->private_data = epcm;
+ substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+ runtime->hw = snd_emu10k1_capture;
+ runtime->hw.rates = SNDRV_PCM_RATE_48000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+ spin_lock_irq(&emu->reg_lock);
+ runtime->hw.channels_min = runtime->hw.channels_max = 0;
+ for (idx = 0; idx < nefx; idx++) {
+ if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) {
+ runtime->hw.channels_min++;
+ runtime->hw.channels_max++;
+ }
+ }
+ epcm->capture_cr_val = emu->efx_voices_mask[0];
+ epcm->capture_cr_val2 = emu->efx_voices_mask[1];
+ spin_unlock_irq(&emu->reg_lock);
+ emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
+ emu->pcm_capture_efx_substream = substream;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+ return 0;
+}
+
+static int snd_emu10k1_capture_efx_close(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+
+ emu->capture_interrupt = NULL;
+ emu->pcm_capture_efx_substream = NULL;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_emu10k1_playback_ops = {
+ .open = snd_emu10k1_playback_open,
+ .close = snd_emu10k1_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1_playback_hw_params,
+ .hw_free = snd_emu10k1_playback_hw_free,
+ .prepare = snd_emu10k1_playback_prepare,
+ .trigger = snd_emu10k1_playback_trigger,
+ .pointer = snd_emu10k1_playback_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static snd_pcm_ops_t snd_emu10k1_capture_ops = {
+ .open = snd_emu10k1_capture_open,
+ .close = snd_emu10k1_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1_capture_hw_params,
+ .hw_free = snd_emu10k1_capture_hw_free,
+ .prepare = snd_emu10k1_capture_prepare,
+ .trigger = snd_emu10k1_capture_trigger,
+ .pointer = snd_emu10k1_capture_pointer,
+};
+
+/* EFX playback */
+static snd_pcm_ops_t snd_emu10k1_efx_playback_ops = {
+ .open = snd_emu10k1_efx_playback_open,
+ .close = snd_emu10k1_efx_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1_playback_hw_params,
+ .hw_free = snd_emu10k1_efx_playback_hw_free,
+ .prepare = snd_emu10k1_efx_playback_prepare,
+ .trigger = snd_emu10k1_efx_playback_trigger,
+ .pointer = snd_emu10k1_efx_playback_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static void snd_emu10k1_pcm_free(snd_pcm_t *pcm)
+{
+ emu10k1_t *emu = pcm->private_data;
+ emu->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+
+ if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = emu;
+ pcm->private_free = snd_emu10k1_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops);
+
+ pcm->info_flags = 0;
+ pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+ strcpy(pcm->name, "ADC Capture/Standard PCM Playback");
+ emu->pcm = pcm;
+
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+ if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
+ return err;
+
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next)
+ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+int __devinit snd_emu10k1_pcm_multi(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+
+ if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = emu;
+ pcm->private_free = snd_emu10k1_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops);
+
+ pcm->info_flags = 0;
+ pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+ strcpy(pcm->name, "Multichannel Playback");
+ emu->pcm = pcm;
+
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+ if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
+ return err;
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+
+static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = {
+ .open = snd_emu10k1_capture_mic_open,
+ .close = snd_emu10k1_capture_mic_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1_capture_hw_params,
+ .hw_free = snd_emu10k1_capture_hw_free,
+ .prepare = snd_emu10k1_capture_prepare,
+ .trigger = snd_emu10k1_capture_trigger,
+ .pointer = snd_emu10k1_capture_pointer,
+};
+
+static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm)
+{
+ emu10k1_t *emu = pcm->private_data;
+ emu->pcm_mic = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+
+ if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = emu;
+ pcm->private_free = snd_emu10k1_pcm_mic_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops);
+
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "Mic Capture");
+ emu->pcm_mic = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ int nefx = emu->audigy ? 64 : 32;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = nefx;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ int nefx = emu->audigy ? 64 : 32;
+ int idx;
+
+ spin_lock_irq(&emu->reg_lock);
+ for (idx = 0; idx < nefx; idx++)
+ ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0;
+ spin_unlock_irq(&emu->reg_lock);
+ return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int nval[2], bits;
+ int nefx = emu->audigy ? 64 : 32;
+ int nefxb = emu->audigy ? 7 : 6;
+ int change, idx;
+
+ nval[0] = nval[1] = 0;
+ for (idx = 0, bits = 0; idx < nefx; idx++)
+ if (ucontrol->value.integer.value[idx]) {
+ nval[idx / 32] |= 1 << (idx % 32);
+ bits++;
+ }
+
+ for (idx = 0; idx < nefxb; idx++)
+ if (1 << idx == bits)
+ break;
+
+ if (idx >= nefxb)
+ return -EINVAL;
+
+ spin_lock_irq(&emu->reg_lock);
+ change = (nval[0] != emu->efx_voices_mask[0]) ||
+ (nval[1] != emu->efx_voices_mask[1]);
+ emu->efx_voices_mask[0] = nval[0];
+ emu->efx_voices_mask[1] = nval[1];
+ spin_unlock_irq(&emu->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Captured FX8010 Outputs",
+ .info = snd_emu10k1_pcm_efx_voices_mask_info,
+ .get = snd_emu10k1_pcm_efx_voices_mask_get,
+ .put = snd_emu10k1_pcm_efx_voices_mask_put
+};
+
+static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = {
+ .open = snd_emu10k1_capture_efx_open,
+ .close = snd_emu10k1_capture_efx_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1_capture_hw_params,
+ .hw_free = snd_emu10k1_capture_hw_free,
+ .prepare = snd_emu10k1_capture_prepare,
+ .trigger = snd_emu10k1_capture_trigger,
+ .pointer = snd_emu10k1_capture_pointer,
+};
+
+
+/* EFX playback */
+
+#define INITIAL_TRAM_SHIFT 14
+#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1)
+
+static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data)
+{
+ snd_pcm_substream_t *substream = private_data;
+ snd_pcm_period_elapsed(substream);
+}
+
+static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left,
+ unsigned short *dst_right,
+ unsigned short *src,
+ unsigned int count,
+ unsigned int tram_shift)
+{
+ // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count);
+ if ((tram_shift & 1) == 0) {
+ while (count--) {
+ *dst_left-- = *src++;
+ *dst_right-- = *src++;
+ }
+ } else {
+ while (count--) {
+ *dst_right-- = *src++;
+ *dst_left-- = *src++;
+ }
+ }
+}
+
+static void fx8010_pb_trans_copy(snd_pcm_substream_t *substream,
+ snd_pcm_indirect_t *rec, size_t bytes)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+ unsigned int tram_size = pcm->buffer_size;
+ unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data);
+ unsigned int frames = bytes >> 2, count;
+ unsigned int tram_pos = pcm->tram_pos;
+ unsigned int tram_shift = pcm->tram_shift;
+
+ while (frames > tram_pos) {
+ count = tram_pos + 1;
+ snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos,
+ (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2,
+ src, count, tram_shift);
+ src += count * 2;
+ frames -= count;
+ tram_pos = (tram_size / 2) - 1;
+ tram_shift++;
+ }
+ snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos,
+ (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2,
+ src, frames, tram_shift);
+ tram_pos -= frames;
+ pcm->tram_pos = tram_pos;
+ pcm->tram_shift = tram_shift;
+}
+
+static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+
+ snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy);
+ return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+ unsigned int i;
+
+ for (i = 0; i < pcm->channels; i++)
+ snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0);
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+ unsigned int i;
+
+ // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2);
+ memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec));
+ pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */
+ pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+ pcm->tram_shift = 0;
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size);
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size);
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size);
+ for (i = 0; i < pcm->channels; i++)
+ snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels));
+ return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+ int result = 0;
+
+ spin_lock(&emu->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* follow thru */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+#ifdef EMU10K1_SET_AC3_IEC958
+ {
+ int i;
+ for (i = 0; i < 3; i++) {
+ unsigned int bits;
+ bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS |
+ 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA;
+ snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits);
+ }
+ }
+#endif
+ result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq);
+ if (result < 0)
+ goto __err;
+ snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
+ pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+ pcm->tram_shift = 0;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ __err:
+ spin_unlock(&emu->reg_lock);
+ return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+ size_t ptr; /* byte pointer */
+
+ if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0))
+ return 0;
+ ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2;
+ return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr);
+}
+
+static snd_pcm_hardware_t snd_emu10k1_fx8010_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 1024,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+
+ runtime->hw = snd_emu10k1_fx8010_playback;
+ runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels;
+ runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2;
+ spin_lock_irq(&emu->reg_lock);
+ if (pcm->valid == 0) {
+ spin_unlock_irq(&emu->reg_lock);
+ return -ENODEV;
+ }
+ pcm->opened = 1;
+ spin_unlock_irq(&emu->reg_lock);
+ return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+
+ spin_lock_irq(&emu->reg_lock);
+ pcm->opened = 0;
+ spin_unlock_irq(&emu->reg_lock);
+ return 0;
+}
+
+static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = {
+ .open = snd_emu10k1_fx8010_playback_open,
+ .close = snd_emu10k1_fx8010_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1_fx8010_playback_hw_params,
+ .hw_free = snd_emu10k1_fx8010_playback_hw_free,
+ .prepare = snd_emu10k1_fx8010_playback_prepare,
+ .trigger = snd_emu10k1_fx8010_playback_trigger,
+ .pointer = snd_emu10k1_fx8010_playback_pointer,
+ .ack = snd_emu10k1_fx8010_playback_transfer,
+};
+
+static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm)
+{
+ emu10k1_t *emu = pcm->private_data;
+ emu->pcm_efx = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+
+ if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = emu;
+ pcm->private_free = snd_emu10k1_pcm_efx_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
+
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "Multichannel Capture/PT Playback");
+ emu->pcm_efx = pcm;
+ if (rpcm)
+ *rpcm = pcm;
+
+ /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs
+ * to these
+ */
+
+ /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
+ if (emu->audigy) {
+ emu->efx_voices_mask[0] = 0;
+ emu->efx_voices_mask[1] = 0xffff;
+ } else {
+ emu->efx_voices_mask[0] = 0xffff0000;
+ emu->efx_voices_mask[1] = 0;
+ }
+ snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu));
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+ return 0;
+}
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
new file mode 100644
index 00000000000..d990d5eb45a
--- /dev/null
+++ b/sound/pci/emu10k1/emuproc.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Creative Labs, Inc.
+ * Routines for control of EMU10K1 chips / proc interface routines
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu,
+ snd_info_buffer_t * buffer,
+ char *title,
+ int status_reg,
+ int rate_reg)
+{
+ static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
+ static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
+ static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
+ unsigned int status, rate = 0;
+
+ status = snd_emu10k1_ptr_read(emu, status_reg, 0);
+ if (rate_reg > 0)
+ rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
+
+ snd_iprintf(buffer, "\n%s\n", title);
+
+ snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
+ snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
+ snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+ snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
+ snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6);
+ snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
+ snd_iprintf(buffer, "Generation Status : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
+ snd_iprintf(buffer, "Source Mask : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
+ snd_iprintf(buffer, "Channel Number : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
+ snd_iprintf(buffer, "Sample Rate : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
+ snd_iprintf(buffer, "Clock Accuracy : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
+
+ if (rate_reg > 0) {
+ snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
+ snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+ snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE);
+ }
+}
+
+static void snd_emu10k1_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ /* FIXME - output names are in emufx.c too */
+ static char *creative_outs[32] = {
+ /* 00 */ "AC97 Left",
+ /* 01 */ "AC97 Right",
+ /* 02 */ "Optical IEC958 Left",
+ /* 03 */ "Optical IEC958 Right",
+ /* 04 */ "Center",
+ /* 05 */ "LFE",
+ /* 06 */ "Headphone Left",
+ /* 07 */ "Headphone Right",
+ /* 08 */ "Surround Left",
+ /* 09 */ "Surround Right",
+ /* 10 */ "PCM Capture Left",
+ /* 11 */ "PCM Capture Right",
+ /* 12 */ "MIC Capture",
+ /* 13 */ "AC97 Surround Left",
+ /* 14 */ "AC97 Surround Right",
+ /* 15 */ "???",
+ /* 16 */ "???",
+ /* 17 */ "Analog Center",
+ /* 18 */ "Analog LFE",
+ /* 19 */ "???",
+ /* 20 */ "???",
+ /* 21 */ "???",
+ /* 22 */ "???",
+ /* 23 */ "???",
+ /* 24 */ "???",
+ /* 25 */ "???",
+ /* 26 */ "???",
+ /* 27 */ "???",
+ /* 28 */ "???",
+ /* 29 */ "???",
+ /* 30 */ "???",
+ /* 31 */ "???"
+ };
+
+ static char *audigy_outs[64] = {
+ /* 00 */ "Digital Front Left",
+ /* 01 */ "Digital Front Right",
+ /* 02 */ "Digital Center",
+ /* 03 */ "Digital LEF",
+ /* 04 */ "Headphone Left",
+ /* 05 */ "Headphone Right",
+ /* 06 */ "Digital Rear Left",
+ /* 07 */ "Digital Rear Right",
+ /* 08 */ "Front Left",
+ /* 09 */ "Front Right",
+ /* 10 */ "Center",
+ /* 11 */ "LFE",
+ /* 12 */ "???",
+ /* 13 */ "???",
+ /* 14 */ "Rear Left",
+ /* 15 */ "Rear Right",
+ /* 16 */ "AC97 Front Left",
+ /* 17 */ "AC97 Front Right",
+ /* 18 */ "ADC Caputre Left",
+ /* 19 */ "ADC Capture Right",
+ /* 20 */ "???",
+ /* 21 */ "???",
+ /* 22 */ "???",
+ /* 23 */ "???",
+ /* 24 */ "???",
+ /* 25 */ "???",
+ /* 26 */ "???",
+ /* 27 */ "???",
+ /* 28 */ "???",
+ /* 29 */ "???",
+ /* 30 */ "???",
+ /* 31 */ "???",
+ /* 32 */ "FXBUS2_0",
+ /* 33 */ "FXBUS2_1",
+ /* 34 */ "FXBUS2_2",
+ /* 35 */ "FXBUS2_3",
+ /* 36 */ "FXBUS2_4",
+ /* 37 */ "FXBUS2_5",
+ /* 38 */ "FXBUS2_6",
+ /* 39 */ "FXBUS2_7",
+ /* 40 */ "FXBUS2_8",
+ /* 41 */ "FXBUS2_9",
+ /* 42 */ "FXBUS2_10",
+ /* 43 */ "FXBUS2_11",
+ /* 44 */ "FXBUS2_12",
+ /* 45 */ "FXBUS2_13",
+ /* 46 */ "FXBUS2_14",
+ /* 47 */ "FXBUS2_15",
+ /* 48 */ "FXBUS2_16",
+ /* 49 */ "FXBUS2_17",
+ /* 50 */ "FXBUS2_18",
+ /* 51 */ "FXBUS2_19",
+ /* 52 */ "FXBUS2_20",
+ /* 53 */ "FXBUS2_21",
+ /* 54 */ "FXBUS2_22",
+ /* 55 */ "FXBUS2_23",
+ /* 56 */ "FXBUS2_24",
+ /* 57 */ "FXBUS2_25",
+ /* 58 */ "FXBUS2_26",
+ /* 59 */ "FXBUS2_27",
+ /* 60 */ "FXBUS2_28",
+ /* 61 */ "FXBUS2_29",
+ /* 62 */ "FXBUS2_30",
+ /* 63 */ "FXBUS2_31"
+ };
+
+ emu10k1_t *emu = entry->private_data;
+ unsigned int val, val1;
+ int nefx = emu->audigy ? 64 : 32;
+ char **outputs = emu->audigy ? audigy_outs : creative_outs;
+ int idx;
+
+ snd_iprintf(buffer, "EMU10K1\n\n");
+ snd_iprintf(buffer, "Card : %s\n",
+ emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative"));
+ snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
+ snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
+ snd_iprintf(buffer, "\n");
+ snd_iprintf(buffer, "Effect Send Routing :\n");
+ for (idx = 0; idx < NUM_G; idx++) {
+ val = emu->audigy ?
+ snd_emu10k1_ptr_read(emu, A_FXRT1, idx) :
+ snd_emu10k1_ptr_read(emu, FXRT, idx);
+ val1 = emu->audigy ?
+ snd_emu10k1_ptr_read(emu, A_FXRT2, idx) :
+ 0;
+ if (emu->audigy) {
+ snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i, ",
+ idx,
+ val & 0x3f,
+ (val >> 8) & 0x3f,
+ (val >> 16) & 0x3f,
+ (val >> 24) & 0x3f);
+ snd_iprintf(buffer, "E=%i, F=%i, G=%i, H=%i\n",
+ val1 & 0x3f,
+ (val1 >> 8) & 0x3f,
+ (val1 >> 16) & 0x3f,
+ (val1 >> 24) & 0x3f);
+ } else {
+ snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i\n",
+ idx,
+ (val >> 16) & 0x0f,
+ (val >> 20) & 0x0f,
+ (val >> 24) & 0x0f,
+ (val >> 28) & 0x0f);
+ }
+ }
+ snd_iprintf(buffer, "\nCaptured FX Outputs :\n");
+ for (idx = 0; idx < nefx; idx++) {
+ if (emu->efx_voices_mask[idx/32] & (1 << (idx%32)))
+ snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
+ }
+ snd_iprintf(buffer, "\nAll FX Outputs :\n");
+ for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
+ snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
+ snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1);
+ snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1);
+ snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1);
+ snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS);
+ snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS);
+ val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
+ snd_iprintf(buffer, "\nZoomed Video\n");
+ snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
+ snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
+}
+
+static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ u32 pc;
+ emu10k1_t *emu = entry->private_data;
+
+ snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name);
+ snd_iprintf(buffer, " Code dump :\n");
+ for (pc = 0; pc < (emu->audigy ? 1024 : 512); pc++) {
+ u32 low, high;
+
+ low = snd_emu10k1_efx_read(emu, pc * 2);
+ high = snd_emu10k1_efx_read(emu, pc * 2 + 1);
+ if (emu->audigy)
+ snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+ (high >> 24) & 0x0f,
+ (high >> 12) & 0x7ff,
+ (high >> 0) & 0x7ff,
+ (low >> 12) & 0x7ff,
+ (low >> 0) & 0x7ff,
+ pc,
+ high, low);
+ else
+ snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+ (high >> 20) & 0x0f,
+ (high >> 10) & 0x3ff,
+ (high >> 0) & 0x3ff,
+ (low >> 10) & 0x3ff,
+ (low >> 0) & 0x3ff,
+ pc,
+ high, low);
+ }
+}
+
+#define TOTAL_SIZE_GPR (0x100*4)
+#define A_TOTAL_SIZE_GPR (0x200*4)
+#define TOTAL_SIZE_TANKMEM_DATA (0xa0*4)
+#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4)
+#define A_TOTAL_SIZE_TANKMEM_DATA (0x100*4)
+#define A_TOTAL_SIZE_TANKMEM_ADDR (0x100*4)
+#define TOTAL_SIZE_CODE (0x200*8)
+#define A_TOTAL_SIZE_CODE (0x400*8)
+
+static long snd_emu10k1_fx8010_read(snd_info_entry_t *entry, void *file_private_data,
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
+{
+ long size;
+ emu10k1_t *emu = entry->private_data;
+ unsigned int offset;
+ int tram_addr = 0;
+
+ if (!strcmp(entry->name, "fx8010_tram_addr")) {
+ offset = TANKMEMADDRREGBASE;
+ tram_addr = 1;
+ } else if (!strcmp(entry->name, "fx8010_tram_data")) {
+ offset = TANKMEMDATAREGBASE;
+ } else if (!strcmp(entry->name, "fx8010_code")) {
+ offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+ } else {
+ offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE;
+ }
+ size = count;
+ if (pos + size > entry->size)
+ size = (long)entry->size - pos;
+ if (size > 0) {
+ unsigned int *tmp;
+ long res;
+ unsigned int idx;
+ if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ for (idx = 0; idx < ((pos & 3) + size + 3) >> 2; idx++)
+ if (tram_addr && emu->audigy) {
+ tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0) >> 11;
+ tmp[idx] |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20;
+ } else
+ tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0);
+ if (copy_to_user(buf, ((char *)tmp) + (pos & 3), size))
+ res = -EFAULT;
+ else {
+ res = size;
+ }
+ kfree(tmp);
+ return res;
+ }
+ return 0;
+}
+
+static void snd_emu10k1_proc_voices_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ emu10k1_t *emu = entry->private_data;
+ emu10k1_voice_t *voice;
+ int idx;
+
+ snd_iprintf(buffer, "ch\tuse\tpcm\tefx\tsynth\tmidi\n");
+ for (idx = 0; idx < NUM_G; idx++) {
+ voice = &emu->voices[idx];
+ snd_iprintf(buffer, "%i\t%i\t%i\t%i\t%i\t%i\n",
+ idx,
+ voice->use,
+ voice->pcm,
+ voice->efx,
+ voice->synth,
+ voice->midi);
+ }
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_emu_proc_io_reg_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ emu10k1_t *emu = entry->private_data;
+ unsigned long value;
+ unsigned long flags;
+ int i;
+ snd_iprintf(buffer, "IO Registers:\n\n");
+ for(i = 0; i < 0x40; i+=4) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ value = inl(emu->port + i);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+ }
+}
+
+static void snd_emu_proc_io_reg_write(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ emu10k1_t *emu = entry->private_data;
+ unsigned long flags;
+ char line[64];
+ u32 reg, val;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x", &reg, &val) != 2)
+ continue;
+ if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(val, emu->port + (reg & 0xfffffffc));
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ }
+ }
+}
+
+static unsigned int snd_ptr_read(emu10k1_t * emu,
+ unsigned int iobase,
+ unsigned int reg,
+ unsigned int chn)
+{
+ unsigned long flags;
+ unsigned int regptr, val;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + iobase + PTR);
+ val = inl(emu->port + iobase + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+}
+
+static void snd_ptr_write(emu10k1_t *emu,
+ unsigned int iobase,
+ unsigned int reg,
+ unsigned int chn,
+ unsigned int data)
+{
+ unsigned int regptr;
+ unsigned long flags;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + iobase + PTR);
+ outl(data, emu->port + iobase + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+
+static void snd_emu_proc_ptr_reg_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer, int iobase, int offset, int length, int voices)
+{
+ emu10k1_t *emu = entry->private_data;
+ unsigned long value;
+ int i,j;
+ if (offset+length > 0x80) {
+ snd_iprintf(buffer, "Input values out of range\n");
+ return;
+ }
+ snd_iprintf(buffer, "Registers 0x%x\n", iobase);
+ for(i = offset; i < offset+length; i++) {
+ snd_iprintf(buffer, "%02X: ",i);
+ for (j = 0; j < voices; j++) {
+ if(iobase == 0)
+ value = snd_ptr_read(emu, 0, i, j);
+ else
+ value = snd_ptr_read(emu, 0x20, i, j);
+ snd_iprintf(buffer, "%08lX ", value);
+ }
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void snd_emu_proc_ptr_reg_write(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer, int iobase)
+{
+ emu10k1_t *emu = entry->private_data;
+ char line[64];
+ unsigned int reg, channel_id , val;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+ continue;
+ if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+ snd_ptr_write(emu, iobase, reg, channel_id, val);
+ }
+}
+
+static void snd_emu_proc_ptr_reg_write00(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_emu_proc_ptr_reg_write(entry, buffer, 0);
+}
+
+static void snd_emu_proc_ptr_reg_write20(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_emu_proc_ptr_reg_write(entry, buffer, 0x20);
+}
+
+
+static void snd_emu_proc_ptr_reg_read00a(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0, 0x40, 64);
+}
+
+static void snd_emu_proc_ptr_reg_read00b(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0x40, 0x40, 64);
+}
+
+static void snd_emu_proc_ptr_reg_read20a(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0, 0x40, 4);
+}
+
+static void snd_emu_proc_ptr_reg_read20b(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0x40, 0x40, 4);
+}
+#endif
+
+static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
+ .read = snd_emu10k1_fx8010_read,
+};
+
+int __devinit snd_emu10k1_proc_init(emu10k1_t * emu)
+{
+ snd_info_entry_t *entry;
+#ifdef CONFIG_SND_DEBUG
+ if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
+ snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_emu_proc_io_reg_write;
+ }
+ if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
+ snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+ }
+ if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
+ snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+ }
+ if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
+ snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+ }
+ if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
+ snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+ }
+#endif
+
+ if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
+ snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_read);
+
+ if (! snd_card_proc_new(emu->card, "voices", &entry))
+ snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read);
+
+ if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = emu;
+ entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
+ entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+ }
+ if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = emu;
+ entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
+ entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+ }
+ if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = emu;
+ entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
+ entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+ }
+ if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = emu;
+ entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
+ entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+ }
+ if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->private_data = emu;
+ entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->c.text.read_size = 128*1024;
+ entry->c.text.read = snd_emu10k1_proc_acode_read;
+ }
+ return 0;
+}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
new file mode 100644
index 00000000000..b9d3ae0dcab
--- /dev/null
+++ b/sound/pci/emu10k1/io.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Creative Labs, Inc.
+ * Routines for control of EMU10K1 chips
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn)
+{
+ unsigned long flags;
+ unsigned int regptr, val;
+ unsigned int mask;
+
+ mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+ regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+ if (reg & 0xff000000) {
+ unsigned char size, offset;
+
+ size = (reg >> 24) & 0x3f;
+ offset = (reg >> 16) & 0x1f;
+ mask = ((1 << size) - 1) << offset;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+ return (val & mask) >> offset;
+ } else {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+ }
+}
+
+void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data)
+{
+ unsigned int regptr;
+ unsigned long flags;
+ unsigned int mask;
+
+ mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+ regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+ if (reg & 0xff000000) {
+ unsigned char size, offset;
+
+ size = (reg >> 24) & 0x3f;
+ offset = (reg >> 16) & 0x1f;
+ mask = ((1 << size) - 1) << offset;
+ data = (data << offset) & mask;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ data |= inl(emu->port + DATA) & ~mask;
+ outl(data, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ } else {
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ outl(data, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ }
+}
+
+unsigned int snd_emu10k1_ptr20_read(emu10k1_t * emu,
+ unsigned int reg,
+ unsigned int chn)
+{
+ unsigned long flags;
+ unsigned int regptr, val;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + 0x20 + PTR);
+ val = inl(emu->port + 0x20 + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+}
+
+void snd_emu10k1_ptr20_write(emu10k1_t *emu,
+ unsigned int reg,
+ unsigned int chn,
+ unsigned int data)
+{
+ unsigned int regptr;
+ unsigned long flags;
+
+ regptr = (reg << 16) | chn;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + 0x20 + PTR);
+ outl(data, emu->port + 0x20 + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb)
+{
+ unsigned long flags;
+ unsigned int enable;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ enable = inl(emu->port + INTE) | intrenb;
+ outl(enable, emu->port + INTE);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb)
+{
+ unsigned long flags;
+ unsigned int enable;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ enable = inl(emu->port + INTE) & ~intrenb;
+ outl(enable, emu->port + INTE);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(CLIEH << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val |= 1 << (voicenum - 32);
+ } else {
+ outl(CLIEL << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val |= 1 << voicenum;
+ }
+ outl(val, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(CLIEH << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val &= ~(1 << (voicenum - 32));
+ } else {
+ outl(CLIEL << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val &= ~(1 << voicenum);
+ }
+ outl(val, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(CLIPH << 16, emu->port + PTR);
+ voicenum = 1 << (voicenum - 32);
+ } else {
+ outl(CLIPL << 16, emu->port + PTR);
+ voicenum = 1 << voicenum;
+ }
+ outl(voicenum, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_enable(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(HLIEH << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val |= 1 << (voicenum - 32);
+ } else {
+ outl(HLIEL << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val |= 1 << voicenum;
+ }
+ outl(val, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_disable(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(HLIEH << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val &= ~(1 << (voicenum - 32));
+ } else {
+ outl(HLIEL << 16, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ val &= ~(1 << voicenum);
+ }
+ outl(val, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_ack(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(HLIPH << 16, emu->port + PTR);
+ voicenum = 1 << (voicenum - 32);
+ } else {
+ outl(HLIPL << 16, emu->port + PTR);
+ voicenum = 1 << voicenum;
+ }
+ outl(voicenum, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+ unsigned int sol;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(SOLEH << 16, emu->port + PTR);
+ sol = inl(emu->port + DATA);
+ sol |= 1 << (voicenum - 32);
+ } else {
+ outl(SOLEL << 16, emu->port + PTR);
+ sol = inl(emu->port + DATA);
+ sol |= 1 << voicenum;
+ }
+ outl(sol, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum)
+{
+ unsigned long flags;
+ unsigned int sol;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ /* voice interrupt */
+ if (voicenum >= 32) {
+ outl(SOLEH << 16, emu->port + PTR);
+ sol = inl(emu->port + DATA);
+ sol &= ~(1 << (voicenum - 32));
+ } else {
+ outl(SOLEL << 16, emu->port + PTR);
+ sol = inl(emu->port + DATA);
+ sol &= ~(1 << voicenum);
+ }
+ outl(sol, emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait)
+{
+ volatile unsigned count;
+ unsigned int newtime = 0, curtime;
+
+ curtime = inl(emu->port + WC) >> 6;
+ while (wait-- > 0) {
+ count = 0;
+ while (count++ < 16384) {
+ newtime = inl(emu->port + WC) >> 6;
+ if (newtime != curtime)
+ break;
+ }
+ if (count >= 16384)
+ break;
+ curtime = newtime;
+ }
+}
+
+unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ emu10k1_t *emu = ac97->private_data;
+ unsigned long flags;
+ unsigned short val;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outb(reg, emu->port + AC97ADDRESS);
+ val = inw(emu->port + AC97DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return val;
+}
+
+void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data)
+{
+ emu10k1_t *emu = ac97->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outb(reg, emu->port + AC97ADDRESS);
+ outw(data, emu->port + AC97DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/*
+ * convert rate to pitch
+ */
+
+unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
+{
+ static u32 logMagTable[128] = {
+ 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
+ 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
+ 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
+ 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
+ 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
+ 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
+ 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+ 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
+ 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
+ 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
+ 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
+ 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
+ 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
+ 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+ 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
+ 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
+ };
+ static char logSlopeTable[128] = {
+ 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+ 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+ 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+ 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+ 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+ 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+ 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+ 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+ 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+ 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+ 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+ 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+ };
+ int i;
+
+ if (rate == 0)
+ return 0; /* Bail out if no leading "1" */
+ rate *= 11185; /* Scale 48000 to 0x20002380 */
+ for (i = 31; i > 0; i--) {
+ if (rate & 0x80000000) { /* Detect leading "1" */
+ return (((unsigned int) (i - 15) << 20) +
+ logMagTable[0x7f & (rate >> 24)] +
+ (0x7f & (rate >> 17)) *
+ logSlopeTable[0x7f & (rate >> 24)]);
+ }
+ rate <<= 1;
+ }
+
+ return 0; /* Should never reach this point */
+}
+
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
new file mode 100644
index 00000000000..b81a7cafff3
--- /dev/null
+++ b/sound/pci/emu10k1/irq.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Creative Labs, Inc.
+ * Routines for IRQ control of EMU10K1 chips
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ emu10k1_t *emu = dev_id;
+ unsigned int status, status2, orig_status, orig_status2;
+ int handled = 0;
+
+ while ((status = inl(emu->port + IPR)) != 0) {
+ // printk("irq - status = 0x%x\n", status);
+ orig_status = status;
+ handled = 1;
+ if (status & IPR_PCIERROR) {
+ snd_printk("interrupt: PCI error\n");
+ snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
+ status &= ~IPR_PCIERROR;
+ }
+ if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
+ if (emu->hwvol_interrupt)
+ emu->hwvol_interrupt(emu, status);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
+ status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
+ }
+ if (status & IPR_CHANNELLOOP) {
+ int voice;
+ int voice_max = status & IPR_CHANNELNUMBERMASK;
+ u32 val;
+ emu10k1_voice_t *pvoice = emu->voices;
+
+ val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
+ for (voice = 0; voice <= voice_max; voice++) {
+ if (voice == 0x20)
+ val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
+ if (val & 1) {
+ if (pvoice->use && pvoice->interrupt != NULL) {
+ pvoice->interrupt(emu, pvoice);
+ snd_emu10k1_voice_intr_ack(emu, voice);
+ } else {
+ snd_emu10k1_voice_intr_disable(emu, voice);
+ }
+ }
+ val >>= 1;
+ pvoice++;
+ }
+ val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
+ for (voice = 0; voice <= voice_max; voice++) {
+ if (voice == 0x20)
+ val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
+ if (val & 1) {
+ if (pvoice->use && pvoice->interrupt != NULL) {
+ pvoice->interrupt(emu, pvoice);
+ snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
+ } else {
+ snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
+ }
+ }
+ val >>= 1;
+ pvoice++;
+ }
+ status &= ~IPR_CHANNELLOOP;
+ }
+ status &= ~IPR_CHANNELNUMBERMASK;
+ if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
+ if (emu->capture_interrupt)
+ emu->capture_interrupt(emu, status);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
+ status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
+ }
+ if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
+ if (emu->capture_mic_interrupt)
+ emu->capture_mic_interrupt(emu, status);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
+ status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
+ }
+ if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
+ if (emu->capture_efx_interrupt)
+ emu->capture_efx_interrupt(emu, status);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
+ status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
+ }
+ if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+ if (emu->midi.interrupt)
+ emu->midi.interrupt(emu, status);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+ status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
+ }
+ if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
+ if (emu->midi2.interrupt)
+ emu->midi2.interrupt(emu, status);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
+ status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
+ }
+ if (status & IPR_INTERVALTIMER) {
+ if (emu->timer)
+ snd_timer_interrupt(emu->timer, emu->timer->sticks);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+ status &= ~IPR_INTERVALTIMER;
+ }
+ if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
+ if (emu->spdif_interrupt)
+ emu->spdif_interrupt(emu, status);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
+ status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
+ }
+ if (status & IPR_FXDSP) {
+ if (emu->dsp_interrupt)
+ emu->dsp_interrupt(emu);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+ status &= ~IPR_FXDSP;
+ }
+ if (status) {
+ unsigned int bits;
+ //snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
+ //make sure any interrupts we don't handle are disabled:
+ bits = INTE_FXDSPENABLE |
+ INTE_PCIERRORENABLE |
+ INTE_VOLINCRENABLE |
+ INTE_VOLDECRENABLE |
+ INTE_MUTEENABLE |
+ INTE_MICBUFENABLE |
+ INTE_ADCBUFENABLE |
+ INTE_EFXBUFENABLE |
+ INTE_GPSPDIFENABLE |
+ INTE_CDSPDIFENABLE |
+ INTE_INTERVALTIMERENB |
+ INTE_MIDITXENABLE |
+ INTE_MIDIRXENABLE;
+ if (emu->audigy)
+ bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
+ snd_emu10k1_intr_disable(emu, bits);
+ }
+ outl(orig_status, emu->port + IPR); /* ack all */
+ }
+ if (emu->audigy && emu->revision == 4) { /* P16V */
+ while ((status2 = inl(emu->port + IPR2)) != 0) {
+ u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */
+ emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]);
+ orig_status2 = status2;
+ if(status2 & mask) {
+ if(pvoice->use) {
+ snd_pcm_period_elapsed(pvoice->epcm->substream);
+ } else {
+ snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
+ }
+ }
+ outl(orig_status2, emu->port + IPR2); /* ack all */
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
new file mode 100644
index 00000000000..7a595f0dd7a
--- /dev/null
+++ b/sound/pci/emu10k1/memory.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ * EMU10K1 memory page allocation (PTB area)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+/* page arguments of these two macros are Emu page (4096 bytes), not like
+ * aligned pages in others
+ */
+#define __set_ptb_entry(emu,page,addr) \
+ (((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << 1) | (page)))
+
+#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE)
+#define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES)
+/* get aligned page from offset address */
+#define get_aligned_page(offset) ((offset) >> PAGE_SHIFT)
+/* get offset address from aligned page */
+#define aligned_page_offset(page) ((page) << PAGE_SHIFT)
+
+#if PAGE_SIZE == 4096
+/* page size == EMUPAGESIZE */
+/* fill PTB entrie(s) corresponding to page with addr */
+#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr)
+/* fill PTB entrie(s) corresponding to page with silence pointer */
+#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page.addr)
+#else
+/* fill PTB entries -- we need to fill UNIT_PAGES entries */
+static inline void set_ptb_entry(emu10k1_t *emu, int page, dma_addr_t addr)
+{
+ int i;
+ page *= UNIT_PAGES;
+ for (i = 0; i < UNIT_PAGES; i++, page++) {
+ __set_ptb_entry(emu, page, addr);
+ addr += EMUPAGESIZE;
+ }
+}
+static inline void set_silent_ptb(emu10k1_t *emu, int page)
+{
+ int i;
+ page *= UNIT_PAGES;
+ for (i = 0; i < UNIT_PAGES; i++, page++)
+ /* do not increment ptr */
+ __set_ptb_entry(emu, page, emu->silent_page.addr);
+}
+#endif /* PAGE_SIZE */
+
+
+/*
+ */
+static int synth_alloc_pages(emu10k1_t *hw, emu10k1_memblk_t *blk);
+static int synth_free_pages(emu10k1_t *hw, emu10k1_memblk_t *blk);
+
+#define get_emu10k1_memblk(l,member) list_entry(l, emu10k1_memblk_t, member)
+
+
+/* initialize emu10k1 part */
+static void emu10k1_memblk_init(emu10k1_memblk_t *blk)
+{
+ blk->mapped_page = -1;
+ INIT_LIST_HEAD(&blk->mapped_link);
+ INIT_LIST_HEAD(&blk->mapped_order_link);
+ blk->map_locked = 0;
+
+ blk->first_page = get_aligned_page(blk->mem.offset);
+ blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1);
+ blk->pages = blk->last_page - blk->first_page + 1;
+}
+
+/*
+ * search empty region on PTB with the given size
+ *
+ * if an empty region is found, return the page and store the next mapped block
+ * in nextp
+ * if not found, return a negative error code.
+ */
+static int search_empty_map_area(emu10k1_t *emu, int npages, struct list_head **nextp)
+{
+ int page = 0, found_page = -ENOMEM;
+ int max_size = npages;
+ int size;
+ struct list_head *candidate = &emu->mapped_link_head;
+ struct list_head *pos;
+
+ list_for_each (pos, &emu->mapped_link_head) {
+ emu10k1_memblk_t *blk = get_emu10k1_memblk(pos, mapped_link);
+ snd_assert(blk->mapped_page >= 0, continue);
+ size = blk->mapped_page - page;
+ if (size == npages) {
+ *nextp = pos;
+ return page;
+ }
+ else if (size > max_size) {
+ /* we look for the maximum empty hole */
+ max_size = size;
+ candidate = pos;
+ found_page = page;
+ }
+ page = blk->mapped_page + blk->pages;
+ }
+ size = MAX_ALIGN_PAGES - page;
+ if (size >= max_size) {
+ *nextp = pos;
+ return page;
+ }
+ *nextp = candidate;
+ return found_page;
+}
+
+/*
+ * map a memory block onto emu10k1's PTB
+ *
+ * call with memblk_lock held
+ */
+static int map_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+ int page, pg;
+ struct list_head *next;
+
+ page = search_empty_map_area(emu, blk->pages, &next);
+ if (page < 0) /* not found */
+ return page;
+ /* insert this block in the proper position of mapped list */
+ list_add_tail(&blk->mapped_link, next);
+ /* append this as a newest block in order list */
+ list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+ blk->mapped_page = page;
+ /* fill PTB */
+ for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+ set_ptb_entry(emu, page, emu->page_addr_table[pg]);
+ page++;
+ }
+ return 0;
+}
+
+/*
+ * unmap the block
+ * return the size of resultant empty pages
+ *
+ * call with memblk_lock held
+ */
+static int unmap_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+ int start_page, end_page, mpage, pg;
+ struct list_head *p;
+ emu10k1_memblk_t *q;
+
+ /* calculate the expected size of empty region */
+ if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) {
+ q = get_emu10k1_memblk(p, mapped_link);
+ start_page = q->mapped_page + q->pages;
+ } else
+ start_page = 0;
+ if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
+ q = get_emu10k1_memblk(p, mapped_link);
+ end_page = q->mapped_page;
+ } else
+ end_page = MAX_ALIGN_PAGES;
+
+ /* remove links */
+ list_del(&blk->mapped_link);
+ list_del(&blk->mapped_order_link);
+ /* clear PTB */
+ mpage = blk->mapped_page;
+ for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+ set_silent_ptb(emu, mpage);
+ mpage++;
+ }
+ blk->mapped_page = -1;
+ return end_page - start_page; /* return the new empty size */
+}
+
+/*
+ * search empty pages with the given size, and create a memory block
+ *
+ * unlike synth_alloc the memory block is aligned to the page start
+ */
+static emu10k1_memblk_t *
+search_empty(emu10k1_t *emu, int size)
+{
+ struct list_head *p;
+ emu10k1_memblk_t *blk;
+ int page, psize;
+
+ psize = get_aligned_page(size + PAGE_SIZE -1);
+ page = 0;
+ list_for_each(p, &emu->memhdr->block) {
+ blk = get_emu10k1_memblk(p, mem.list);
+ if (page + psize <= blk->first_page)
+ goto __found_pages;
+ page = blk->last_page + 1;
+ }
+ if (page + psize > emu->max_cache_pages)
+ return NULL;
+
+__found_pages:
+ /* create a new memory block */
+ blk = (emu10k1_memblk_t *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev);
+ if (blk == NULL)
+ return NULL;
+ blk->mem.offset = aligned_page_offset(page); /* set aligned offset */
+ emu10k1_memblk_init(blk);
+ return blk;
+}
+
+
+/*
+ * check if the given pointer is valid for pages
+ */
+static int is_valid_page(emu10k1_t *emu, dma_addr_t addr)
+{
+ if (addr & ~emu->dma_mask) {
+ snd_printk("max memory size is 0x%lx (addr = 0x%lx)!!\n", emu->dma_mask, (unsigned long)addr);
+ return 0;
+ }
+ if (addr & (EMUPAGESIZE-1)) {
+ snd_printk("page is not aligned\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * map the given memory block on PTB.
+ * if the block is already mapped, update the link order.
+ * if no empty pages are found, tries to release unsed memory blocks
+ * and retry the mapping.
+ */
+int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+ int err;
+ int size;
+ struct list_head *p, *nextp;
+ emu10k1_memblk_t *deleted;
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->memblk_lock, flags);
+ if (blk->mapped_page >= 0) {
+ /* update order link */
+ list_del(&blk->mapped_order_link);
+ list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+ spin_unlock_irqrestore(&emu->memblk_lock, flags);
+ return 0;
+ }
+ if ((err = map_memblk(emu, blk)) < 0) {
+ /* no enough page - try to unmap some blocks */
+ /* starting from the oldest block */
+ p = emu->mapped_order_link_head.next;
+ for (; p != &emu->mapped_order_link_head; p = nextp) {
+ nextp = p->next;
+ deleted = get_emu10k1_memblk(p, mapped_order_link);
+ if (deleted->map_locked)
+ continue;
+ size = unmap_memblk(emu, deleted);
+ if (size >= blk->pages) {
+ /* ok the empty region is enough large */
+ err = map_memblk(emu, blk);
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&emu->memblk_lock, flags);
+ return err;
+}
+
+/*
+ * page allocation for DMA
+ */
+snd_util_memblk_t *
+snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+ snd_util_memhdr_t *hdr;
+ emu10k1_memblk_t *blk;
+ int page, err, idx;
+
+ snd_assert(emu, return NULL);
+ snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL);
+ hdr = emu->memhdr;
+ snd_assert(hdr, return NULL);
+
+ down(&hdr->block_mutex);
+ blk = search_empty(emu, runtime->dma_bytes);
+ if (blk == NULL) {
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ /* fill buffer addresses but pointers are not stored so that
+ * snd_free_pci_page() is not called in in synth_free()
+ */
+ idx = 0;
+ for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
+ dma_addr_t addr;
+#ifdef CONFIG_SND_DEBUG
+ if (idx >= sgbuf->pages) {
+ printk(KERN_ERR "emu: pages overflow! (%d-%d) for %d\n",
+ blk->first_page, blk->last_page, sgbuf->pages);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+#endif
+ addr = sgbuf->table[idx].addr;
+ if (! is_valid_page(emu, addr)) {
+ printk(KERN_ERR "emu: failure page = %d\n", idx);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ emu->page_addr_table[page] = addr;
+ emu->page_ptr_table[page] = NULL;
+ }
+
+ /* set PTB entries */
+ blk->map_locked = 1; /* do not unmap this block! */
+ err = snd_emu10k1_memblk_map(emu, blk);
+ if (err < 0) {
+ __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ up(&hdr->block_mutex);
+ return (snd_util_memblk_t *)blk;
+}
+
+
+/*
+ * release DMA buffer from page table
+ */
+int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk)
+{
+ snd_assert(emu && blk, return -EINVAL);
+ return snd_emu10k1_synth_free(emu, blk);
+}
+
+
+/*
+ * memory allocation using multiple pages (for synth)
+ * Unlike the DMA allocation above, non-contiguous pages are assined.
+ */
+
+/*
+ * allocate a synth sample area
+ */
+snd_util_memblk_t *
+snd_emu10k1_synth_alloc(emu10k1_t *hw, unsigned int size)
+{
+ emu10k1_memblk_t *blk;
+ snd_util_memhdr_t *hdr = hw->memhdr;
+
+ down(&hdr->block_mutex);
+ blk = (emu10k1_memblk_t *)__snd_util_mem_alloc(hdr, size);
+ if (blk == NULL) {
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ if (synth_alloc_pages(hw, blk)) {
+ __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ snd_emu10k1_memblk_map(hw, blk);
+ up(&hdr->block_mutex);
+ return (snd_util_memblk_t *)blk;
+}
+
+
+/*
+ * free a synth sample area
+ */
+int
+snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *memblk)
+{
+ snd_util_memhdr_t *hdr = emu->memhdr;
+ emu10k1_memblk_t *blk = (emu10k1_memblk_t *)memblk;
+ unsigned long flags;
+
+ down(&hdr->block_mutex);
+ spin_lock_irqsave(&emu->memblk_lock, flags);
+ if (blk->mapped_page >= 0)
+ unmap_memblk(emu, blk);
+ spin_unlock_irqrestore(&emu->memblk_lock, flags);
+ synth_free_pages(emu, blk);
+ __snd_util_mem_free(hdr, memblk);
+ up(&hdr->block_mutex);
+ return 0;
+}
+
+
+/* check new allocation range */
+static void get_single_page_range(snd_util_memhdr_t *hdr, emu10k1_memblk_t *blk, int *first_page_ret, int *last_page_ret)
+{
+ struct list_head *p;
+ emu10k1_memblk_t *q;
+ int first_page, last_page;
+ first_page = blk->first_page;
+ if ((p = blk->mem.list.prev) != &hdr->block) {
+ q = get_emu10k1_memblk(p, mem.list);
+ if (q->last_page == first_page)
+ first_page++; /* first page was already allocated */
+ }
+ last_page = blk->last_page;
+ if ((p = blk->mem.list.next) != &hdr->block) {
+ q = get_emu10k1_memblk(p, mem.list);
+ if (q->first_page == last_page)
+ last_page--; /* last page was already allocated */
+ }
+ *first_page_ret = first_page;
+ *last_page_ret = last_page;
+}
+
+/*
+ * allocate kernel pages
+ */
+static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+ int page, first_page, last_page;
+ struct snd_dma_buffer dmab;
+
+ emu10k1_memblk_init(blk);
+ get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+ /* allocate kernel pages */
+ for (page = first_page; page <= last_page; page++) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
+ PAGE_SIZE, &dmab) < 0)
+ goto __fail;
+ if (! is_valid_page(emu, dmab.addr)) {
+ snd_dma_free_pages(&dmab);
+ goto __fail;
+ }
+ emu->page_addr_table[page] = dmab.addr;
+ emu->page_ptr_table[page] = dmab.area;
+ }
+ return 0;
+
+__fail:
+ /* release allocated pages */
+ last_page = page - 1;
+ for (page = first_page; page <= last_page; page++) {
+ dmab.area = emu->page_ptr_table[page];
+ dmab.addr = emu->page_addr_table[page];
+ dmab.bytes = PAGE_SIZE;
+ snd_dma_free_pages(&dmab);
+ emu->page_addr_table[page] = 0;
+ emu->page_ptr_table[page] = NULL;
+ }
+
+ return -ENOMEM;
+}
+
+/*
+ * free pages
+ */
+static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+ int page, first_page, last_page;
+ struct snd_dma_buffer dmab;
+
+ get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+ dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+ dmab.dev.dev = snd_dma_pci_data(emu->pci);
+ for (page = first_page; page <= last_page; page++) {
+ if (emu->page_ptr_table[page] == NULL)
+ continue;
+ dmab.area = emu->page_ptr_table[page];
+ dmab.addr = emu->page_addr_table[page];
+ dmab.bytes = PAGE_SIZE;
+ snd_dma_free_pages(&dmab);
+ emu->page_addr_table[page] = 0;
+ emu->page_ptr_table[page] = NULL;
+ }
+
+ return 0;
+}
+
+/* calculate buffer pointer from offset address */
+inline static void *offset_ptr(emu10k1_t *emu, int page, int offset)
+{
+ char *ptr;
+ snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL);
+ ptr = emu->page_ptr_table[page];
+ if (! ptr) {
+ printk("emu10k1: access to NULL ptr: page = %d\n", page);
+ return NULL;
+ }
+ ptr += offset & (PAGE_SIZE - 1);
+ return (void*)ptr;
+}
+
+/*
+ * bzero(blk + offset, size)
+ */
+int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size)
+{
+ int page, nextofs, end_offset, temp, temp1;
+ void *ptr;
+ emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk;
+
+ offset += blk->offset & (PAGE_SIZE - 1);
+ end_offset = offset + size;
+ page = get_aligned_page(offset);
+ do {
+ nextofs = aligned_page_offset(page + 1);
+ temp = nextofs - offset;
+ temp1 = end_offset - offset;
+ if (temp1 < temp)
+ temp = temp1;
+ ptr = offset_ptr(emu, page + p->first_page, offset);
+ if (ptr)
+ memset(ptr, 0, temp);
+ offset = nextofs;
+ page++;
+ } while (offset < end_offset);
+ return 0;
+}
+
+/*
+ * copy_from_user(blk + offset, data, size)
+ */
+int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char __user *data, int size)
+{
+ int page, nextofs, end_offset, temp, temp1;
+ void *ptr;
+ emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk;
+
+ offset += blk->offset & (PAGE_SIZE - 1);
+ end_offset = offset + size;
+ page = get_aligned_page(offset);
+ do {
+ nextofs = aligned_page_offset(page + 1);
+ temp = nextofs - offset;
+ temp1 = end_offset - offset;
+ if (temp1 < temp)
+ temp = temp1;
+ ptr = offset_ptr(emu, page + p->first_page, offset);
+ if (ptr && copy_from_user(ptr, data, temp))
+ return -EFAULT;
+ offset = nextofs;
+ data += temp;
+ page++;
+ } while (offset < end_offset);
+ return 0;
+}
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
new file mode 100644
index 00000000000..d03cb2fefc9
--- /dev/null
+++ b/sound/pci/emu10k1/p16v.c
@@ -0,0 +1,736 @@
+/*
+ * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Driver p16v chips
+ * Version: 0.22
+ *
+ * FEATURES currently supported:
+ * Output fixed at S32_LE, 2 channel to hw:0,0
+ * Rates: 44.1, 48, 96, 192.
+ *
+ * Changelog:
+ * 0.8
+ * Use separate card based buffer for periods table.
+ * 0.9
+ * Use 2 channel output streams instead of 8 channel.
+ * (8 channel output streams might be good for ASIO type output)
+ * Corrected speaker output, so Front -> Front etc.
+ * 0.10
+ * Fixed missed interrupts.
+ * 0.11
+ * Add Sound card model number and names.
+ * Add Analog volume controls.
+ * 0.12
+ * Corrected playback interrupts. Now interrupt per period, instead of half period.
+ * 0.13
+ * Use single trigger for multichannel.
+ * 0.14
+ * Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
+ * 0.15
+ * Force buffer_size / period_size == INTEGER.
+ * 0.16
+ * Update p16v.c to work with changed alsa api.
+ * 0.17
+ * Update p16v.c to work with changed alsa api. Removed boot_devs.
+ * 0.18
+ * Merging with snd-emu10k1 driver.
+ * 0.19
+ * One stereo channel at 24bit now works.
+ * 0.20
+ * Added better register defines.
+ * 0.21
+ * Integrated with snd-emu10k1 driver.
+ * 0.22
+ * Removed #if 0 ... #endif
+ *
+ *
+ * BUGS:
+ * Some stability problems when unloading the snd-p16v kernel module.
+ * --
+ *
+ * TODO:
+ * SPDIF out.
+ * Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
+ * Currently capture fixed at 48000Hz.
+ *
+ * --
+ * GENERAL INFO:
+ * Model: SB0240
+ * P16V Chip: CA0151-DBS
+ * Audigy 2 Chip: CA0102-IAT
+ * AC97 Codec: STAC 9721
+ * ADC: Philips 1361T (Stereo 24bit)
+ * DAC: CS4382-K (8-channel, 24bit, 192Khz)
+ *
+ * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/emu10k1.h>
+#include "p16v.h"
+
+#define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
+#define PCM_FRONT_CHANNEL 0
+#define PCM_REAR_CHANNEL 1
+#define PCM_CENTER_LFE_CHANNEL 2
+#define PCM_UNKNOWN_CHANNEL 3
+#define CONTROL_FRONT_CHANNEL 0
+#define CONTROL_REAR_CHANNEL 3
+#define CONTROL_CENTER_LFE_CHANNEL 1
+#define CONTROL_UNKNOWN_CHANNEL 2
+
+/* Card IDs:
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2002 -> Audigy2 ZS 7.1 Model:SB0350
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1007 -> Audigy2 6.1 Model:SB0240
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1002 -> Audigy2 Platinum Model:SB msb0240230009266
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2007 -> Audigy4 Pro Model:SB0380 M1SB0380472001901E
+ *
+ */
+
+ /* hardware definition */
+static snd_pcm_hardware_t snd_p16v_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
+ .rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 ,
+ .rate_min = 48000,
+ .rate_max = 192000,
+ .channels_min = 8,
+ .channels_max = 8,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = 8,
+ .fifo_size = 0,
+};
+
+static void snd_p16v_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ snd_pcm_t *epcm = runtime->private_data;
+
+ if (epcm) {
+ //snd_printk("epcm free: %p\n", epcm);
+ kfree(epcm);
+ }
+}
+
+/* open_playback callback */
+static int snd_p16v_pcm_open_playback_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ emu10k1_voice_t *channel = &(emu->p16v_voices[channel_id]);
+ emu10k1_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+ //snd_printk("epcm kcalloc: %p\n", epcm);
+
+ if (epcm == NULL)
+ return -ENOMEM;
+ epcm->emu = emu;
+ epcm->substream = substream;
+ //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_p16v_pcm_free_substream;
+
+ runtime->hw = snd_p16v_playback_hw;
+
+ channel->emu = emu;
+ channel->number = channel_id;
+
+ channel->use=1;
+ //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
+ //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+ //channel->interrupt = snd_p16v_pcm_channel_interrupt;
+ channel->epcm=epcm;
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+
+ return 0;
+}
+
+/* close callback */
+static int snd_p16v_pcm_close_playback(snd_pcm_substream_t *substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ //snd_pcm_runtime_t *runtime = substream->runtime;
+ //emu10k1_pcm_t *epcm = runtime->private_data;
+ emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
+/* FIXME: maybe zero others */
+ return 0;
+}
+
+static int snd_p16v_pcm_open_playback_front(snd_pcm_substream_t *substream)
+{
+ return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_playback(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ int result;
+ //snd_printk("hw_params alloc: substream=%p\n", substream);
+ result = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ //snd_printk("hw_params alloc: result=%d\n", result);
+ //dump_stack();
+ return result;
+}
+
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_playback(snd_pcm_substream_t *substream)
+{
+ int result;
+ //snd_printk("hw_params free: substream=%p\n", substream);
+ result = snd_pcm_lib_free_pages(substream);
+ //snd_printk("hw_params free: result=%d\n", result);
+ //dump_stack();
+ return result;
+}
+
+/* prepare playback callback */
+static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ //emu10k1_pcm_t *epcm = runtime->private_data;
+ int channel = substream->pcm->device - emu->p16v_device_offset;
+ u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
+ u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+ int i;
+ u32 tmp;
+
+ //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+ //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+ //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->p16v_buffer.addr, emu->p16v_buffer.area, emu->p16v_buffer.bytes);
+ tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+ switch (runtime->rate) {
+ case 44100:
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x8000); /* FIXME: This will change the capture rate as well! */
+ break;
+ case 48000:
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x0000); /* FIXME: This will change the capture rate as well! */
+ break;
+ case 96000:
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x4000); /* FIXME: This will change the capture rate as well! */
+ break;
+ case 192000:
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x2000); /* FIXME: This will change the capture rate as well! */
+ break;
+ default:
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, 0x0000); /* FIXME: This will change the capture rate as well! */
+ break;
+ }
+ /* FIXME: Check emu->buffer.size before actually writing to it. */
+ for(i=0; i < runtime->periods; i++) {
+ table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
+ table_base[(i*2)+1]=period_size_bytes<<16;
+ }
+
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
+ snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
+ snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
+
+ return 0;
+}
+
+static void snd_p16v_intr_enable(emu10k1_t *emu, unsigned int intrenb)
+{
+ unsigned long flags;
+ unsigned int enable;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ enable = inl(emu->port + INTE2) | intrenb;
+ outl(enable, emu->port + INTE2);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_p16v_intr_disable(emu10k1_t *emu, unsigned int intrenb)
+{
+ unsigned long flags;
+ unsigned int disable;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ disable = inl(emu->port + INTE2) & (~intrenb);
+ outl(disable, emu->port + INTE2);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/* trigger_playback callback */
+static int snd_p16v_pcm_trigger_playback(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime;
+ emu10k1_pcm_t *epcm;
+ int channel;
+ int result = 0;
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ u32 basic = 0;
+ u32 inte = 0;
+ int running=0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ running=1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ default:
+ running=0;
+ break;
+ }
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ runtime = s->runtime;
+ epcm = runtime->private_data;
+ channel = substream->pcm->device-emu->p16v_device_offset;
+ //snd_printk("p16v channel=%d\n",channel);
+ epcm->running = running;
+ basic |= (0x1<<channel);
+ inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
+ snd_pcm_trigger_done(s, substream);
+ }
+ //snd_printk("basic=0x%x, inte=0x%x\n",basic, inte);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_p16v_intr_enable(emu, inte);
+ snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)| (basic));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+ snd_p16v_intr_disable(emu, inte);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_playback(snd_pcm_substream_t *substream)
+{
+ emu10k1_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1_pcm_t *epcm = runtime->private_data;
+ snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+ int channel = substream->pcm->device - emu->p16v_device_offset;
+ if (!epcm->running)
+ return 0;
+
+ ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+ ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
+ ptr4 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+ if (ptr3 != ptr4) ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
+ ptr2 = bytes_to_frames(runtime, ptr1);
+ ptr2+= (ptr4 >> 3) * runtime->period_size;
+ ptr=ptr2;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+
+ return ptr;
+}
+
+/* operators */
+static snd_pcm_ops_t snd_p16v_playback_front_ops = {
+ .open = snd_p16v_pcm_open_playback_front,
+ .close = snd_p16v_pcm_close_playback,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_p16v_pcm_hw_params_playback,
+ .hw_free = snd_p16v_pcm_hw_free_playback,
+ .prepare = snd_p16v_pcm_prepare_playback,
+ .trigger = snd_p16v_pcm_trigger_playback,
+ .pointer = snd_p16v_pcm_pointer_playback,
+};
+
+int snd_p16v_free(emu10k1_t *chip)
+{
+ // release the data
+ if (chip->p16v_buffer.area) {
+ snd_dma_free_pages(&chip->p16v_buffer);
+ //snd_printk("period lables free: %p\n", &chip->p16v_buffer);
+ }
+ return 0;
+}
+
+static void snd_p16v_pcm_free(snd_pcm_t *pcm)
+{
+ emu10k1_t *emu = pcm->private_data;
+ //snd_printk("snd_p16v_pcm_free pcm: called\n");
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+ emu->pcm = NULL;
+}
+
+int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm)
+{
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *substream;
+ int err;
+ int capture=0;
+
+ //snd_printk("snd_p16v_pcm called. device=%d\n", device);
+ emu->p16v_device_offset = device;
+ if (rpcm)
+ *rpcm = NULL;
+ //if (device == 0) capture=1;
+ if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = emu;
+ pcm->private_free = snd_p16v_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
+
+ pcm->info_flags = 0;
+ pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+ strcpy(pcm->name, "p16v");
+ emu->pcm = pcm;
+
+ for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ substream;
+ substream = substream->next) {
+ if ((err = snd_pcm_lib_preallocate_pages(substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(emu->pci),
+ 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
+ return err;
+ //snd_printk("preallocate playback substream: err=%d\n", err);
+ }
+
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ substream;
+ substream = substream->next) {
+ if ((err = snd_pcm_lib_preallocate_pages(substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(emu->pci),
+ 64*1024, 64*1024)) < 0)
+ return err;
+ //snd_printk("preallocate capture substream: err=%d\n", err);
+ }
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+static int snd_p16v_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_p16v_volume_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol, int reg, int high_low)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ u32 value;
+
+ value = snd_emu10k1_ptr20_read(emu, reg, high_low);
+ if (high_low == 1) {
+ ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
+ ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
+ } else {
+ ucontrol->value.integer.value[0] = 0xff - ((value >> 8) & 0xff); /* Left */
+ ucontrol->value.integer.value[1] = 0xff - ((value >> 0) & 0xff); /* Right */
+ }
+ return 0;
+}
+
+static int snd_p16v_volume_get_spdif_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER7;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER7;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+static int snd_p16v_volume_get_spdif_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER8;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+static int snd_p16v_volume_get_spdif_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER8;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_analog_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER9;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_analog_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER9;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+static int snd_p16v_volume_get_analog_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER10;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_analog_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER10;
+ return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol, int reg, int high_low)
+{
+ emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+ u32 value;
+ value = snd_emu10k1_ptr20_read(emu, reg, 0);
+ //value = value & 0xffff;
+ if (high_low == 1) {
+ value &= 0xffff;
+ value = value | ((0xff - ucontrol->value.integer.value[0]) << 24) | ((0xff - ucontrol->value.integer.value[1]) << 16);
+ } else {
+ value &= 0xffff0000;
+ value = value | ((0xff - ucontrol->value.integer.value[0]) << 8) | ((0xff - ucontrol->value.integer.value[1]) );
+ }
+ snd_emu10k1_ptr20_write(emu, reg, 0, value);
+ return 1;
+}
+
+static int snd_p16v_volume_put_spdif_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER7;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER7;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_spdif_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER8;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_spdif_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER8;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_front(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER9;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_center_lfe(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER9;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_rear(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 1;
+ int reg = PLAYBACK_VOLUME_MIXER10;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_unknown(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ int high_low = 0;
+ int reg = PLAYBACK_VOLUME_MIXER10;
+ return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_front =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD Analog Front Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_analog_front,
+ .put = snd_p16v_volume_put_analog_front
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_center_lfe =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD Analog Center/LFE Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_analog_center_lfe,
+ .put = snd_p16v_volume_put_analog_center_lfe
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_unknown =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD Analog Unknown Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_analog_unknown,
+ .put = snd_p16v_volume_put_analog_unknown
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_rear =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD Analog Rear Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_analog_rear,
+ .put = snd_p16v_volume_put_analog_rear
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_front =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD SPDIF Front Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_spdif_front,
+ .put = snd_p16v_volume_put_spdif_front
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_center_lfe =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD SPDIF Center/LFE Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_spdif_center_lfe,
+ .put = snd_p16v_volume_put_spdif_center_lfe
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_unknown =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD SPDIF Unknown Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_spdif_unknown,
+ .put = snd_p16v_volume_put_spdif_unknown
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_rear =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HD SPDIF Rear Volume",
+ .info = snd_p16v_volume_info,
+ .get = snd_p16v_volume_get_spdif_rear,
+ .put = snd_p16v_volume_put_spdif_rear
+};
+
+int snd_p16v_mixer(emu10k1_t *emu)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = emu->card;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_front, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_rear, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_center_lfe, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_unknown, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_front, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_rear, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_center_lfe, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_unknown, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ return 0;
+}
+
diff --git a/sound/pci/emu10k1/p16v.h b/sound/pci/emu10k1/p16v.h
new file mode 100644
index 00000000000..15321494033
--- /dev/null
+++ b/sound/pci/emu10k1/p16v.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Driver p16v chips
+ * Version: 0.21
+ *
+ * FEATURES currently supported:
+ * Output fixed at S32_LE, 2 channel to hw:0,0
+ * Rates: 44.1, 48, 96, 192.
+ *
+ * Changelog:
+ * 0.8
+ * Use separate card based buffer for periods table.
+ * 0.9
+ * Use 2 channel output streams instead of 8 channel.
+ * (8 channel output streams might be good for ASIO type output)
+ * Corrected speaker output, so Front -> Front etc.
+ * 0.10
+ * Fixed missed interrupts.
+ * 0.11
+ * Add Sound card model number and names.
+ * Add Analog volume controls.
+ * 0.12
+ * Corrected playback interrupts. Now interrupt per period, instead of half period.
+ * 0.13
+ * Use single trigger for multichannel.
+ * 0.14
+ * Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
+ * 0.15
+ * Force buffer_size / period_size == INTEGER.
+ * 0.16
+ * Update p16v.c to work with changed alsa api.
+ * 0.17
+ * Update p16v.c to work with changed alsa api. Removed boot_devs.
+ * 0.18
+ * Merging with snd-emu10k1 driver.
+ * 0.19
+ * One stereo channel at 24bit now works.
+ * 0.20
+ * Added better register defines.
+ * 0.21
+ * Split from p16v.c
+ *
+ *
+ * BUGS:
+ * Some stability problems when unloading the snd-p16v kernel module.
+ * --
+ *
+ * TODO:
+ * SPDIF out.
+ * Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
+ * Currently capture fixed at 48000Hz.
+ *
+ * --
+ * GENERAL INFO:
+ * Model: SB0240
+ * P16V Chip: CA0151-DBS
+ * Audigy 2 Chip: CA0102-IAT
+ * AC97 Codec: STAC 9721
+ * ADC: Philips 1361T (Stereo 24bit)
+ * DAC: CS4382-K (8-channel, 24bit, 192Khz)
+ *
+ * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/********************************************************************************************************/
+/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */
+/********************************************************************************************************/
+
+/* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE.
+ * The sample rate is also controlled by the same registers that control the rate of the EMU10K2 sample rate converters.
+ */
+
+/* Initally all registers from 0x00 to 0x3f have zero contents. */
+#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
+ /* One list entry: 4 bytes for DMA address,
+ * 4 bytes for period_size << 16.
+ * One list entry is 8 bytes long.
+ * One list entry for each period in the buffer.
+ */
+#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
+#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
+#define PLAYBACK_UNKNOWN3 0x03 /* Not used */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */
+#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
+#define PLAYBACK_FIFO_END_ADDRESS 0x07 /* Playback FIFO end address */
+#define PLAYBACK_FIFO_POINTER 0x08 /* Playback FIFO pointer and number of valid sound samples in cache */
+#define PLAYBACK_UNKNOWN9 0x09 /* Not used */
+#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */
+#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */
+#define CAPTURE_FIFO_POINTER 0x13 /* Capture FIFO pointer and number of valid sound samples in cache */
+#define CAPTURE_P16V_VOLUME1 0x14 /* Low: Capture volume 0xXXXX3030 */
+#define CAPTURE_P16V_VOLUME2 0x15 /* High:Has no effect on capture volume */
+#define CAPTURE_P16V_SOURCE 0x16 /* P16V source select. Set to 0x0700E4E5 for AC97 CAPTURE */
+ /* [0:1] Capture input 0 channel select. 0 = Capture output 0.
+ * 1 = Capture output 1.
+ * 2 = Capture output 2.
+ * 3 = Capture output 3.
+ * [3:2] Capture input 1 channel select. 0 = Capture output 0.
+ * 1 = Capture output 1.
+ * 2 = Capture output 2.
+ * 3 = Capture output 3.
+ * [5:4] Capture input 2 channel select. 0 = Capture output 0.
+ * 1 = Capture output 1.
+ * 2 = Capture output 2.
+ * 3 = Capture output 3.
+ * [7:6] Capture input 3 channel select. 0 = Capture output 0.
+ * 1 = Capture output 1.
+ * 2 = Capture output 2.
+ * 3 = Capture output 3.
+ * [9:8] Playback input 0 channel select. 0 = Play output 0.
+ * 1 = Play output 1.
+ * 2 = Play output 2.
+ * 3 = Play output 3.
+ * [11:10] Playback input 1 channel select. 0 = Play output 0.
+ * 1 = Play output 1.
+ * 2 = Play output 2.
+ * 3 = Play output 3.
+ * [13:12] Playback input 2 channel select. 0 = Play output 0.
+ * 1 = Play output 1.
+ * 2 = Play output 2.
+ * 3 = Play output 3.
+ * [15:14] Playback input 3 channel select. 0 = Play output 0.
+ * 1 = Play output 1.
+ * 2 = Play output 2.
+ * 3 = Play output 3.
+ * [19:16] Playback mixer output enable. 1 bit per channel.
+ * [23:20] Capture mixer output enable. 1 bit per channel.
+ * [26:24] FX engine channel capture 0 = 0x60-0x67.
+ * 1 = 0x68-0x6f.
+ * 2 = 0x70-0x77.
+ * 3 = 0x78-0x7f.
+ * 4 = 0x80-0x87.
+ * 5 = 0x88-0x8f.
+ * 6 = 0x90-0x97.
+ * 7 = 0x98-0x9f.
+ * [31:27] Not used.
+ */
+
+ /* 0x1 = capture on.
+ * 0x100 = capture off.
+ * 0x200 = capture off.
+ * 0x1000 = capture off.
+ */
+#define CAPTURE_RATE_STATUS 0x17 /* Capture sample rate. Read only */
+ /* [15:0] Not used.
+ * [18:16] Channel 0 Detected sample rate. 0 - 44.1khz
+ * 1 - 48 khz
+ * 2 - 96 khz
+ * 3 - 192 khz
+ * 7 - undefined rate.
+ * [19] Channel 0. 1 - Valid, 0 - Not Valid.
+ * [22:20] Channel 1 Detected sample rate.
+ * [23] Channel 1. 1 - Valid, 0 - Not Valid.
+ * [26:24] Channel 2 Detected sample rate.
+ * [27] Channel 2. 1 - Valid, 0 - Not Valid.
+ * [30:28] Channel 3 Detected sample rate.
+ * [31] Channel 3. 1 - Valid, 0 - Not Valid.
+ */
+/* 0x18 - 0x1f unused */
+#define PLAYBACK_LAST_SAMPLE 0x20 /* The sample currently being played. Read only */
+/* 0x21 - 0x3f unused */
+#define BASIC_INTERRUPT 0x40 /* Used by both playback and capture interrupt handler */
+ /* Playback (0x1<<channel_id) Don't touch high 16bits. */
+ /* Capture (0x100<<channel_id). not tested */
+ /* Start Playback [3:0] (one bit per channel)
+ * Start Capture [11:8] (one bit per channel)
+ * Record source select for channel 0 [18:16]
+ * Record source select for channel 1 [22:20]
+ * Record source select for channel 2 [26:24]
+ * Record source select for channel 3 [30:28]
+ * 0 - SPDIF channel.
+ * 1 - I2S channel.
+ * 2 - SRC48 channel.
+ * 3 - SRCMulti_SPDIF channel.
+ * 4 - SRCMulti_I2S channel.
+ * 5 - SPDIF channel.
+ * 6 - fxengine capture.
+ * 7 - AC97 capture.
+ */
+ /* Default 41110000.
+ * Writing 0xffffffff hangs the PC.
+ * Writing 0xffff0000 -> 77770000 so it must be some sort of route.
+ * bit 0x1 starts DMA playback on channel_id 0
+ */
+/* 0x41,42 take values from 0 - 0xffffffff, but have no effect on playback */
+/* 0x43,0x48 do not remember settings */
+/* 0x41-45 unused */
+#define WATERMARK 0x46 /* Test bit to indicate cache level usage */
+ /* Values it can have while playing on channel 0.
+ * 0000f000, 0000f004, 0000f008, 0000f00c.
+ * Readonly.
+ */
+/* 0x47-0x4f unused */
+/* 0x50-0x5f Capture cache data */
+#define SRCSel 0x60 /* SRCSel. Default 0x4. Bypass P16V 0x14 */
+ /* [0] 0 = 10K2 audio, 1 = SRC48 mixer output.
+ * [2] 0 = 10K2 audio, 1 = SRCMulti SPDIF mixer output.
+ * [4] 0 = 10K2 audio, 1 = SRCMulti I2S mixer output.
+ */
+ /* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
+ /* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
+ /* SRC48 and SRCMULTI sample rate select and output select. */
+ /* 0xffffffff -> 0xC0000015
+ * 0xXXXXXXX4 = Enable Front Left/Right
+ * Enable PCMs
+ */
+
+/* 0x61 -> 0x6c are Volume controls */
+#define PLAYBACK_VOLUME_MIXER1 0x61 /* SRC48 Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER2 0x62 /* SRC48 High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER3 0x63 /* SRCMULTI SPDIF Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER4 0x64 /* SRCMULTI SPDIF High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER5 0x65 /* SRCMULTI I2S Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER6 0x66 /* SRCMULTI I2S High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER7 0x67 /* P16V Low to SRCMULTI SPDIF mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER8 0x68 /* P16V High to SRCMULTI SPDIF mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER9 0x69 /* P16V Low to SRCMULTI I2S mixer input volume control. */
+ /* 0xXXXX3030 = PCM0 Volume (Front).
+ * 0x3030XXXX = PCM1 Volume (Center)
+ */
+#define PLAYBACK_VOLUME_MIXER10 0x6a /* P16V High to SRCMULTI I2S mixer input volume control. */
+ /* 0x3030XXXX = PCM3 Volume (Rear). */
+#define PLAYBACK_VOLUME_MIXER11 0x6b /* E10K2 Low to SRC48 mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER12 0x6c /* E10K2 High to SRC48 mixer input volume control. */
+
+#define SRC48_ENABLE 0x6d /* SRC48 input audio enable */
+ /* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
+ /* [23:16] The corresponding P16V channel to SRC48 enabled if == 1.
+ * [31:24] The corresponding E10K2 channel to SRC48 enabled.
+ */
+#define SRCMULTI_ENABLE 0x6e /* SRCMulti input audio enable. Default 0xffffffff */
+ /* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
+ /* [7:0] The corresponding P16V channel to SRCMulti_I2S enabled if == 1.
+ * [15:8] The corresponding E10K2 channel to SRCMulti I2S enabled.
+ * [23:16] The corresponding P16V channel to SRCMulti SPDIF enabled.
+ * [31:24] The corresponding E10K2 channel to SRCMulti SPDIF enabled.
+ */
+ /* Bypass P16V 0xff00ff00
+ * Bitmap. 0 = Off, 1 = On.
+ * P16V playback outputs:
+ * 0xXXXXXXX1 = PCM0 Left. (Front)
+ * 0xXXXXXXX2 = PCM0 Right.
+ * 0xXXXXXXX4 = PCM1 Left. (Center/LFE)
+ * 0xXXXXXXX8 = PCM1 Right.
+ * 0xXXXXXX1X = PCM2 Left. (Unknown)
+ * 0xXXXXXX2X = PCM2 Right.
+ * 0xXXXXXX4X = PCM3 Left. (Rear)
+ * 0xXXXXXX8X = PCM3 Right.
+ */
+#define AUDIO_OUT_ENABLE 0x6f /* Default: 000100FF */
+ /* [3:0] Does something, but not documented. Probably capture enable.
+ * [7:4] Playback channels enable. not documented.
+ * [16] AC97 output enable if == 1
+ * [30] 0 = SRCMulti_I2S input from fxengine 0x68-0x6f.
+ * 1 = SRCMulti_I2S input from SRC48 output.
+ * [31] 0 = SRCMulti_SPDIF input from fxengine 0x60-0x67.
+ * 1 = SRCMulti_SPDIF input from SRC48 output.
+ */
+ /* 0xffffffff -> C00100FF */
+ /* 0 -> Not playback sound, irq still running */
+ /* 0xXXXXXX10 = PCM0 Left/Right On. (Front)
+ * 0xXXXXXX20 = PCM1 Left/Right On. (Center/LFE)
+ * 0xXXXXXX40 = PCM2 Left/Right On. (Unknown)
+ * 0xXXXXXX80 = PCM3 Left/Right On. (Rear)
+ */
+#define PLAYBACK_SPDIF_SELECT 0x70 /* Default: 12030F00 */
+ /* 0xffffffff -> 3FF30FFF */
+ /* 0x00000001 pauses stream/irq fail. */
+ /* All other bits do not effect playback */
+#define PLAYBACK_SPDIF_SRC_SELECT 0x71 /* Default: 0000E4E4 */
+ /* 0xffffffff -> F33FFFFF */
+ /* All bits do not effect playback */
+#define PLAYBACK_SPDIF_USER_DATA0 0x72 /* SPDIF out user data 0 */
+#define PLAYBACK_SPDIF_USER_DATA1 0x73 /* SPDIF out user data 1 */
+/* 0x74-0x75 unknown */
+#define CAPTURE_SPDIF_CONTROL 0x76 /* SPDIF in control setting */
+#define CAPTURE_SPDIF_STATUS 0x77 /* SPDIF in status */
+#define CAPURE_SPDIF_USER_DATA0 0x78 /* SPDIF in user data 0 */
+#define CAPURE_SPDIF_USER_DATA1 0x79 /* SPDIF in user data 1 */
+#define CAPURE_SPDIF_USER_DATA2 0x7a /* SPDIF in user data 2 */
+
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
new file mode 100644
index 00000000000..d2e364607c1
--- /dev/null
+++ b/sound/pci/emu10k1/timer.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) by Lee Revell <rlrevell@joe-job.com>
+ * Clemens Ladisch <clemens@ladisch.de>
+ * Routines for control of EMU10K1 chips
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static int snd_emu10k1_timer_start(snd_timer_t *timer)
+{
+ emu10k1_t *emu;
+ unsigned long flags;
+ unsigned int delay;
+
+ emu = snd_timer_chip(timer);
+ delay = timer->sticks - 1;
+ if (delay < 5 ) /* minimum time is 5 ticks */
+ delay = 5;
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
+ outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_timer_stop(snd_timer_t *timer)
+{
+ emu10k1_t *emu;
+ unsigned long flags;
+
+ emu = snd_timer_chip(timer);
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return 0;
+}
+
+static int snd_emu10k1_timer_precise_resolution(snd_timer_t *timer,
+ unsigned long *num, unsigned long *den)
+{
+ *num = 1;
+ *den = 48000;
+ return 0;
+}
+
+static struct _snd_timer_hardware snd_emu10k1_timer_hw = {
+ .flags = SNDRV_TIMER_HW_AUTO,
+ .resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */
+ .ticks = 1024,
+ .start = snd_emu10k1_timer_start,
+ .stop = snd_emu10k1_timer_stop,
+ .precise_resolution = snd_emu10k1_timer_precise_resolution,
+};
+
+int __devinit snd_emu10k1_timer(emu10k1_t *emu, int device)
+{
+ snd_timer_t *timer = NULL;
+ snd_timer_id_t tid;
+ int err;
+
+ tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+ tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+ tid.card = emu->card->number;
+ tid.device = device;
+ tid.subdevice = 0;
+ if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) {
+ strcpy(timer->name, "EMU10K1 timer");
+ timer->private_data = emu;
+ timer->hw = snd_emu10k1_timer_hw;
+ }
+ emu->timer = timer;
+ return err;
+}
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
new file mode 100644
index 00000000000..d251d3440ee
--- /dev/null
+++ b/sound/pci/emu10k1/voice.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Creative Labs, Inc.
+ * Lee Revell <rlrevell@joe-job.com>
+ * Routines for control of EMU10K1 chips - voice manager
+ *
+ * Rewrote voice allocator for multichannel support - rlrevell 12/2004
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+/* Previously the voice allocator started at 0 every time. The new voice
+ * allocator uses a round robin scheme. The next free voice is tracked in
+ * the card record and each allocation begins where the last left off. The
+ * hardware requires stereo interleaved voices be aligned to an even/odd
+ * boundary. For multichannel voice allocation we ensure than the block of
+ * voices does not cross the 32 voice boundary. This simplifies the
+ * multichannel support and ensures we can use a single write to the
+ * (set|clear)_loop_stop registers. Otherwise (for example) the voices would
+ * get out of sync when pausing/resuming a stream.
+ * --rlrevell
+ */
+
+static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int number, emu10k1_voice_t **rvoice)
+{
+ emu10k1_voice_t *voice;
+ int i, j, k, first_voice, last_voice, skip;
+
+ *rvoice = NULL;
+ first_voice = last_voice = 0;
+ for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
+ // printk("i %d j %d next free %d!\n", i, j, emu->next_free_voice);
+ i %= NUM_G;
+
+ /* stereo voices must be even/odd */
+ if ((number == 2) && (i % 2)) {
+ i++;
+ continue;
+ }
+
+ skip = 0;
+ for (k = 0; k < number; k++) {
+ voice = &emu->voices[(i+k) % NUM_G];
+ if (voice->use) {
+ skip = 1;
+ break;
+ }
+ }
+ if (!skip) {
+ // printk("allocated voice %d\n", i);
+ first_voice = i;
+ last_voice = (i + number) % NUM_G;
+ emu->next_free_voice = last_voice;
+ break;
+ }
+ }
+
+ if (first_voice == last_voice)
+ return -ENOMEM;
+
+ for (i=0; i < number; i++) {
+ voice = &emu->voices[(first_voice + i) % NUM_G];
+ // printk("voice alloc - %i, %i of %i\n", voice->number, idx-first_voice+1, number);
+ voice->use = 1;
+ switch (type) {
+ case EMU10K1_PCM:
+ voice->pcm = 1;
+ break;
+ case EMU10K1_SYNTH:
+ voice->synth = 1;
+ break;
+ case EMU10K1_MIDI:
+ voice->midi = 1;
+ break;
+ case EMU10K1_EFX:
+ voice->efx = 1;
+ break;
+ }
+ }
+ *rvoice = &emu->voices[first_voice];
+ return 0;
+}
+
+int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int number, emu10k1_voice_t **rvoice)
+{
+ unsigned long flags;
+ int result;
+
+ snd_assert(rvoice != NULL, return -EINVAL);
+ snd_assert(number, return -EINVAL);
+
+ spin_lock_irqsave(&emu->voice_lock, flags);
+ for (;;) {
+ result = voice_alloc(emu, type, number, rvoice);
+ if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
+ break;
+
+ /* free a voice from synth */
+ if (emu->get_synth_voice) {
+ result = emu->get_synth_voice(emu);
+ if (result >= 0) {
+ emu10k1_voice_t *pvoice = &emu->voices[result];
+ pvoice->interrupt = NULL;
+ pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
+ pvoice->epcm = NULL;
+ }
+ }
+ if (result < 0)
+ break;
+ }
+ spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+ return result;
+}
+
+int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice)
+{
+ unsigned long flags;
+
+ snd_assert(pvoice != NULL, return -EINVAL);
+ spin_lock_irqsave(&emu->voice_lock, flags);
+ pvoice->interrupt = NULL;
+ pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
+ pvoice->epcm = NULL;
+ snd_emu10k1_voice_init(emu, pvoice->number);
+ spin_unlock_irqrestore(&emu->voice_lock, flags);
+ return 0;
+}
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
new file mode 100644
index 00000000000..f910399db5c
--- /dev/null
+++ b/sound/pci/ens1370.c
@@ -0,0 +1,2413 @@
+/*
+ * Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ * Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#ifdef CHIP1371
+#include <sound/ac97_codec.h>
+#else
+#include <sound/ak4531_codec.h>
+#endif
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+#ifndef CHIP1371
+#undef CHIP1370
+#define CHIP1370
+#endif
+
+#ifdef CHIP1370
+#define DRIVER_NAME "ENS1370"
+#else
+#define DRIVER_NAME "ENS1371"
+#endif
+
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Thomas Sailer <sailer@ife.ee.ethz.ch>");
+MODULE_LICENSE("GPL");
+#ifdef CHIP1370
+MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370");
+MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI-97 ES1370},"
+ "{Creative Labs,SB PCI64/128 (ES1370)}}");
+#endif
+#ifdef CHIP1371
+MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+");
+MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73},"
+ "{Ensoniq,AudioPCI ES1373},"
+ "{Creative Labs,Ectiva EV1938},"
+ "{Creative Labs,SB PCI64/128 (ES1371/73)},"
+ "{Creative Labs,Vibra PCI128},"
+ "{Ectiva,EV1938}}");
+#endif
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+#ifdef SUPPORT_JOYSTICK
+#ifdef CHIP1371
+static int joystick_port[SNDRV_CARDS];
+#else
+static int joystick[SNDRV_CARDS];
+#endif
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Ensoniq AudioPCI soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Ensoniq AudioPCI soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard.");
+#ifdef SUPPORT_JOYSTICK
+#ifdef CHIP1371
+module_param_array(joystick_port, int, NULL, 0444);
+MODULE_PARM_DESC(joystick_port, "Joystick port address.");
+#else
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable joystick.");
+#endif
+#endif /* SUPPORT_JOYSTICK */
+
+#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880
+#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880
+#endif
+#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371
+#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
+#endif
+
+/* ES1371 chip ID */
+/* This is a little confusing because all ES1371 compatible chips have the
+ same DEVICE_ID, the only thing differentiating them is the REV_ID field.
+ This is only significant if you want to enable features on the later parts.
+ Yes, I know it's stupid and why didn't we use the sub IDs?
+*/
+#define ES1371REV_ES1373_A 0x04
+#define ES1371REV_ES1373_B 0x06
+#define ES1371REV_CT5880_A 0x07
+#define CT5880REV_CT5880_C 0x02
+#define CT5880REV_CT5880_D 0x03 /* ??? -jk */
+#define CT5880REV_CT5880_E 0x04 /* mw */
+#define ES1371REV_ES1371_B 0x09
+#define EV1938REV_EV1938_A 0x00
+#define ES1371REV_ES1373_8 0x08
+
+/*
+ * Direct registers
+ */
+
+#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x)
+
+#define ES_REG_CONTROL 0x00 /* R/W: Interrupt/Chip select control register */
+#define ES_1370_ADC_STOP (1<<31) /* disable capture buffer transfers */
+#define ES_1370_XCTL1 (1<<30) /* general purpose output bit */
+#define ES_1373_BYPASS_P1 (1<<31) /* bypass SRC for PB1 */
+#define ES_1373_BYPASS_P2 (1<<30) /* bypass SRC for PB2 */
+#define ES_1373_BYPASS_R (1<<29) /* bypass SRC for REC */
+#define ES_1373_TEST_BIT (1<<28) /* should be set to 0 for normal operation */
+#define ES_1373_RECEN_B (1<<27) /* mix record with playback for I2S/SPDIF out */
+#define ES_1373_SPDIF_THRU (1<<26) /* 0 = SPDIF thru mode, 1 = SPDIF == dig out */
+#define ES_1371_JOY_ASEL(o) (((o)&0x03)<<24)/* joystick port mapping */
+#define ES_1371_JOY_ASELM (0x03<<24) /* mask for above */
+#define ES_1371_JOY_ASELI(i) (((i)>>24)&0x03)
+#define ES_1371_GPIO_IN(i) (((i)>>20)&0x0f)/* GPIO in [3:0] pins - R/O */
+#define ES_1370_PCLKDIVO(o) (((o)&0x1fff)<<16)/* clock divide ratio for DAC2 */
+#define ES_1370_PCLKDIVM ((0x1fff)<<16) /* mask for above */
+#define ES_1370_PCLKDIVI(i) (((i)>>16)&0x1fff)/* clock divide ratio for DAC2 */
+#define ES_1371_GPIO_OUT(o) (((o)&0x0f)<<16)/* GPIO out [3:0] pins - W/R */
+#define ES_1371_GPIO_OUTM (0x0f<<16) /* mask for above */
+#define ES_MSFMTSEL (1<<15) /* MPEG serial data format; 0 = SONY, 1 = I2S */
+#define ES_1370_M_SBB (1<<14) /* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */
+#define ES_1371_SYNC_RES (1<<14) /* Warm AC97 reset */
+#define ES_1370_WTSRSEL(o) (((o)&0x03)<<12)/* fixed frequency clock for DAC1 */
+#define ES_1370_WTSRSELM (0x03<<12) /* mask for above */
+#define ES_1371_ADC_STOP (1<<13) /* disable CCB transfer capture information */
+#define ES_1371_PWR_INTRM (1<<12) /* power level change interrupts enable */
+#define ES_1370_DAC_SYNC (1<<11) /* DAC's are synchronous */
+#define ES_1371_M_CB (1<<11) /* capture clock source; 0 = AC'97 ADC; 1 = I2S */
+#define ES_CCB_INTRM (1<<10) /* CCB voice interrupts enable */
+#define ES_1370_M_CB (1<<9) /* capture clock source; 0 = ADC; 1 = MPEG */
+#define ES_1370_XCTL0 (1<<8) /* generap purpose output bit */
+#define ES_1371_PDLEV(o) (((o)&0x03)<<8) /* current power down level */
+#define ES_1371_PDLEVM (0x03<<8) /* mask for above */
+#define ES_BREQ (1<<7) /* memory bus request enable */
+#define ES_DAC1_EN (1<<6) /* DAC1 playback channel enable */
+#define ES_DAC2_EN (1<<5) /* DAC2 playback channel enable */
+#define ES_ADC_EN (1<<4) /* ADC capture channel enable */
+#define ES_UART_EN (1<<3) /* UART enable */
+#define ES_JYSTK_EN (1<<2) /* Joystick module enable */
+#define ES_1370_CDC_EN (1<<1) /* Codec interface enable */
+#define ES_1371_XTALCKDIS (1<<1) /* Xtal clock disable */
+#define ES_1370_SERR_DISABLE (1<<0) /* PCI serr signal disable */
+#define ES_1371_PCICLKDIS (1<<0) /* PCI clock disable */
+#define ES_REG_STATUS 0x04 /* R/O: Interrupt/Chip select status register */
+#define ES_INTR (1<<31) /* Interrupt is pending */
+#define ES_1371_ST_AC97_RST (1<<29) /* CT5880 AC'97 Reset bit */
+#define ES_1373_REAR_BIT27 (1<<27) /* rear bits: 000 - front, 010 - mirror, 101 - separate */
+#define ES_1373_REAR_BIT26 (1<<26)
+#define ES_1373_REAR_BIT24 (1<<24)
+#define ES_1373_GPIO_INT_EN(o)(((o)&0x0f)<<20)/* GPIO [3:0] pins - interrupt enable */
+#define ES_1373_SPDIF_EN (1<<18) /* SPDIF enable */
+#define ES_1373_SPDIF_TEST (1<<17) /* SPDIF test */
+#define ES_1371_TEST (1<<16) /* test ASIC */
+#define ES_1373_GPIO_INT(i) (((i)&0x0f)>>12)/* GPIO [3:0] pins - interrupt pending */
+#define ES_1370_CSTAT (1<<10) /* CODEC is busy or register write in progress */
+#define ES_1370_CBUSY (1<<9) /* CODEC is busy */
+#define ES_1370_CWRIP (1<<8) /* CODEC register write in progress */
+#define ES_1371_SYNC_ERR (1<<8) /* CODEC synchronization error occurred */
+#define ES_1371_VC(i) (((i)>>6)&0x03) /* voice code from CCB module */
+#define ES_1370_VC(i) (((i)>>5)&0x03) /* voice code from CCB module */
+#define ES_1371_MPWR (1<<5) /* power level interrupt pending */
+#define ES_MCCB (1<<4) /* CCB interrupt pending */
+#define ES_UART (1<<3) /* UART interrupt pending */
+#define ES_DAC1 (1<<2) /* DAC1 channel interrupt pending */
+#define ES_DAC2 (1<<1) /* DAC2 channel interrupt pending */
+#define ES_ADC (1<<0) /* ADC channel interrupt pending */
+#define ES_REG_UART_DATA 0x08 /* R/W: UART data register */
+#define ES_REG_UART_STATUS 0x09 /* R/O: UART status register */
+#define ES_RXINT (1<<7) /* RX interrupt occurred */
+#define ES_TXINT (1<<2) /* TX interrupt occurred */
+#define ES_TXRDY (1<<1) /* transmitter ready */
+#define ES_RXRDY (1<<0) /* receiver ready */
+#define ES_REG_UART_CONTROL 0x09 /* W/O: UART control register */
+#define ES_RXINTEN (1<<7) /* RX interrupt enable */
+#define ES_TXINTENO(o) (((o)&0x03)<<5) /* TX interrupt enable */
+#define ES_TXINTENM (0x03<<5) /* mask for above */
+#define ES_TXINTENI(i) (((i)>>5)&0x03)
+#define ES_CNTRL(o) (((o)&0x03)<<0) /* control */
+#define ES_CNTRLM (0x03<<0) /* mask for above */
+#define ES_REG_UART_RES 0x0a /* R/W: UART reserver register */
+#define ES_TEST_MODE (1<<0) /* test mode enabled */
+#define ES_REG_MEM_PAGE 0x0c /* R/W: Memory page register */
+#define ES_MEM_PAGEO(o) (((o)&0x0f)<<0) /* memory page select - out */
+#define ES_MEM_PAGEM (0x0f<<0) /* mask for above */
+#define ES_MEM_PAGEI(i) (((i)>>0)&0x0f) /* memory page select - in */
+#define ES_REG_1370_CODEC 0x10 /* W/O: Codec write register address */
+#define ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0))
+#define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */
+#define ES_1371_CODEC_RDY (1<<31) /* codec ready */
+#define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */
+#define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */
+#define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0))
+#define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD)
+#define ES_1371_CODEC_READ(i) (((i)>>0)&0xffff)
+
+#define ES_REG_1371_SMPRATE 0x10 /* W/R: Codec rate converter interface register */
+#define ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25)/* address of the sample rate converter */
+#define ES_1371_SRC_RAM_ADDRM (0x7f<<25) /* mask for above */
+#define ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f)/* address of the sample rate converter */
+#define ES_1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */
+#define ES_1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */
+#define ES_1371_SRC_DISABLE (1<<22) /* sample rate converter disable */
+#define ES_1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */
+#define ES_1371_DIS_P2 (1<<20) /* playback channel 1 accumulator update disable */
+#define ES_1371_DIS_R1 (1<<19) /* capture channel accumulator update disable */
+#define ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0)/* current value of the sample rate converter */
+#define ES_1371_SRC_RAM_DATAM (0xffff<<0) /* mask for above */
+#define ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff)/* current value of the sample rate converter */
+
+#define ES_REG_1371_LEGACY 0x18 /* W/R: Legacy control/status register */
+#define ES_1371_JFAST (1<<31) /* fast joystick timing */
+#define ES_1371_HIB (1<<30) /* host interrupt blocking enable */
+#define ES_1371_VSB (1<<29) /* SB; 0 = addr 0x220xH, 1 = 0x22FxH */
+#define ES_1371_VMPUO(o) (((o)&0x03)<<27)/* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */
+#define ES_1371_VMPUM (0x03<<27) /* mask for above */
+#define ES_1371_VMPUI(i) (((i)>>27)&0x03)/* base register address */
+#define ES_1371_VCDCO(o) (((o)&0x03)<<25)/* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */
+#define ES_1371_VCDCM (0x03<<25) /* mask for above */
+#define ES_1371_VCDCI(i) (((i)>>25)&0x03)/* CODEC address */
+#define ES_1371_FIRQ (1<<24) /* force an interrupt */
+#define ES_1371_SDMACAP (1<<23) /* enable event capture for slave DMA controller */
+#define ES_1371_SPICAP (1<<22) /* enable event capture for slave IRQ controller */
+#define ES_1371_MDMACAP (1<<21) /* enable event capture for master DMA controller */
+#define ES_1371_MPICAP (1<<20) /* enable event capture for master IRQ controller */
+#define ES_1371_ADCAP (1<<19) /* enable event capture for ADLIB register; 0x388xH */
+#define ES_1371_SVCAP (1<<18) /* enable event capture for SB registers */
+#define ES_1371_CDCCAP (1<<17) /* enable event capture for CODEC registers */
+#define ES_1371_BACAP (1<<16) /* enable event capture for SoundScape base address */
+#define ES_1371_EXI(i) (((i)>>8)&0x07) /* event number */
+#define ES_1371_AI(i) (((i)>>3)&0x1f) /* event significant I/O address */
+#define ES_1371_WR (1<<2) /* event capture; 0 = read; 1 = write */
+#define ES_1371_LEGINT (1<<0) /* interrupt for legacy events; 0 = interrupt did occur */
+
+#define ES_REG_CHANNEL_STATUS 0x1c /* R/W: first 32-bits from S/PDIF channel status block, es1373 */
+
+#define ES_REG_SERIAL 0x20 /* R/W: Serial interface control register */
+#define ES_1371_DAC_TEST (1<<22) /* DAC test mode enable */
+#define ES_P2_END_INCO(o) (((o)&0x07)<<19)/* binary offset value to increment / loop end */
+#define ES_P2_END_INCM (0x07<<19) /* mask for above */
+#define ES_P2_END_INCI(i) (((i)>>16)&0x07)/* binary offset value to increment / loop end */
+#define ES_P2_ST_INCO(o) (((o)&0x07)<<16)/* binary offset value to increment / start */
+#define ES_P2_ST_INCM (0x07<<16) /* mask for above */
+#define ES_P2_ST_INCI(i) (((i)<<16)&0x07)/* binary offset value to increment / start */
+#define ES_R1_LOOP_SEL (1<<15) /* ADC; 0 - loop mode; 1 = stop mode */
+#define ES_P2_LOOP_SEL (1<<14) /* DAC2; 0 - loop mode; 1 = stop mode */
+#define ES_P1_LOOP_SEL (1<<13) /* DAC1; 0 - loop mode; 1 = stop mode */
+#define ES_P2_PAUSE (1<<12) /* DAC2; 0 - play mode; 1 = pause mode */
+#define ES_P1_PAUSE (1<<11) /* DAC1; 0 - play mode; 1 = pause mode */
+#define ES_R1_INT_EN (1<<10) /* ADC interrupt enable */
+#define ES_P2_INT_EN (1<<9) /* DAC2 interrupt enable */
+#define ES_P1_INT_EN (1<<8) /* DAC1 interrupt enable */
+#define ES_P1_SCT_RLD (1<<7) /* force sample counter reload for DAC1 */
+#define ES_P2_DAC_SEN (1<<6) /* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */
+#define ES_R1_MODEO(o) (((o)&0x03)<<4) /* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */
+#define ES_R1_MODEM (0x03<<4) /* mask for above */
+#define ES_R1_MODEI(i) (((i)>>4)&0x03)
+#define ES_P2_MODEO(o) (((o)&0x03)<<2) /* DAC2 mode; -- '' -- */
+#define ES_P2_MODEM (0x03<<2) /* mask for above */
+#define ES_P2_MODEI(i) (((i)>>2)&0x03)
+#define ES_P1_MODEO(o) (((o)&0x03)<<0) /* DAC1 mode; -- '' -- */
+#define ES_P1_MODEM (0x03<<0) /* mask for above */
+#define ES_P1_MODEI(i) (((i)>>0)&0x03)
+
+#define ES_REG_DAC1_COUNT 0x24 /* R/W: DAC1 sample count register */
+#define ES_REG_DAC2_COUNT 0x28 /* R/W: DAC2 sample count register */
+#define ES_REG_ADC_COUNT 0x2c /* R/W: ADC sample count register */
+#define ES_REG_CURR_COUNT(i) (((i)>>16)&0xffff)
+#define ES_REG_COUNTO(o) (((o)&0xffff)<<0)
+#define ES_REG_COUNTM (0xffff<<0)
+#define ES_REG_COUNTI(i) (((i)>>0)&0xffff)
+
+#define ES_REG_DAC1_FRAME 0x30 /* R/W: PAGE 0x0c; DAC1 frame address */
+#define ES_REG_DAC1_SIZE 0x34 /* R/W: PAGE 0x0c; DAC1 frame size */
+#define ES_REG_DAC2_FRAME 0x38 /* R/W: PAGE 0x0c; DAC2 frame address */
+#define ES_REG_DAC2_SIZE 0x3c /* R/W: PAGE 0x0c; DAC2 frame size */
+#define ES_REG_ADC_FRAME 0x30 /* R/W: PAGE 0x0d; ADC frame address */
+#define ES_REG_ADC_SIZE 0x34 /* R/W: PAGE 0x0d; ADC frame size */
+#define ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16)
+#define ES_REG_FCURR_COUNTM (0xffff<<16)
+#define ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc)
+#define ES_REG_FSIZEO(o) (((o)&0xffff)<<0)
+#define ES_REG_FSIZEM (0xffff<<0)
+#define ES_REG_FSIZEI(i) (((i)>>0)&0xffff)
+#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */
+#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */
+
+#define ES_REG_UART_FIFO 0x30 /* R/W: PAGE 0x0e; UART FIFO register */
+#define ES_REG_UF_VALID (1<<8)
+#define ES_REG_UF_BYTEO(o) (((o)&0xff)<<0)
+#define ES_REG_UF_BYTEM (0xff<<0)
+#define ES_REG_UF_BYTEI(i) (((i)>>0)&0xff)
+
+
+/*
+ * Pages
+ */
+
+#define ES_PAGE_DAC 0x0c
+#define ES_PAGE_ADC 0x0d
+#define ES_PAGE_UART 0x0e
+#define ES_PAGE_UART1 0x0f
+
+/*
+ * Sample rate converter addresses
+ */
+
+#define ES_SMPREG_DAC1 0x70
+#define ES_SMPREG_DAC2 0x74
+#define ES_SMPREG_ADC 0x78
+#define ES_SMPREG_VOL_ADC 0x6c
+#define ES_SMPREG_VOL_DAC1 0x7c
+#define ES_SMPREG_VOL_DAC2 0x7e
+#define ES_SMPREG_TRUNC_N 0x00
+#define ES_SMPREG_INT_REGS 0x01
+#define ES_SMPREG_ACCUM_FRAC 0x02
+#define ES_SMPREG_VFREQ_FRAC 0x03
+
+/*
+ * Some contants
+ */
+
+#define ES_1370_SRCLOCK 1411200
+#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2)
+
+/*
+ * Open modes
+ */
+
+#define ES_MODE_PLAY1 0x0001
+#define ES_MODE_PLAY2 0x0002
+#define ES_MODE_CAPTURE 0x0004
+
+#define ES_MODE_OUTPUT 0x0001 /* for MIDI */
+#define ES_MODE_INPUT 0x0002 /* for MIDI */
+
+/*
+
+ */
+
+typedef struct _snd_ensoniq ensoniq_t;
+
+struct _snd_ensoniq {
+ spinlock_t reg_lock;
+ struct semaphore src_mutex;
+
+ int irq;
+
+ unsigned long playback1size;
+ unsigned long playback2size;
+ unsigned long capture3size;
+
+ unsigned long port;
+ unsigned int mode;
+ unsigned int uartm; /* UART mode */
+
+ unsigned int ctrl; /* control register */
+ unsigned int sctrl; /* serial control register */
+ unsigned int cssr; /* control status register */
+ unsigned int uartc; /* uart control register */
+ unsigned int rev; /* chip revision */
+
+ union {
+#ifdef CHIP1371
+ struct {
+ ac97_t *ac97;
+ } es1371;
+#else
+ struct {
+ int pclkdiv_lock;
+ ak4531_t *ak4531;
+ } es1370;
+#endif
+ } u;
+
+ struct pci_dev *pci;
+ unsigned short subsystem_vendor_id;
+ unsigned short subsystem_device_id;
+ snd_card_t *card;
+ snd_pcm_t *pcm1; /* DAC1/ADC PCM */
+ snd_pcm_t *pcm2; /* DAC2 PCM */
+ snd_pcm_substream_t *playback1_substream;
+ snd_pcm_substream_t *playback2_substream;
+ snd_pcm_substream_t *capture_substream;
+ unsigned int p1_dma_size;
+ unsigned int p2_dma_size;
+ unsigned int c_dma_size;
+ unsigned int p1_period_size;
+ unsigned int p2_period_size;
+ unsigned int c_period_size;
+ snd_rawmidi_t *rmidi;
+ snd_rawmidi_substream_t *midi_input;
+ snd_rawmidi_substream_t *midi_output;
+
+ unsigned int spdif;
+ unsigned int spdif_default;
+ unsigned int spdif_stream;
+
+#ifdef CHIP1370
+ struct snd_dma_buffer dma_bug;
+#endif
+
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+};
+
+static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_audiopci_ids[] = {
+#ifdef CHIP1370
+ { 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1370 */
+#endif
+#ifdef CHIP1371
+ { 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1371 */
+ { 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1373 - CT5880 */
+ { 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ectiva EV1938 */
+#endif
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_audiopci_ids);
+
+/*
+ * constants
+ */
+
+#define POLL_COUNT 0xa000
+
+#ifdef CHIP1370
+static unsigned int snd_es1370_fixed_rates[] =
+ {5512, 11025, 22050, 44100};
+static snd_pcm_hw_constraint_list_t snd_es1370_hw_constraints_rates = {
+ .count = 4,
+ .list = snd_es1370_fixed_rates,
+ .mask = 0,
+};
+static ratnum_t es1370_clock = {
+ .num = ES_1370_SRCLOCK,
+ .den_min = 29,
+ .den_max = 353,
+ .den_step = 1,
+};
+static snd_pcm_hw_constraint_ratnums_t snd_es1370_hw_constraints_clock = {
+ .nrats = 1,
+ .rats = &es1370_clock,
+};
+#else
+static ratden_t es1371_dac_clock = {
+ .num_min = 3000 * (1 << 15),
+ .num_max = 48000 * (1 << 15),
+ .num_step = 3000,
+ .den = 1 << 15,
+};
+static snd_pcm_hw_constraint_ratdens_t snd_es1371_hw_constraints_dac_clock = {
+ .nrats = 1,
+ .rats = &es1371_dac_clock,
+};
+static ratnum_t es1371_adc_clock = {
+ .num = 48000 << 15,
+ .den_min = 32768,
+ .den_max = 393216,
+ .den_step = 1,
+};
+static snd_pcm_hw_constraint_ratnums_t snd_es1371_hw_constraints_adc_clock = {
+ .nrats = 1,
+ .rats = &es1371_adc_clock,
+};
+#endif
+static const unsigned int snd_ensoniq_sample_shift[] =
+ {0, 1, 1, 2};
+
+/*
+ * common I/O routines
+ */
+
+#ifdef CHIP1371
+
+static unsigned int snd_es1371_wait_src_ready(ensoniq_t * ensoniq)
+{
+ unsigned int t, r = 0;
+
+ for (t = 0; t < POLL_COUNT; t++) {
+ r = inl(ES_REG(ensoniq, 1371_SMPRATE));
+ if ((r & ES_1371_SRC_RAM_BUSY) == 0)
+ return r;
+ cond_resched();
+ }
+ snd_printk("wait source ready timeout 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_SMPRATE), r);
+ return 0;
+}
+
+static unsigned int snd_es1371_src_read(ensoniq_t * ensoniq, unsigned short reg)
+{
+ unsigned int temp, i, orig, r;
+
+ /* wait for ready */
+ temp = orig = snd_es1371_wait_src_ready(ensoniq);
+
+ /* expose the SRC state bits */
+ r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+ ES_1371_DIS_P2 | ES_1371_DIS_R1);
+ r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000;
+ outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+
+ /* now, wait for busy and the correct time to read */
+ temp = snd_es1371_wait_src_ready(ensoniq);
+
+ if ((temp & 0x00870000) != 0x00010000) {
+ /* wait for the right state */
+ for (i = 0; i < POLL_COUNT; i++) {
+ temp = inl(ES_REG(ensoniq, 1371_SMPRATE));
+ if ((temp & 0x00870000) == 0x00010000)
+ break;
+ }
+ }
+
+ /* hide the state bits */
+ r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+ ES_1371_DIS_P2 | ES_1371_DIS_R1);
+ r |= ES_1371_SRC_RAM_ADDRO(reg);
+ outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+
+ return temp;
+}
+
+static void snd_es1371_src_write(ensoniq_t * ensoniq,
+ unsigned short reg, unsigned short data)
+{
+ unsigned int r;
+
+ r = snd_es1371_wait_src_ready(ensoniq) &
+ (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+ ES_1371_DIS_P2 | ES_1371_DIS_R1);
+ r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data);
+ outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE));
+}
+
+#endif /* CHIP1371 */
+
+#ifdef CHIP1370
+
+static void snd_es1370_codec_write(ak4531_t *ak4531,
+ unsigned short reg, unsigned short val)
+{
+ ensoniq_t *ensoniq = ak4531->private_data;
+ unsigned long end_time = jiffies + HZ / 10;
+
+#if 0
+ printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n", reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+#endif
+ do {
+ if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) {
+ outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+ return;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after(end_time, jiffies));
+ snd_printk("codec write timeout, status = 0x%x\n", inl(ES_REG(ensoniq, STATUS)));
+}
+
+#endif /* CHIP1370 */
+
+#ifdef CHIP1371
+
+static void snd_es1371_codec_write(ac97_t *ac97,
+ unsigned short reg, unsigned short val)
+{
+ ensoniq_t *ensoniq = ac97->private_data;
+ unsigned int t, x;
+
+ down(&ensoniq->src_mutex);
+ for (t = 0; t < POLL_COUNT; t++) {
+ if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+ /* save the current state for latter */
+ x = snd_es1371_wait_src_ready(ensoniq);
+ outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+ ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+ ES_REG(ensoniq, 1371_SMPRATE));
+ /* wait for not busy (state 0) first to avoid
+ transition states */
+ for (t = 0; t < POLL_COUNT; t++) {
+ if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000)
+ break;
+ }
+ /* wait for a SAFE time to write addr/data and then do it, dammit */
+ for (t = 0; t < POLL_COUNT; t++) {
+ if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000)
+ break;
+ }
+ outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC));
+ /* restore SRC reg */
+ snd_es1371_wait_src_ready(ensoniq);
+ outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+ up(&ensoniq->src_mutex);
+ return;
+ }
+ }
+ up(&ensoniq->src_mutex);
+ snd_printk("codec write timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+}
+
+static unsigned short snd_es1371_codec_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ ensoniq_t *ensoniq = ac97->private_data;
+ unsigned int t, x, fail = 0;
+
+ __again:
+ down(&ensoniq->src_mutex);
+ for (t = 0; t < POLL_COUNT; t++) {
+ if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+ /* save the current state for latter */
+ x = snd_es1371_wait_src_ready(ensoniq);
+ outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+ ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+ ES_REG(ensoniq, 1371_SMPRATE));
+ /* wait for not busy (state 0) first to avoid
+ transition states */
+ for (t = 0; t < POLL_COUNT; t++) {
+ if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000)
+ break;
+ }
+ /* wait for a SAFE time to write addr/data and then do it, dammit */
+ for (t = 0; t < POLL_COUNT; t++) {
+ if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000)
+ break;
+ }
+ outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC));
+ /* restore SRC reg */
+ snd_es1371_wait_src_ready(ensoniq);
+ outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+ /* wait for WIP again */
+ for (t = 0; t < POLL_COUNT; t++) {
+ if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP))
+ break;
+ }
+ /* now wait for the stinkin' data (RDY) */
+ for (t = 0; t < POLL_COUNT; t++) {
+ if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
+ up(&ensoniq->src_mutex);
+ return ES_1371_CODEC_READ(x);
+ }
+ }
+ up(&ensoniq->src_mutex);
+ if (++fail > 10) {
+ snd_printk("codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), reg, inl(ES_REG(ensoniq, 1371_CODEC)));
+ return 0;
+ }
+ goto __again;
+ }
+ }
+ up(&ensoniq->src_mutex);
+ snd_printk("es1371: codec read timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+ return 0;
+}
+
+static void snd_es1371_adc_rate(ensoniq_t * ensoniq, unsigned int rate)
+{
+ unsigned int n, truncm, freq, result;
+
+ down(&ensoniq->src_mutex);
+ n = rate / 3000;
+ if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
+ n--;
+ truncm = (21 * n - 1) | 1;
+ freq = ((48000UL << 15) / rate) * n;
+ result = (48000UL << 15) / (freq / n);
+ if (rate >= 24000) {
+ if (truncm > 239)
+ truncm = 239;
+ snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+ (((239 - truncm) >> 1) << 9) | (n << 4));
+ } else {
+ if (truncm > 119)
+ truncm = 119;
+ snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+ 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+ }
+ snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
+ (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & 0x00ff) |
+ ((freq >> 5) & 0xfc00));
+ snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8);
+ up(&ensoniq->src_mutex);
+}
+
+static void snd_es1371_dac1_rate(ensoniq_t * ensoniq, unsigned int rate)
+{
+ unsigned int freq, r;
+
+ down(&ensoniq->src_mutex);
+ freq = ((rate << 15) + 1500) / 3000;
+ r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)) | ES_1371_DIS_P1;
+ outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS,
+ (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) |
+ ((freq >> 5) & 0xfc00));
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+ r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1));
+ outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+ up(&ensoniq->src_mutex);
+}
+
+static void snd_es1371_dac2_rate(ensoniq_t * ensoniq, unsigned int rate)
+{
+ unsigned int freq, r;
+
+ down(&ensoniq->src_mutex);
+ freq = ((rate << 15) + 1500) / 3000;
+ r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)) | ES_1371_DIS_P2;
+ outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS,
+ (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) & 0x00ff) |
+ ((freq >> 5) & 0xfc00));
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+ r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1));
+ outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+ up(&ensoniq->src_mutex);
+}
+
+#endif /* CHIP1371 */
+
+static int snd_ensoniq_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ {
+ unsigned int what = 0;
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == ensoniq->playback1_substream) {
+ what |= ES_P1_PAUSE;
+ snd_pcm_trigger_done(s, substream);
+ } else if (s == ensoniq->playback2_substream) {
+ what |= ES_P2_PAUSE;
+ snd_pcm_trigger_done(s, substream);
+ } else if (s == ensoniq->capture_substream)
+ return -EINVAL;
+ }
+ spin_lock(&ensoniq->reg_lock);
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ ensoniq->sctrl |= what;
+ else
+ ensoniq->sctrl &= ~what;
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ spin_unlock(&ensoniq->reg_lock);
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ unsigned int what = 0;
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == ensoniq->playback1_substream) {
+ what |= ES_DAC1_EN;
+ snd_pcm_trigger_done(s, substream);
+ } else if (s == ensoniq->playback2_substream) {
+ what |= ES_DAC2_EN;
+ snd_pcm_trigger_done(s, substream);
+ } else if (s == ensoniq->capture_substream) {
+ what |= ES_ADC_EN;
+ snd_pcm_trigger_done(s, substream);
+ }
+ }
+ spin_lock(&ensoniq->reg_lock);
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ ensoniq->ctrl |= what;
+ else
+ ensoniq->ctrl &= ~what;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ spin_unlock(&ensoniq->reg_lock);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * PCM part
+ */
+
+static int snd_ensoniq_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ensoniq_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ensoniq_playback1_prepare(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int mode = 0;
+
+ ensoniq->p1_dma_size = snd_pcm_lib_buffer_bytes(substream);
+ ensoniq->p1_period_size = snd_pcm_lib_period_bytes(substream);
+ if (snd_pcm_format_width(runtime->format) == 16)
+ mode |= 0x02;
+ if (runtime->channels > 1)
+ mode |= 0x01;
+ spin_lock_irq(&ensoniq->reg_lock);
+ ensoniq->ctrl &= ~ES_DAC1_EN;
+#ifdef CHIP1371
+ /* 48k doesn't need SRC (it breaks AC3-passthru) */
+ if (runtime->rate == 48000)
+ ensoniq->ctrl |= ES_1373_BYPASS_P1;
+ else
+ ensoniq->ctrl &= ~ES_1373_BYPASS_P1;
+#endif
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME));
+ outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
+ ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
+ ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC1_COUNT));
+#ifdef CHIP1370
+ ensoniq->ctrl &= ~ES_1370_WTSRSELM;
+ switch (runtime->rate) {
+ case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break;
+ case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break;
+ case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break;
+ case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break;
+ default: snd_BUG();
+ }
+#endif
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ spin_unlock_irq(&ensoniq->reg_lock);
+#ifndef CHIP1370
+ snd_es1371_dac1_rate(ensoniq, runtime->rate);
+#endif
+ return 0;
+}
+
+static int snd_ensoniq_playback2_prepare(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int mode = 0;
+
+ ensoniq->p2_dma_size = snd_pcm_lib_buffer_bytes(substream);
+ ensoniq->p2_period_size = snd_pcm_lib_period_bytes(substream);
+ if (snd_pcm_format_width(runtime->format) == 16)
+ mode |= 0x02;
+ if (runtime->channels > 1)
+ mode |= 0x01;
+ spin_lock_irq(&ensoniq->reg_lock);
+ ensoniq->ctrl &= ~ES_DAC2_EN;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME));
+ outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
+ ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
+ ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
+ ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
+ ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC2_COUNT));
+#ifdef CHIP1370
+ if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) {
+ ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+ ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+ ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
+ }
+#endif
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ spin_unlock_irq(&ensoniq->reg_lock);
+#ifndef CHIP1370
+ snd_es1371_dac2_rate(ensoniq, runtime->rate);
+#endif
+ return 0;
+}
+
+static int snd_ensoniq_capture_prepare(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int mode = 0;
+
+ ensoniq->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+ ensoniq->c_period_size = snd_pcm_lib_period_bytes(substream);
+ if (snd_pcm_format_width(runtime->format) == 16)
+ mode |= 0x02;
+ if (runtime->channels > 1)
+ mode |= 0x01;
+ spin_lock_irq(&ensoniq->reg_lock);
+ ensoniq->ctrl &= ~ES_ADC_EN;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME));
+ outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
+ ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
+ ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, ADC_COUNT));
+#ifdef CHIP1370
+ if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) {
+ ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+ ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+ ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
+ }
+#endif
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ spin_unlock_irq(&ensoniq->reg_lock);
+#ifndef CHIP1370
+ snd_es1371_adc_rate(ensoniq, runtime->rate);
+#endif
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ spin_lock(&ensoniq->reg_lock);
+ if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) {
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE)));
+ ptr = bytes_to_frames(substream->runtime, ptr);
+ } else {
+ ptr = 0;
+ }
+ spin_unlock(&ensoniq->reg_lock);
+ return ptr;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ spin_lock(&ensoniq->reg_lock);
+ if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) {
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE)));
+ ptr = bytes_to_frames(substream->runtime, ptr);
+ } else {
+ ptr = 0;
+ }
+ spin_unlock(&ensoniq->reg_lock);
+ return ptr;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_capture_pointer(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ spin_lock(&ensoniq->reg_lock);
+ if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) {
+ outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+ ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE)));
+ ptr = bytes_to_frames(substream->runtime, ptr);
+ } else {
+ ptr = 0;
+ }
+ spin_unlock(&ensoniq->reg_lock);
+ return ptr;
+}
+
+static snd_pcm_hardware_t snd_ensoniq_playback1 =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates =
+#ifndef CHIP1370
+ SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+#else
+ (SNDRV_PCM_RATE_KNOT | /* 5512Hz rate */
+ SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_44100),
+#endif
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .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 snd_pcm_hardware_t snd_ensoniq_playback2 =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .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 snd_pcm_hardware_t snd_ensoniq_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .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 int snd_ensoniq_playback1_open(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ ensoniq->mode |= ES_MODE_PLAY1;
+ ensoniq->playback1_substream = substream;
+ runtime->hw = snd_ensoniq_playback1;
+ snd_pcm_set_sync(substream);
+ spin_lock_irq(&ensoniq->reg_lock);
+ if (ensoniq->spdif && ensoniq->playback2_substream == NULL)
+ ensoniq->spdif_stream = ensoniq->spdif_default;
+ spin_unlock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_es1370_hw_constraints_rates);
+#else
+ snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_es1371_hw_constraints_dac_clock);
+#endif
+ return 0;
+}
+
+static int snd_ensoniq_playback2_open(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ ensoniq->mode |= ES_MODE_PLAY2;
+ ensoniq->playback2_substream = substream;
+ runtime->hw = snd_ensoniq_playback2;
+ snd_pcm_set_sync(substream);
+ spin_lock_irq(&ensoniq->reg_lock);
+ if (ensoniq->spdif && ensoniq->playback1_substream == NULL)
+ ensoniq->spdif_stream = ensoniq->spdif_default;
+ spin_unlock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+ snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_es1370_hw_constraints_clock);
+#else
+ snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_es1371_hw_constraints_dac_clock);
+#endif
+ return 0;
+}
+
+static int snd_ensoniq_capture_open(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ ensoniq->mode |= ES_MODE_CAPTURE;
+ ensoniq->capture_substream = substream;
+ runtime->hw = snd_ensoniq_capture;
+ snd_pcm_set_sync(substream);
+#ifdef CHIP1370
+ snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_es1370_hw_constraints_clock);
+#else
+ snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_es1371_hw_constraints_adc_clock);
+#endif
+ return 0;
+}
+
+static int snd_ensoniq_playback1_close(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+
+ ensoniq->playback1_substream = NULL;
+ ensoniq->mode &= ~ES_MODE_PLAY1;
+ return 0;
+}
+
+static int snd_ensoniq_playback2_close(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+
+ ensoniq->playback2_substream = NULL;
+ spin_lock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+ ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
+#endif
+ ensoniq->mode &= ~ES_MODE_PLAY2;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_ensoniq_capture_close(snd_pcm_substream_t * substream)
+{
+ ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+
+ ensoniq->capture_substream = NULL;
+ spin_lock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+ ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE;
+#endif
+ ensoniq->mode &= ~ES_MODE_CAPTURE;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ensoniq_playback1_ops = {
+ .open = snd_ensoniq_playback1_open,
+ .close = snd_ensoniq_playback1_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ensoniq_hw_params,
+ .hw_free = snd_ensoniq_hw_free,
+ .prepare = snd_ensoniq_playback1_prepare,
+ .trigger = snd_ensoniq_trigger,
+ .pointer = snd_ensoniq_playback1_pointer,
+};
+
+static snd_pcm_ops_t snd_ensoniq_playback2_ops = {
+ .open = snd_ensoniq_playback2_open,
+ .close = snd_ensoniq_playback2_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ensoniq_hw_params,
+ .hw_free = snd_ensoniq_hw_free,
+ .prepare = snd_ensoniq_playback2_prepare,
+ .trigger = snd_ensoniq_trigger,
+ .pointer = snd_ensoniq_playback2_pointer,
+};
+
+static snd_pcm_ops_t snd_ensoniq_capture_ops = {
+ .open = snd_ensoniq_capture_open,
+ .close = snd_ensoniq_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ensoniq_hw_params,
+ .hw_free = snd_ensoniq_hw_free,
+ .prepare = snd_ensoniq_capture_prepare,
+ .trigger = snd_ensoniq_trigger,
+ .pointer = snd_ensoniq_capture_pointer,
+};
+
+static void snd_ensoniq_pcm_free(snd_pcm_t *pcm)
+{
+ ensoniq_t *ensoniq = pcm->private_data;
+ ensoniq->pcm1 = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+#ifdef CHIP1370
+ err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm);
+#else
+ err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm);
+#endif
+ if (err < 0)
+ return err;
+
+#ifdef CHIP1370
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops);
+#else
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops);
+#endif
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops);
+
+ pcm->private_data = ensoniq;
+ pcm->private_free = snd_ensoniq_pcm_free;
+ pcm->info_flags = 0;
+#ifdef CHIP1370
+ strcpy(pcm->name, "ES1370 DAC2/ADC");
+#else
+ strcpy(pcm->name, "ES1371 DAC2/ADC");
+#endif
+ ensoniq->pcm1 = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+static void snd_ensoniq_pcm_free2(snd_pcm_t *pcm)
+{
+ ensoniq_t *ensoniq = pcm->private_data;
+ ensoniq->pcm2 = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+#ifdef CHIP1370
+ err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm);
+#else
+ err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm);
+#endif
+ if (err < 0)
+ return err;
+
+#ifdef CHIP1370
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops);
+#else
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops);
+#endif
+ pcm->private_data = ensoniq;
+ pcm->private_free = snd_ensoniq_pcm_free2;
+ pcm->info_flags = 0;
+#ifdef CHIP1370
+ strcpy(pcm->name, "ES1370 DAC1");
+#else
+ strcpy(pcm->name, "ES1371 DAC1");
+#endif
+ ensoniq->pcm2 = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+/*
+ * Mixer section
+ */
+
+/*
+ * ENS1371 mixer (including SPDIF interface)
+ */
+#ifdef CHIP1371
+static int snd_ens1373_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ens1373_spdif_default_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ spin_lock_irq(&ensoniq->reg_lock);
+ ucontrol->value.iec958.status[0] = (ensoniq->spdif_default >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (ensoniq->spdif_default >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (ensoniq->spdif_default >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (ensoniq->spdif_default >> 24) & 0xff;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_ens1373_spdif_default_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ((u32)ucontrol->value.iec958.status[0] << 0) |
+ ((u32)ucontrol->value.iec958.status[1] << 8) |
+ ((u32)ucontrol->value.iec958.status[2] << 16) |
+ ((u32)ucontrol->value.iec958.status[3] << 24);
+ spin_lock_irq(&ensoniq->reg_lock);
+ change = ensoniq->spdif_default != val;
+ ensoniq->spdif_default = val;
+ if (change && ensoniq->playback1_substream == NULL && ensoniq->playback2_substream == NULL)
+ outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return change;
+}
+
+static int snd_ens1373_spdif_mask_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * 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 snd_ens1373_spdif_stream_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ spin_lock_irq(&ensoniq->reg_lock);
+ ucontrol->value.iec958.status[0] = (ensoniq->spdif_stream >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (ensoniq->spdif_stream >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (ensoniq->spdif_stream >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (ensoniq->spdif_stream >> 24) & 0xff;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_ens1373_spdif_stream_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ((u32)ucontrol->value.iec958.status[0] << 0) |
+ ((u32)ucontrol->value.iec958.status[1] << 8) |
+ ((u32)ucontrol->value.iec958.status[2] << 16) |
+ ((u32)ucontrol->value.iec958.status[3] << 24);
+ spin_lock_irq(&ensoniq->reg_lock);
+ change = ensoniq->spdif_stream != val;
+ ensoniq->spdif_stream = val;
+ if (change && (ensoniq->playback1_substream != NULL || ensoniq->playback2_substream != NULL))
+ outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return change;
+}
+
+#define ES1371_SPDIF(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \
+ .get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put }
+
+static int snd_es1371_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_es1371_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1373_SPDIF_THRU ? 1 : 0;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ unsigned int nval1, nval2;
+ int change;
+
+ nval1 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_THRU : 0;
+ nval2 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_EN : 0;
+ spin_lock_irq(&ensoniq->reg_lock);
+ change = (ensoniq->ctrl & ES_1373_SPDIF_THRU) != nval1;
+ ensoniq->ctrl &= ~ES_1373_SPDIF_THRU;
+ ensoniq->ctrl |= nval1;
+ ensoniq->cssr &= ~ES_1373_SPDIF_EN;
+ ensoniq->cssr |= nval2;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return change;
+}
+
+
+/* spdif controls */
+static snd_kcontrol_new_t snd_es1371_mixer_spdif[] __devinitdata = {
+ ES1371_SPDIF("IEC958 Playback Switch"),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_ens1373_spdif_info,
+ .get = snd_ens1373_spdif_default_get,
+ .put = snd_ens1373_spdif_default_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+ .info = snd_ens1373_spdif_info,
+ .get = snd_ens1373_spdif_mask_get
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_ens1373_spdif_info,
+ .get = snd_ens1373_spdif_stream_get,
+ .put = snd_ens1373_spdif_stream_put
+ },
+};
+
+
+static int snd_es1373_rear_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_es1373_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ int val = 0;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ if ((ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) == ES_1373_REAR_BIT26)
+ val = 1;
+ ucontrol->value.integer.value[0] = val;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_es1373_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ unsigned int nval1;
+ int change;
+
+ nval1 = ucontrol->value.integer.value[0] ? ES_1373_REAR_BIT26 : (ES_1373_REAR_BIT27|ES_1373_REAR_BIT24);
+ spin_lock_irq(&ensoniq->reg_lock);
+ change = (ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) != nval1;
+ ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24);
+ ensoniq->cssr |= nval1;
+ outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ens1373_rear __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "AC97 2ch->4ch Copy Switch",
+ .info = snd_es1373_rear_info,
+ .get = snd_es1373_rear_get,
+ .put = snd_es1373_rear_put,
+};
+
+static int snd_es1373_line_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_es1373_line_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ int val = 0;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4)
+ val = 1;
+ ucontrol->value.integer.value[0] = val;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_es1373_line_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ int changed;
+ unsigned int ctrl;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ ctrl = ensoniq->ctrl;
+ if (ucontrol->value.integer.value[0])
+ ensoniq->ctrl |= ES_1371_GPIO_OUT(4); /* switch line-in -> rear out */
+ else
+ ensoniq->ctrl &= ~ES_1371_GPIO_OUT(4);
+ changed = (ctrl != ensoniq->ctrl);
+ if (changed)
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return changed;
+}
+
+static snd_kcontrol_new_t snd_ens1373_line __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line In->Rear Out Switch",
+ .info = snd_es1373_line_info,
+ .get = snd_es1373_line_get,
+ .put = snd_es1373_line_put,
+};
+
+static void snd_ensoniq_mixer_free_ac97(ac97_t *ac97)
+{
+ ensoniq_t *ensoniq = ac97->private_data;
+ ensoniq->u.es1371.ac97 = NULL;
+}
+
+static struct {
+ unsigned short vid; /* vendor ID */
+ unsigned short did; /* device ID */
+ unsigned char rev; /* revision */
+} es1371_spdif_present[] __devinitdata = {
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_CT5880_A },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 },
+ { .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
+};
+
+static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq)
+{
+ snd_card_t *card = ensoniq->card;
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int err, idx;
+ static ac97_bus_ops_t ops = {
+ .write = snd_es1371_codec_write,
+ .read = snd_es1371_codec_read,
+ };
+
+ if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = ensoniq;
+ ac97.private_free = snd_ensoniq_mixer_free_ac97;
+ ac97.scaps = AC97_SCAP_AUDIO;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0)
+ return err;
+ for (idx = 0; es1371_spdif_present[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
+ if (ensoniq->pci->vendor == es1371_spdif_present[idx].vid &&
+ ensoniq->pci->device == es1371_spdif_present[idx].did &&
+ ensoniq->rev == es1371_spdif_present[idx].rev) {
+ snd_kcontrol_t *kctl;
+ int i, index = 0;
+
+ ensoniq->spdif_default = ensoniq->spdif_stream = SNDRV_PCM_DEFAULT_CON_SPDIF;
+ outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS));
+
+ if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
+ index++;
+
+ for (i = 0; i < (int)ARRAY_SIZE(snd_es1371_mixer_spdif); i++) {
+ kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq);
+ if (! kctl)
+ return -ENOMEM;
+ kctl->id.index = index;
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ return err;
+ }
+ break;
+ }
+ if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SDAC) {
+ /* mirror rear to front speakers */
+ ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT24);
+ ensoniq->cssr |= ES_1373_REAR_BIT26;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_rear, ensoniq));
+ if (err < 0)
+ return err;
+ }
+ if (((ensoniq->subsystem_vendor_id == 0x1274) &&
+ (ensoniq->subsystem_device_id == 0x2000)) || /* GA-7DXR */
+ ((ensoniq->subsystem_vendor_id == 0x1458) &&
+ (ensoniq->subsystem_device_id == 0xa000))) { /* GA-8IEXP */
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_line, ensoniq));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+#endif /* CHIP1371 */
+
+/* generic control callbacks for ens1370 */
+#ifdef CHIP1370
+#define ENSONIQ_CONTROL(xname, mask) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \
+ .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \
+ .private_value = mask }
+
+static int snd_ensoniq_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ int mask = kcontrol->private_value;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+ int mask = kcontrol->private_value;
+ unsigned int nval;
+ int change;
+
+ nval = ucontrol->value.integer.value[0] ? mask : 0;
+ spin_lock_irq(&ensoniq->reg_lock);
+ change = (ensoniq->ctrl & mask) != nval;
+ ensoniq->ctrl &= ~mask;
+ ensoniq->ctrl |= nval;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return change;
+}
+
+/*
+ * ENS1370 mixer
+ */
+
+static snd_kcontrol_new_t snd_es1370_controls[2] __devinitdata = {
+ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0),
+ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1)
+};
+
+#define ES1370_CONTROLS ARRAY_SIZE(snd_es1370_controls)
+
+static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531)
+{
+ ensoniq_t *ensoniq = ak4531->private_data;
+ ensoniq->u.es1370.ak4531 = NULL;
+}
+
+static int __devinit snd_ensoniq_1370_mixer(ensoniq_t * ensoniq)
+{
+ snd_card_t *card = ensoniq->card;
+ ak4531_t ak4531;
+ unsigned int idx;
+ int err;
+
+ /* try reset AK4531 */
+ outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC));
+ inw(ES_REG(ensoniq, 1370_CODEC));
+ udelay(100);
+ outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC));
+ inw(ES_REG(ensoniq, 1370_CODEC));
+ udelay(100);
+
+ memset(&ak4531, 0, sizeof(ak4531));
+ ak4531.write = snd_es1370_codec_write;
+ ak4531.private_data = ensoniq;
+ ak4531.private_free = snd_ensoniq_mixer_free_ak4531;
+ if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0)
+ return err;
+ for (idx = 0; idx < ES1370_CONTROLS; idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+#endif /* CHIP1370 */
+
+#ifdef SUPPORT_JOYSTICK
+
+#ifdef CHIP1371
+static int __devinit snd_ensoniq_get_joystick_port(int dev)
+{
+ switch (joystick_port[dev]) {
+ case 0: /* disabled */
+ case 1: /* auto-detect */
+ case 0x200:
+ case 0x208:
+ case 0x210:
+ case 0x218:
+ return joystick_port[dev];
+
+ default:
+ printk(KERN_ERR "ens1371: invalid joystick port %#x", joystick_port[dev]);
+ return 0;
+ }
+}
+#else
+static inline int snd_ensoniq_get_joystick_port(int dev)
+{
+ return joystick[dev] ? 0x200 : 0;
+}
+#endif
+
+static int __devinit snd_ensoniq_create_gameport(ensoniq_t *ensoniq, int dev)
+{
+ struct gameport *gp;
+ int io_port;
+
+ io_port = snd_ensoniq_get_joystick_port(dev);
+
+ switch (io_port) {
+ case 0:
+ return -ENOSYS;
+
+ case 1: /* auto_detect */
+ for (io_port = 0x200; io_port <= 0x218; io_port += 8)
+ if (request_region(io_port, 8, "ens137x: gameport"))
+ break;
+ if (io_port > 0x218) {
+ printk(KERN_WARNING "ens137x: no gameport ports available\n");
+ return -EBUSY;
+ }
+ break;
+
+ default:
+ if (!request_region(io_port, 8, "ens137x: gameport")) {
+ printk(KERN_WARNING "ens137x: gameport io port 0x%#x in use\n", io_port);
+ return -EBUSY;
+ }
+ break;
+ }
+
+ ensoniq->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "ens137x: cannot allocate memory for gameport\n");
+ release_region(io_port, 8);
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "ES137x");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(ensoniq->pci));
+ gameport_set_dev_parent(gp, &ensoniq->pci->dev);
+ gp->io = io_port;
+
+ ensoniq->ctrl |= ES_JYSTK_EN;
+#ifdef CHIP1371
+ ensoniq->ctrl &= ~ES_1371_JOY_ASELM;
+ ensoniq->ctrl |= ES_1371_JOY_ASEL((io_port - 0x200) / 8);
+#endif
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+
+ gameport_register_port(ensoniq->gameport);
+
+ return 0;
+}
+
+static void snd_ensoniq_free_gameport(ensoniq_t *ensoniq)
+{
+ if (ensoniq->gameport) {
+ int port = ensoniq->gameport->io;
+
+ gameport_unregister_port(ensoniq->gameport);
+ ensoniq->gameport = NULL;
+ ensoniq->ctrl &= ~ES_JYSTK_EN;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ release_region(port, 8);
+ }
+}
+#else
+static inline int snd_ensoniq_create_gameport(ensoniq_t *ensoniq, long port) { return -ENOSYS; }
+static inline void snd_ensoniq_free_gameport(ensoniq_t *ensoniq) { }
+#endif /* SUPPORT_JOYSTICK */
+
+/*
+
+ */
+
+static void snd_ensoniq_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ensoniq_t *ensoniq = entry->private_data;
+
+#ifdef CHIP1370
+ snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
+#else
+ snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
+#endif
+ snd_iprintf(buffer, "Joystick enable : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
+#ifdef CHIP1370
+ snd_iprintf(buffer, "MIC +5V bias : %s\n", ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off");
+ snd_iprintf(buffer, "Line In to AOUT : %s\n", ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off");
+#else
+ snd_iprintf(buffer, "Joystick port : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
+#endif
+}
+
+static void __devinit snd_ensoniq_proc_init(ensoniq_t * ensoniq)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry))
+ snd_info_set_text_ops(entry, ensoniq, 1024, snd_ensoniq_proc_read);
+}
+
+/*
+
+ */
+
+static int snd_ensoniq_free(ensoniq_t *ensoniq)
+{
+ snd_ensoniq_free_gameport(ensoniq);
+ if (ensoniq->irq < 0)
+ goto __hw_end;
+#ifdef CHIP1370
+ outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL)); /* switch everything off */
+ outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
+#else
+ outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */
+ outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
+#endif
+ synchronize_irq(ensoniq->irq);
+ pci_set_power_state(ensoniq->pci, 3);
+ __hw_end:
+#ifdef CHIP1370
+ if (ensoniq->dma_bug.area)
+ snd_dma_free_pages(&ensoniq->dma_bug);
+#endif
+ if (ensoniq->irq >= 0)
+ free_irq(ensoniq->irq, (void *)ensoniq);
+ pci_release_regions(ensoniq->pci);
+ pci_disable_device(ensoniq->pci);
+ kfree(ensoniq);
+ return 0;
+}
+
+static int snd_ensoniq_dev_free(snd_device_t *device)
+{
+ ensoniq_t *ensoniq = device->device_data;
+ return snd_ensoniq_free(ensoniq);
+}
+
+#ifdef CHIP1371
+static struct {
+ unsigned short svid; /* subsystem vendor ID */
+ unsigned short sdid; /* subsystem device ID */
+} es1371_amplifier_hack[] = {
+ { .svid = 0x107b, .sdid = 0x2150 }, /* Gateway Solo 2150 */
+ { .svid = 0x13bd, .sdid = 0x100c }, /* EV1938 on Mebius PC-MJ100V */
+ { .svid = 0x1102, .sdid = 0x5938 }, /* Targa Xtender300 */
+ { .svid = 0x1102, .sdid = 0x8938 }, /* IPC Topnote G notebook */
+ { .svid = PCI_ANY_ID, .sdid = PCI_ANY_ID }
+};
+static struct {
+ unsigned short vid; /* vendor ID */
+ unsigned short did; /* device ID */
+ unsigned char rev; /* revision */
+} es1371_ac97_reset_hack[] = {
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_CT5880_A },
+ { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 },
+ { .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
+};
+#endif
+
+static int __devinit snd_ensoniq_create(snd_card_t * card,
+ struct pci_dev *pci,
+ ensoniq_t ** rensoniq)
+{
+ ensoniq_t *ensoniq;
+ unsigned short cmdw;
+ unsigned char cmdb;
+#ifdef CHIP1371
+ int idx;
+#endif
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_ensoniq_dev_free,
+ };
+
+ *rensoniq = NULL;
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ ensoniq = kcalloc(1, sizeof(*ensoniq), GFP_KERNEL);
+ if (ensoniq == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&ensoniq->reg_lock);
+ init_MUTEX(&ensoniq->src_mutex);
+ ensoniq->card = card;
+ ensoniq->pci = pci;
+ ensoniq->irq = -1;
+ if ((err = pci_request_regions(pci, "Ensoniq AudioPCI")) < 0) {
+ kfree(ensoniq);
+ pci_disable_device(pci);
+ return err;
+ }
+ ensoniq->port = pci_resource_start(pci, 0);
+ if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ, "Ensoniq AudioPCI", (void *)ensoniq)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_ensoniq_free(ensoniq);
+ return -EBUSY;
+ }
+ ensoniq->irq = pci->irq;
+#ifdef CHIP1370
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ 16, &ensoniq->dma_bug) < 0) {
+ snd_printk("unable to allocate space for phantom area - dma_bug\n");
+ snd_ensoniq_free(ensoniq);
+ return -EBUSY;
+ }
+#endif
+ pci_set_master(pci);
+ pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb);
+ ensoniq->rev = cmdb;
+ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw);
+ ensoniq->subsystem_vendor_id = cmdw;
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw);
+ ensoniq->subsystem_device_id = cmdw;
+#ifdef CHIP1370
+#if 0
+ ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
+#else /* get microphone working */
+ ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
+#endif
+ ensoniq->sctrl = 0;
+ /* initialize the chips */
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+ outl(ensoniq->dma_bug.addr, ES_REG(ensoniq, PHANTOM_FRAME));
+ outl(0, ES_REG(ensoniq, PHANTOM_COUNT));
+#else
+ ensoniq->ctrl = 0;
+ ensoniq->sctrl = 0;
+ ensoniq->cssr = 0;
+ for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++)
+ if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid &&
+ ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) {
+ ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */
+ break;
+ }
+ /* initialize the chips */
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl(0, ES_REG(ensoniq, 1371_LEGACY));
+ for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
+ if (pci->vendor == es1371_ac97_reset_hack[idx].vid &&
+ pci->device == es1371_ac97_reset_hack[idx].did &&
+ ensoniq->rev == es1371_ac97_reset_hack[idx].rev) {
+ unsigned long tmo;
+ signed long tmo2;
+
+ ensoniq->cssr |= ES_1371_ST_AC97_RST;
+ outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+ /* need to delay around 20ms(bleech) to give
+ some CODECs enough time to wakeup */
+ tmo = jiffies + (HZ / 50) + 1;
+ while (1) {
+ tmo2 = tmo - jiffies;
+ if (tmo2 <= 0)
+ break;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(tmo2);
+ }
+ break;
+ }
+ /* AC'97 warm reset to start the bitclk */
+ outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL));
+ inl(ES_REG(ensoniq, CONTROL));
+ udelay(20);
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ /* Init the sample rate converter */
+ snd_es1371_wait_src_ready(ensoniq);
+ outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE));
+ for (idx = 0; idx < 0x80; idx++)
+ snd_es1371_src_write(ensoniq, idx, 0);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12);
+ snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
+ snd_es1371_adc_rate(ensoniq, 22050);
+ snd_es1371_dac1_rate(ensoniq, 22050);
+ snd_es1371_dac2_rate(ensoniq, 22050);
+ /* WARNING:
+ * enabling the sample rate converter without properly programming
+ * its parameters causes the chip to lock up (the SRC busy bit will
+ * be stuck high, and I've found no way to rectify this other than
+ * power cycle) - Thomas Sailer
+ */
+ snd_es1371_wait_src_ready(ensoniq);
+ outl(0, ES_REG(ensoniq, 1371_SMPRATE));
+ /* try reset codec directly */
+ outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC));
+#endif
+ outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL));
+ outb(0x00, ES_REG(ensoniq, UART_RES));
+ outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+ synchronize_irq(ensoniq->irq);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) {
+ snd_ensoniq_free(ensoniq);
+ return err;
+ }
+
+ snd_ensoniq_proc_init(ensoniq);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rensoniq = ensoniq;
+ return 0;
+}
+
+/*
+ * MIDI section
+ */
+
+static void snd_ensoniq_midi_interrupt(ensoniq_t * ensoniq)
+{
+ snd_rawmidi_t * rmidi = ensoniq->rmidi;
+ unsigned char status, mask, byte;
+
+ if (rmidi == NULL)
+ return;
+ /* do Rx at first */
+ spin_lock(&ensoniq->reg_lock);
+ mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
+ while (mask) {
+ status = inb(ES_REG(ensoniq, UART_STATUS));
+ if ((status & mask) == 0)
+ break;
+ byte = inb(ES_REG(ensoniq, UART_DATA));
+ snd_rawmidi_receive(ensoniq->midi_input, &byte, 1);
+ }
+ spin_unlock(&ensoniq->reg_lock);
+
+ /* do Tx at second */
+ spin_lock(&ensoniq->reg_lock);
+ mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0;
+ while (mask) {
+ status = inb(ES_REG(ensoniq, UART_STATUS));
+ if ((status & mask) == 0)
+ break;
+ if (snd_rawmidi_transmit(ensoniq->midi_output, &byte, 1) != 1) {
+ ensoniq->uartc &= ~ES_TXINTENM;
+ outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+ mask &= ~ES_TXRDY;
+ } else {
+ outb(byte, ES_REG(ensoniq, UART_DATA));
+ }
+ }
+ spin_unlock(&ensoniq->reg_lock);
+}
+
+static int snd_ensoniq_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+ ensoniq_t *ensoniq = substream->rmidi->private_data;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ ensoniq->uartm |= ES_MODE_INPUT;
+ ensoniq->midi_input = substream;
+ if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
+ outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
+ outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+ outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
+ }
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_ensoniq_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+ ensoniq_t *ensoniq = substream->rmidi->private_data;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
+ outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+ outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
+ } else {
+ outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL));
+ }
+ ensoniq->midi_input = NULL;
+ ensoniq->uartm &= ~ES_MODE_INPUT;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_ensoniq_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+ ensoniq_t *ensoniq = substream->rmidi->private_data;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ ensoniq->uartm |= ES_MODE_OUTPUT;
+ ensoniq->midi_output = substream;
+ if (!(ensoniq->uartm & ES_MODE_INPUT)) {
+ outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
+ outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+ outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
+ }
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static int snd_ensoniq_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+ ensoniq_t *ensoniq = substream->rmidi->private_data;
+
+ spin_lock_irq(&ensoniq->reg_lock);
+ if (!(ensoniq->uartm & ES_MODE_INPUT)) {
+ outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+ outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
+ } else {
+ outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL));
+ }
+ ensoniq->midi_output = NULL;
+ ensoniq->uartm &= ~ES_MODE_OUTPUT;
+ spin_unlock_irq(&ensoniq->reg_lock);
+ return 0;
+}
+
+static void snd_ensoniq_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ ensoniq_t *ensoniq = substream->rmidi->private_data;
+ int idx;
+
+ spin_lock_irqsave(&ensoniq->reg_lock, flags);
+ if (up) {
+ if ((ensoniq->uartc & ES_RXINTEN) == 0) {
+ /* empty input FIFO */
+ for (idx = 0; idx < 32; idx++)
+ inb(ES_REG(ensoniq, UART_DATA));
+ ensoniq->uartc |= ES_RXINTEN;
+ outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+ }
+ } else {
+ if (ensoniq->uartc & ES_RXINTEN) {
+ ensoniq->uartc &= ~ES_RXINTEN;
+ outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+ }
+ }
+ spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+}
+
+static void snd_ensoniq_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ ensoniq_t *ensoniq = substream->rmidi->private_data;
+ unsigned char byte;
+
+ spin_lock_irqsave(&ensoniq->reg_lock, flags);
+ if (up) {
+ if (ES_TXINTENI(ensoniq->uartc) == 0) {
+ ensoniq->uartc |= ES_TXINTENO(1);
+ /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+ while (ES_TXINTENI(ensoniq->uartc) == 1 &&
+ (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) {
+ if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ ensoniq->uartc &= ~ES_TXINTENM;
+ } else {
+ outb(byte, ES_REG(ensoniq, UART_DATA));
+ }
+ }
+ outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+ }
+ } else {
+ if (ES_TXINTENI(ensoniq->uartc) == 1) {
+ ensoniq->uartc &= ~ES_TXINTENM;
+ outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+ }
+ }
+ spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+}
+
+static snd_rawmidi_ops_t snd_ensoniq_midi_output =
+{
+ .open = snd_ensoniq_midi_output_open,
+ .close = snd_ensoniq_midi_output_close,
+ .trigger = snd_ensoniq_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_ensoniq_midi_input =
+{
+ .open = snd_ensoniq_midi_input_open,
+ .close = snd_ensoniq_midi_input_close,
+ .trigger = snd_ensoniq_midi_input_trigger,
+};
+
+static int __devinit snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmidi_t **rrawmidi)
+{
+ snd_rawmidi_t *rmidi;
+ int err;
+
+ if (rrawmidi)
+ *rrawmidi = NULL;
+ if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
+ return err;
+#ifdef CHIP1370
+ strcpy(rmidi->name, "ES1370");
+#else
+ strcpy(rmidi->name, "ES1371");
+#endif
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = ensoniq;
+ ensoniq->rmidi = rmidi;
+ if (rrawmidi)
+ *rrawmidi = rmidi;
+ return 0;
+}
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ensoniq_t *ensoniq = dev_id;
+ unsigned int status, sctrl;
+
+ if (ensoniq == NULL)
+ return IRQ_NONE;
+
+ status = inl(ES_REG(ensoniq, STATUS));
+ if (!(status & ES_INTR))
+ return IRQ_NONE;
+
+ spin_lock(&ensoniq->reg_lock);
+ sctrl = ensoniq->sctrl;
+ if (status & ES_DAC1)
+ sctrl &= ~ES_P1_INT_EN;
+ if (status & ES_DAC2)
+ sctrl &= ~ES_P2_INT_EN;
+ if (status & ES_ADC)
+ sctrl &= ~ES_R1_INT_EN;
+ outl(sctrl, ES_REG(ensoniq, SERIAL));
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ spin_unlock(&ensoniq->reg_lock);
+
+ if (status & ES_UART)
+ snd_ensoniq_midi_interrupt(ensoniq);
+ if ((status & ES_DAC2) && ensoniq->playback2_substream)
+ snd_pcm_period_elapsed(ensoniq->playback2_substream);
+ if ((status & ES_ADC) && ensoniq->capture_substream)
+ snd_pcm_period_elapsed(ensoniq->capture_substream);
+ if ((status & ES_DAC1) && ensoniq->playback1_substream)
+ snd_pcm_period_elapsed(ensoniq->playback1_substream);
+ return IRQ_HANDLED;
+}
+
+static int __devinit snd_audiopci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ ensoniq_t *ensoniq;
+ int err, pcm_devs[2];
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pcm_devs[0] = 0; pcm_devs[1] = 1;
+#ifdef CHIP1370
+ if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#endif
+#ifdef CHIP1371
+ if ((err = snd_ensoniq_1371_mixer(ensoniq)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#endif
+ if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_ensoniq_create_gameport(ensoniq, dev);
+
+ strcpy(card->driver, DRIVER_NAME);
+
+ strcpy(card->shortname, "Ensoniq AudioPCI");
+ sprintf(card->longname, "%s %s at 0x%lx, irq %i",
+ card->shortname,
+ card->driver,
+ ensoniq->port,
+ ensoniq->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_audiopci_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = DRIVER_NAME,
+ .id_table = snd_audiopci_ids,
+ .probe = snd_audiopci_probe,
+ .remove = __devexit_p(snd_audiopci_remove),
+};
+
+static int __init alsa_card_ens137x_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_ens137x_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ens137x_init)
+module_exit(alsa_card_ens137x_exit)
diff --git a/sound/pci/ens1371.c b/sound/pci/ens1371.c
new file mode 100644
index 00000000000..ca0da0ab40b
--- /dev/null
+++ b/sound/pci/ens1371.c
@@ -0,0 +1,2 @@
+#define CHIP1371
+#include "ens1370.c"
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
new file mode 100644
index 00000000000..b4ca8adf393
--- /dev/null
+++ b/sound/pci/es1938.c
@@ -0,0 +1,1773 @@
+/*
+ * Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard
+ * Copyright (c) by Jaromir Koutek <miri@punknet.cz>,
+ * Jaroslav Kysela <perex@suse.cz>,
+ * Thomas Sailer <sailer@ife.ee.ethz.ch>,
+ * Abramo Bagnara <abramo@alsa-project.org>,
+ * Markus Gruber <gruber@eikon.tum.de>
+ *
+ * Rewritten from sonicvibes.c source.
+ *
+ * TODO:
+ * Rewrite better spinlocks
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ NOTES:
+ - Capture data is written unaligned starting from dma_base + 1 so I need to
+ disable mmap and to add a copy callback.
+ - After several cycle of the following:
+ while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done
+ a "playback write error (DMA or IRQ trouble?)" may happen.
+ This is due to playback interrupts not generated.
+ I suspect a timing issue.
+ - Sometimes the interrupt handler is invoked wrongly during playback.
+ This generates some harmless "Unexpected hw_pointer: wrong interrupt
+ acknowledge".
+ I've seen that using small period sizes.
+ Reproducible with:
+ mpg123 test.mp3 &
+ hdparm -t -T /dev/hda
+*/
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/opl3.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Jaromir Koutek <miri@punknet.cz>");
+MODULE_DESCRIPTION("ESS Solo-1");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ESS,ES1938},"
+ "{ESS,ES1946},"
+ "{ESS,ES1969},"
+ "{TerraTec,128i PCI}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS 0x125d
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ES1938
+#define PCI_DEVICE_ID_ESS_ES1938 0x1969
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ESS Solo-1 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ESS Solo-1 soundcard.");
+
+#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x)
+
+#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x)
+
+#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x)
+
+#define SL_PCI_LEGACYCONTROL 0x40
+#define SL_PCI_CONFIG 0x50
+#define SL_PCI_DDMACONTROL 0x60
+
+#define ESSIO_REG_AUDIO2DMAADDR 0
+#define ESSIO_REG_AUDIO2DMACOUNT 4
+#define ESSIO_REG_AUDIO2MODE 6
+#define ESSIO_REG_IRQCONTROL 7
+
+#define ESSDM_REG_DMAADDR 0x00
+#define ESSDM_REG_DMACOUNT 0x04
+#define ESSDM_REG_DMACOMMAND 0x08
+#define ESSDM_REG_DMASTATUS 0x08
+#define ESSDM_REG_DMAMODE 0x0b
+#define ESSDM_REG_DMACLEAR 0x0d
+#define ESSDM_REG_DMAMASK 0x0f
+
+#define ESSSB_REG_FMLOWADDR 0x00
+#define ESSSB_REG_FMHIGHADDR 0x02
+#define ESSSB_REG_MIXERADDR 0x04
+#define ESSSB_REG_MIXERDATA 0x05
+
+#define ESSSB_IREG_AUDIO1 0x14
+#define ESSSB_IREG_MICMIX 0x1a
+#define ESSSB_IREG_RECSRC 0x1c
+#define ESSSB_IREG_MASTER 0x32
+#define ESSSB_IREG_FM 0x36
+#define ESSSB_IREG_AUXACD 0x38
+#define ESSSB_IREG_AUXB 0x3a
+#define ESSSB_IREG_PCSPEAKER 0x3c
+#define ESSSB_IREG_LINE 0x3e
+#define ESSSB_IREG_SPATCONTROL 0x50
+#define ESSSB_IREG_SPATLEVEL 0x52
+#define ESSSB_IREG_MASTER_LEFT 0x60
+#define ESSSB_IREG_MASTER_RIGHT 0x62
+#define ESSSB_IREG_MPU401CONTROL 0x64
+#define ESSSB_IREG_MICMIXRECORD 0x68
+#define ESSSB_IREG_AUDIO2RECORD 0x69
+#define ESSSB_IREG_AUXACDRECORD 0x6a
+#define ESSSB_IREG_FMRECORD 0x6b
+#define ESSSB_IREG_AUXBRECORD 0x6c
+#define ESSSB_IREG_MONO 0x6d
+#define ESSSB_IREG_LINERECORD 0x6e
+#define ESSSB_IREG_MONORECORD 0x6f
+#define ESSSB_IREG_AUDIO2SAMPLE 0x70
+#define ESSSB_IREG_AUDIO2MODE 0x71
+#define ESSSB_IREG_AUDIO2FILTER 0x72
+#define ESSSB_IREG_AUDIO2TCOUNTL 0x74
+#define ESSSB_IREG_AUDIO2TCOUNTH 0x76
+#define ESSSB_IREG_AUDIO2CONTROL1 0x78
+#define ESSSB_IREG_AUDIO2CONTROL2 0x7a
+#define ESSSB_IREG_AUDIO2 0x7c
+
+#define ESSSB_REG_RESET 0x06
+
+#define ESSSB_REG_READDATA 0x0a
+#define ESSSB_REG_WRITEDATA 0x0c
+#define ESSSB_REG_READSTATUS 0x0c
+
+#define ESSSB_REG_STATUS 0x0e
+
+#define ESS_CMD_EXTSAMPLERATE 0xa1
+#define ESS_CMD_FILTERDIV 0xa2
+#define ESS_CMD_DMACNTRELOADL 0xa4
+#define ESS_CMD_DMACNTRELOADH 0xa5
+#define ESS_CMD_ANALOGCONTROL 0xa8
+#define ESS_CMD_IRQCONTROL 0xb1
+#define ESS_CMD_DRQCONTROL 0xb2
+#define ESS_CMD_RECLEVEL 0xb4
+#define ESS_CMD_SETFORMAT 0xb6
+#define ESS_CMD_SETFORMAT2 0xb7
+#define ESS_CMD_DMACONTROL 0xb8
+#define ESS_CMD_DMATYPE 0xb9
+#define ESS_CMD_OFFSETLEFT 0xba
+#define ESS_CMD_OFFSETRIGHT 0xbb
+#define ESS_CMD_READREG 0xc0
+#define ESS_CMD_ENABLEEXT 0xc6
+#define ESS_CMD_PAUSEDMA 0xd0
+#define ESS_CMD_ENABLEAUDIO1 0xd1
+#define ESS_CMD_STOPAUDIO1 0xd3
+#define ESS_CMD_AUDIO1STATUS 0xd8
+#define ESS_CMD_CONTDMA 0xd4
+#define ESS_CMD_TESTIRQ 0xf2
+
+#define ESS_RECSRC_MIC 0
+#define ESS_RECSRC_AUXACD 2
+#define ESS_RECSRC_AUXB 5
+#define ESS_RECSRC_LINE 6
+#define ESS_RECSRC_NONE 7
+
+#define DAC1 0x01
+#define ADC1 0x02
+#define DAC2 0x04
+
+/*
+
+ */
+
+typedef struct _snd_es1938 es1938_t;
+
+#define SAVED_REG_SIZE 32 /* max. number of registers to save */
+
+struct _snd_es1938 {
+ int irq;
+
+ unsigned long io_port;
+ unsigned long sb_port;
+ unsigned long vc_port;
+ unsigned long mpu_port;
+ unsigned long game_port;
+ unsigned long ddma_port;
+
+ unsigned char irqmask;
+ unsigned char revision;
+
+ snd_kcontrol_t *hw_volume;
+ snd_kcontrol_t *hw_switch;
+ snd_kcontrol_t *master_volume;
+ snd_kcontrol_t *master_switch;
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *capture_substream;
+ snd_pcm_substream_t *playback1_substream;
+ snd_pcm_substream_t *playback2_substream;
+ snd_kmixer_t *mixer;
+ snd_rawmidi_t *rmidi;
+
+ unsigned int dma1_size;
+ unsigned int dma2_size;
+ unsigned int dma1_start;
+ unsigned int dma2_start;
+ unsigned int dma1_shift;
+ unsigned int dma2_shift;
+ unsigned int active;
+
+ spinlock_t reg_lock;
+ spinlock_t mixer_lock;
+ snd_info_entry_t *proc_entry;
+
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+#ifdef CONFIG_PM
+ unsigned char saved_regs[SAVED_REG_SIZE];
+#endif
+};
+
+static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_es1938_ids[] = {
+ { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Solo-1 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_es1938_ids);
+
+#define RESET_LOOP_TIMEOUT 0x10000
+#define WRITE_LOOP_TIMEOUT 0x10000
+#define GET_LOOP_TIMEOUT 0x01000
+
+#undef REG_DEBUG
+/* -----------------------------------------------------------------
+ * Write to a mixer register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+ outb(reg, SLSB_REG(chip, MIXERADDR));
+ outb(val, SLSB_REG(chip, MIXERDATA));
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+ snd_printk("Mixer reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read from a mixer register
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_read(es1938_t *chip, unsigned char reg)
+{
+ int data;
+ unsigned long flags;
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+ outb(reg, SLSB_REG(chip, MIXERADDR));
+ data = inb(SLSB_REG(chip, MIXERDATA));
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+ snd_printk("Mixer reg %02x now is %02x\n", reg, data);
+#endif
+ return data;
+}
+
+/* -----------------------------------------------------------------
+ * Write to some bits of a mixer register (return old value)
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+ unsigned long flags;
+ unsigned char old, new, oval;
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+ outb(reg, SLSB_REG(chip, MIXERADDR));
+ old = inb(SLSB_REG(chip, MIXERDATA));
+ oval = old & mask;
+ if (val != oval) {
+ new = (old & ~mask) | (val & mask);
+ outb(new, SLSB_REG(chip, MIXERDATA));
+#ifdef REG_DEBUG
+ snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+ }
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ return oval;
+}
+
+/* -----------------------------------------------------------------
+ * Write command to Controller Registers
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write_cmd(es1938_t *chip, unsigned char cmd)
+{
+ int i;
+ unsigned char v;
+ for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {
+ if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {
+ outb(cmd, SLSB_REG(chip, WRITEDATA));
+ return;
+ }
+ }
+ printk("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
+}
+
+/* -----------------------------------------------------------------
+ * Read the Read Data Buffer
+ * -----------------------------------------------------------------*/
+static int snd_es1938_get_byte(es1938_t *chip)
+{
+ int i;
+ unsigned char v;
+ for (i = GET_LOOP_TIMEOUT; i; i--)
+ if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)
+ return inb(SLSB_REG(chip, READDATA));
+ snd_printk("get_byte timeout: status 0x02%x\n", v);
+ return -ENODEV;
+}
+
+/* -----------------------------------------------------------------
+ * Write value cmd register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_es1938_write_cmd(chip, reg);
+ snd_es1938_write_cmd(chip, val);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+ snd_printk("Reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read data from cmd register and return it
+ * -----------------------------------------------------------------*/
+static unsigned char snd_es1938_read(es1938_t *chip, unsigned char reg)
+{
+ unsigned char val;
+ unsigned long flags;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+ snd_es1938_write_cmd(chip, reg);
+ val = snd_es1938_get_byte(chip);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+ snd_printk("Reg %02x now is %02x\n", reg, val);
+#endif
+ return val;
+}
+
+/* -----------------------------------------------------------------
+ * Write data to cmd register and return old value
+ * -----------------------------------------------------------------*/
+static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+ unsigned long flags;
+ unsigned char old, new, oval;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+ snd_es1938_write_cmd(chip, reg);
+ old = snd_es1938_get_byte(chip);
+ oval = old & mask;
+ if (val != oval) {
+ snd_es1938_write_cmd(chip, reg);
+ new = (old & ~mask) | (val & mask);
+ snd_es1938_write_cmd(chip, new);
+#ifdef REG_DEBUG
+ snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return oval;
+}
+
+/* --------------------------------------------------------------------
+ * Reset the chip
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset(es1938_t *chip)
+{
+ int i;
+
+ outb(3, SLSB_REG(chip, RESET));
+ inb(SLSB_REG(chip, RESET));
+ outb(0, SLSB_REG(chip, RESET));
+ for (i = 0; i < RESET_LOOP_TIMEOUT; i++) {
+ if (inb(SLSB_REG(chip, STATUS)) & 0x80) {
+ if (inb(SLSB_REG(chip, READDATA)) == 0xaa)
+ goto __next;
+ }
+ }
+ snd_printk("ESS Solo-1 reset failed\n");
+
+ __next:
+ snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);
+
+ /* Demand transfer DMA: 4 bytes per DMA request */
+ snd_es1938_write(chip, ESS_CMD_DMATYPE, 2);
+
+ /* Change behaviour of register A1
+ 4x oversampling
+ 2nd channel DAC asynchronous */
+ snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32);
+ /* enable/select DMA channel and IRQ channel */
+ snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50);
+ snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50);
+ snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1);
+ /* Set spatializer parameters to recommended values */
+ snd_es1938_mixer_write(chip, 0x54, 0x8f);
+ snd_es1938_mixer_write(chip, 0x56, 0x95);
+ snd_es1938_mixer_write(chip, 0x58, 0x94);
+ snd_es1938_mixer_write(chip, 0x5a, 0x80);
+}
+
+/* --------------------------------------------------------------------
+ * Reset the FIFOs
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset_fifo(es1938_t *chip)
+{
+ outb(2, SLSB_REG(chip, RESET));
+ outb(0, SLSB_REG(chip, RESET));
+}
+
+static ratnum_t clocks[2] = {
+ {
+ .num = 793800,
+ .den_min = 1,
+ .den_max = 128,
+ .den_step = 1,
+ },
+ {
+ .num = 768000,
+ .den_min = 1,
+ .den_max = 128,
+ .den_step = 1,
+ }
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
+ .nrats = 2,
+ .rats = clocks,
+};
+
+
+static void snd_es1938_rate_set(es1938_t *chip,
+ snd_pcm_substream_t *substream,
+ int mode)
+{
+ unsigned int bits, div0;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (runtime->rate_num == clocks[0].num)
+ bits = 128 - runtime->rate_den;
+ else
+ bits = 256 - runtime->rate_den;
+
+ /* set filter register */
+ div0 = 256 - 7160000*20/(8*82*runtime->rate);
+
+ if (mode == DAC2) {
+ snd_es1938_mixer_write(chip, 0x70, bits);
+ snd_es1938_mixer_write(chip, 0x72, div0);
+ } else {
+ snd_es1938_write(chip, 0xA1, bits);
+ snd_es1938_write(chip, 0xA2, div0);
+ }
+}
+
+/* --------------------------------------------------------------------
+ * Configure Solo1 builtin DMA Controller
+ * --------------------------------------------------------------------*/
+
+static void snd_es1938_playback1_setdma(es1938_t *chip)
+{
+ outb(0x00, SLIO_REG(chip, AUDIO2MODE));
+ outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR));
+ outw(0, SLIO_REG(chip, AUDIO2DMACOUNT));
+ outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT));
+}
+
+static void snd_es1938_playback2_setdma(es1938_t *chip)
+{
+ /* Enable DMA controller */
+ outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+ /* 1. Master reset */
+ outb(0, SLDM_REG(chip, DMACLEAR));
+ /* 2. Mask DMA */
+ outb(1, SLDM_REG(chip, DMAMASK));
+ outb(0x18, SLDM_REG(chip, DMAMODE));
+ outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+ outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+ /* 3. Unmask DMA */
+ outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+static void snd_es1938_capture_setdma(es1938_t *chip)
+{
+ /* Enable DMA controller */
+ outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+ /* 1. Master reset */
+ outb(0, SLDM_REG(chip, DMACLEAR));
+ /* 2. Mask DMA */
+ outb(1, SLDM_REG(chip, DMAMASK));
+ outb(0x14, SLDM_REG(chip, DMAMODE));
+ outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+ outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+ /* 3. Unmask DMA */
+ outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+/* ----------------------------------------------------------------------
+ *
+ * *** PCM part ***
+ */
+
+static int snd_es1938_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ int val;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ val = 0x0f;
+ chip->active |= ADC1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ val = 0x00;
+ chip->active &= ~ADC1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+ return 0;
+}
+
+static int snd_es1938_playback1_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* According to the documentation this should be:
+ 0x13 but that value may randomly swap stereo channels */
+ snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x92);
+ udelay(10);
+ snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93);
+ /* This two stage init gives the FIFO -> DAC connection time to
+ * settle before first data from DMA flows in. This should ensure
+ * no swapping of stereo channels. Report a bug if otherwise :-) */
+ outb(0x0a, SLIO_REG(chip, AUDIO2MODE));
+ chip->active |= DAC2;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ outb(0, SLIO_REG(chip, AUDIO2MODE));
+ snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0);
+ chip->active &= ~DAC2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int snd_es1938_playback2_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ int val;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ val = 5;
+ chip->active |= DAC1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ val = 0;
+ chip->active &= ~DAC1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+ return 0;
+}
+
+static int snd_es1938_playback_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ switch (substream->number) {
+ case 0:
+ return snd_es1938_playback1_trigger(substream, cmd);
+ case 1:
+ return snd_es1938_playback2_trigger(substream, cmd);
+ }
+ snd_BUG();
+ return -EINVAL;
+}
+
+/* --------------------------------------------------------------------
+ * First channel for Extended Mode Audio 1 ADC Operation
+ * --------------------------------------------------------------------*/
+static int snd_es1938_capture_prepare(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int u, is8, mono;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ chip->dma1_size = size;
+ chip->dma1_start = runtime->dma_addr;
+
+ mono = (runtime->channels > 1) ? 0 : 1;
+ is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+ u = snd_pcm_format_unsigned(runtime->format);
+
+ chip->dma1_shift = 2 - mono - is8;
+
+ snd_es1938_reset_fifo(chip);
+
+ /* program type */
+ snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+ /* set clock and counters */
+ snd_es1938_rate_set(chip, substream, ADC1);
+
+ count = 0x10000 - count;
+ snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+ snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+ /* initialize and configure ADC */
+ snd_es1938_write(chip, ESS_CMD_SETFORMAT2, u ? 0x51 : 0x71);
+ snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 0x90 |
+ (u ? 0x00 : 0x20) |
+ (is8 ? 0x00 : 0x04) |
+ (mono ? 0x40 : 0x08));
+
+ // snd_es1938_reset_fifo(chip);
+
+ /* 11. configure system interrupt controller and DMA controller */
+ snd_es1938_capture_setdma(chip);
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------------
+ * Second Audio channel DAC Operation
+ * ------------------------------------------------------------------------------*/
+static int snd_es1938_playback1_prepare(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int u, is8, mono;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ chip->dma2_size = size;
+ chip->dma2_start = runtime->dma_addr;
+
+ mono = (runtime->channels > 1) ? 0 : 1;
+ is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+ u = snd_pcm_format_unsigned(runtime->format);
+
+ chip->dma2_shift = 2 - mono - is8;
+
+ snd_es1938_reset_fifo(chip);
+
+ /* set clock and counters */
+ snd_es1938_rate_set(chip, substream, DAC2);
+
+ count >>= 1;
+ count = 0x10000 - count;
+ snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff);
+ snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8);
+
+ /* initialize and configure Audio 2 DAC */
+ snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) | (mono ? 0 : 2) | (is8 ? 0 : 1));
+
+ /* program DMA */
+ snd_es1938_playback1_setdma(chip);
+
+ return 0;
+}
+
+static int snd_es1938_playback2_prepare(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int u, is8, mono;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ chip->dma1_size = size;
+ chip->dma1_start = runtime->dma_addr;
+
+ mono = (runtime->channels > 1) ? 0 : 1;
+ is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+ u = snd_pcm_format_unsigned(runtime->format);
+
+ chip->dma1_shift = 2 - mono - is8;
+
+ count = 0x10000 - count;
+
+ /* reset */
+ snd_es1938_reset_fifo(chip);
+
+ snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+ /* set clock and counters */
+ snd_es1938_rate_set(chip, substream, DAC1);
+ snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+ snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+ /* initialized and configure DAC */
+ snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00);
+ snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71);
+ snd_es1938_write(chip, ESS_CMD_SETFORMAT2,
+ 0x90 | (mono ? 0x40 : 0x08) |
+ (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20));
+
+ /* program DMA */
+ snd_es1938_playback2_setdma(chip);
+
+ return 0;
+}
+
+static int snd_es1938_playback_prepare(snd_pcm_substream_t *substream)
+{
+ switch (substream->number) {
+ case 0:
+ return snd_es1938_playback1_prepare(substream);
+ case 1:
+ return snd_es1938_playback2_prepare(substream);
+ }
+ snd_BUG();
+ return -EINVAL;
+}
+
+static snd_pcm_uframes_t snd_es1938_capture_pointer(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+ size_t old, new;
+#if 1
+ /* This stuff is *needed*, don't ask why - AB */
+ old = inw(SLDM_REG(chip, DMACOUNT));
+ while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+ old = new;
+ ptr = chip->dma1_size - 1 - new;
+#else
+ ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+ return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback1_pointer(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+#if 1
+ ptr = chip->dma2_size - inw(SLIO_REG(chip, AUDIO2DMACOUNT));
+#else
+ ptr = inl(SLIO_REG(chip, AUDIO2DMAADDR)) - chip->dma2_start;
+#endif
+ return ptr >> chip->dma2_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback2_pointer(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+ size_t old, new;
+#if 1
+ /* This stuff is *needed*, don't ask why - AB */
+ old = inw(SLDM_REG(chip, DMACOUNT));
+ while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+ old = new;
+ ptr = chip->dma1_size - 1 - new;
+#else
+ ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+ return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback_pointer(snd_pcm_substream_t *substream)
+{
+ switch (substream->number) {
+ case 0:
+ return snd_es1938_playback1_pointer(substream);
+ case 1:
+ return snd_es1938_playback2_pointer(substream);
+ }
+ snd_BUG();
+ return -EINVAL;
+}
+
+static int snd_es1938_capture_copy(snd_pcm_substream_t *substream,
+ int channel,
+ snd_pcm_uframes_t pos,
+ void __user *dst,
+ snd_pcm_uframes_t count)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ pos <<= chip->dma1_shift;
+ count <<= chip->dma1_shift;
+ snd_assert(pos + count <= chip->dma1_size, return -EINVAL);
+ if (pos + count < chip->dma1_size) {
+ if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1))
+ return -EFAULT;
+ if (put_user(runtime->dma_area[0], ((unsigned char __user *)dst) + count - 1))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/*
+ * buffer management
+ */
+static int snd_es1938_pcm_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t * hw_params)
+
+{
+ int err;
+
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ return 0;
+}
+
+static int snd_es1938_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* ----------------------------------------------------------------------
+ * Audio1 Capture (ADC)
+ * ----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_capture =
+{
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 6000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 0x8000, /* DMA controller screws on higher values */
+ .period_bytes_min = 64,
+ .period_bytes_max = 0x8000,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 256,
+};
+
+/* -----------------------------------------------------------------------
+ * Audio2 Playback (DAC)
+ * -----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 6000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 0x8000, /* DMA controller screws on higher values */
+ .period_bytes_min = 64,
+ .period_bytes_max = 0x8000,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 256,
+};
+
+static int snd_es1938_capture_open(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (chip->playback2_substream)
+ return -EAGAIN;
+ chip->capture_substream = substream;
+ runtime->hw = snd_es1938_capture;
+ snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_clocks);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+ return 0;
+}
+
+static int snd_es1938_playback_open(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ switch (substream->number) {
+ case 0:
+ chip->playback1_substream = substream;
+ break;
+ case 1:
+ if (chip->capture_substream)
+ return -EAGAIN;
+ chip->playback2_substream = substream;
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+ runtime->hw = snd_es1938_playback;
+ snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_clocks);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+ return 0;
+}
+
+static int snd_es1938_capture_close(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->capture_substream = NULL;
+ return 0;
+}
+
+static int snd_es1938_playback_close(snd_pcm_substream_t * substream)
+{
+ es1938_t *chip = snd_pcm_substream_chip(substream);
+
+ switch (substream->number) {
+ case 0:
+ chip->playback1_substream = NULL;
+ break;
+ case 1:
+ chip->playback2_substream = NULL;
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_ops_t snd_es1938_playback_ops = {
+ .open = snd_es1938_playback_open,
+ .close = snd_es1938_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_es1938_pcm_hw_params,
+ .hw_free = snd_es1938_pcm_hw_free,
+ .prepare = snd_es1938_playback_prepare,
+ .trigger = snd_es1938_playback_trigger,
+ .pointer = snd_es1938_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_es1938_capture_ops = {
+ .open = snd_es1938_capture_open,
+ .close = snd_es1938_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_es1938_pcm_hw_params,
+ .hw_free = snd_es1938_pcm_hw_free,
+ .prepare = snd_es1938_capture_prepare,
+ .trigger = snd_es1938_capture_trigger,
+ .pointer = snd_es1938_capture_pointer,
+ .copy = snd_es1938_capture_copy,
+};
+
+static void snd_es1938_free_pcm(snd_pcm_t *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_es1938_new_pcm(es1938_t *chip, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops);
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_es1938_free_pcm;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ESS Solo-1");
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+
+ chip->pcm = pcm;
+ return 0;
+}
+
+/* -------------------------------------------------------------------
+ *
+ * *** Mixer part ***
+ */
+
+static int snd_es1938_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[8] = {
+ "Mic", "Mic Master", "CD", "AOUT",
+ "Mic1", "Mix", "Line", "Master"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 8;
+ if (uinfo->value.enumerated.item > 7)
+ uinfo->value.enumerated.item = 7;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_es1938_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = snd_es1938_mixer_read(chip, 0x1c) & 0x07;
+ return 0;
+}
+
+static int snd_es1938_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned char val = ucontrol->value.enumerated.item[0];
+
+ if (val > 7)
+ return -EINVAL;
+ return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val;
+}
+
+static int snd_es1938_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_es1938_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned char val = snd_es1938_mixer_read(chip, 0x50);
+ ucontrol->value.integer.value[0] = !!(val & 8);
+ return 0;
+}
+
+static int snd_es1938_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned char oval, nval;
+ int change;
+ nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04;
+ oval = snd_es1938_mixer_read(chip, 0x50) & 0x0c;
+ change = nval != oval;
+ if (change) {
+ snd_es1938_mixer_write(chip, 0x50, nval & ~0x04);
+ snd_es1938_mixer_write(chip, 0x50, nval);
+ }
+ return change;
+}
+
+static int snd_es1938_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 63;
+ return 0;
+}
+
+static int snd_es1938_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = snd_es1938_mixer_read(chip, 0x61) & 0x3f;
+ ucontrol->value.integer.value[1] = snd_es1938_mixer_read(chip, 0x63) & 0x3f;
+ return 0;
+}
+
+static int snd_es1938_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_es1938_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = !(snd_es1938_mixer_read(chip, 0x61) & 0x40);
+ ucontrol->value.integer.value[1] = !(snd_es1938_mixer_read(chip, 0x63) & 0x40);
+ return 0;
+}
+
+static void snd_es1938_hwv_free(snd_kcontrol_t *kcontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ chip->master_volume = NULL;
+ chip->master_switch = NULL;
+ chip->hw_volume = NULL;
+ chip->hw_switch = NULL;
+}
+
+static int snd_es1938_reg_bits(es1938_t *chip, unsigned char reg,
+ unsigned char mask, unsigned char val)
+{
+ if (reg < 0xa0)
+ return snd_es1938_mixer_bits(chip, reg, mask, val);
+ else
+ return snd_es1938_bits(chip, reg, mask, val);
+}
+
+static int snd_es1938_reg_read(es1938_t *chip, unsigned char reg)
+{
+ if (reg < 0xa0)
+ return snd_es1938_mixer_read(chip, reg);
+ else
+ return snd_es1938_read(chip, reg);
+}
+
+#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_es1938_info_single, \
+ .get = snd_es1938_get_single, .put = snd_es1938_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_es1938_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_es1938_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+ int val;
+
+ val = snd_es1938_reg_read(chip, reg);
+ ucontrol->value.integer.value[0] = (val >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int snd_es1938_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+ unsigned char val;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = mask - val;
+ mask <<= shift;
+ val <<= shift;
+ return snd_es1938_reg_bits(chip, reg, mask, val) != val;
+}
+
+#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_es1938_info_double, \
+ .get = snd_es1938_get_double, .put = snd_es1938_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_es1938_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_es1938_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x07;
+ int shift_right = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ unsigned char left, right;
+
+ left = snd_es1938_reg_read(chip, left_reg);
+ if (left_reg != right_reg)
+ right = snd_es1938_reg_read(chip, right_reg);
+ else
+ right = left;
+ ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+ ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+ if (invert) {
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ }
+ return 0;
+}
+
+static int snd_es1938_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ es1938_t *chip = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x07;
+ int shift_right = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ int change;
+ unsigned char val1, val2, mask1, mask2;
+
+ val1 = ucontrol->value.integer.value[0] & mask;
+ val2 = ucontrol->value.integer.value[1] & mask;
+ if (invert) {
+ val1 = mask - val1;
+ val2 = mask - val2;
+ }
+ val1 <<= shift_left;
+ val2 <<= shift_right;
+ mask1 = mask << shift_left;
+ mask2 = mask << shift_right;
+ if (left_reg != right_reg) {
+ change = 0;
+ if (snd_es1938_reg_bits(chip, left_reg, mask1, val1) != val1)
+ change = 1;
+ if (snd_es1938_reg_bits(chip, right_reg, mask2, val2) != val2)
+ change = 1;
+ } else {
+ change = (snd_es1938_reg_bits(chip, left_reg, mask1 | mask2,
+ val1 | val2) != (val1 | val2));
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_es1938_controls[] = {
+ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Hardware Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_es1938_info_hw_volume,
+ .get = snd_es1938_get_hw_volume,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Hardware Master Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_es1938_info_hw_switch,
+ .get = snd_es1938_get_hw_switch,
+},
+ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
+ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
+ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
+ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
+ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_es1938_info_mux,
+ .get = snd_es1938_get_mux,
+ .put = snd_es1938_put_mux,
+},
+ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
+ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
+ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "3D Control - Switch",
+ .info = snd_es1938_info_spatializer_enable,
+ .get = snd_es1938_get_spatializer_enable,
+ .put = snd_es1938_put_spatializer_enable,
+},
+ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0)
+};
+
+
+/* ---------------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------------- */
+
+/*
+ * initialize the chip - used by resume callback, too
+ */
+static void snd_es1938_chip_init(es1938_t *chip)
+{
+ /* reset chip */
+ snd_es1938_reset(chip);
+
+ /* configure native mode */
+
+ /* enable bus master */
+ pci_set_master(chip->pci);
+
+ /* disable legacy audio */
+ pci_write_config_word(chip->pci, SL_PCI_LEGACYCONTROL, 0x805f);
+
+ /* set DDMA base */
+ pci_write_config_word(chip->pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1);
+
+ /* set DMA/IRQ policy */
+ pci_write_config_dword(chip->pci, SL_PCI_CONFIG, 0);
+
+ /* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/
+ outb(0xf0, SLIO_REG(chip, IRQCONTROL));
+
+ /* reset DMA */
+ outb(0, SLDM_REG(chip, DMACLEAR));
+}
+
+#ifdef CONFIG_PM
+/*
+ * PM support
+ */
+
+static unsigned char saved_regs[SAVED_REG_SIZE+1] = {
+ 0x14, 0x1a, 0x1c, 0x3a, 0x3c, 0x3e, 0x36, 0x38,
+ 0x50, 0x52, 0x60, 0x61, 0x62, 0x63, 0x64, 0x68,
+ 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x7c, 0x7d,
+ 0xa8, 0xb4,
+};
+
+
+static int es1938_suspend(snd_card_t *card, pm_message_t state)
+{
+ es1938_t *chip = card->pm_private_data;
+ unsigned char *s, *d;
+
+ snd_pcm_suspend_all(chip->pcm);
+
+ /* save mixer-related registers */
+ for (s = saved_regs, d = chip->saved_regs; *s; s++, d++)
+ *d = snd_es1938_reg_read(chip, *s);
+
+ outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */
+
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int es1938_resume(snd_card_t *card)
+{
+ es1938_t *chip = card->pm_private_data;
+ unsigned char *s, *d;
+
+ pci_enable_device(chip->pci);
+ snd_es1938_chip_init(chip);
+
+ /* restore mixer-related registers */
+ for (s = saved_regs, d = chip->saved_regs; *s; s++, d++) {
+ if (*s < 0xa0)
+ snd_es1938_mixer_write(chip, *s, *d);
+ else
+ snd_es1938_write(chip, *s, *d);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef SUPPORT_JOYSTICK
+static int __devinit snd_es1938_create_gameport(es1938_t *chip)
+{
+ struct gameport *gp;
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "es1938: cannot allocate memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "ES1938");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gp->io = chip->game_port;
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static void snd_es1938_free_gameport(es1938_t *chip)
+{
+ if (chip->gameport) {
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+ }
+}
+#else
+static inline int snd_es1938_create_gameport(es1938_t *chip) { return -ENOSYS; }
+static inline void snd_es1938_free_gameport(es1938_t *chip) { }
+#endif /* SUPPORT_JOYSTICK */
+
+static int snd_es1938_free(es1938_t *chip)
+{
+ /* disable irqs */
+ outb(0x00, SLIO_REG(chip, IRQCONTROL));
+ if (chip->rmidi)
+ snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0);
+
+ snd_es1938_free_gameport(chip);
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_es1938_dev_free(snd_device_t *device)
+{
+ es1938_t *chip = device->device_data;
+ return snd_es1938_free(chip);
+}
+
+static int __devinit snd_es1938_create(snd_card_t * card,
+ struct pci_dev * pci,
+ es1938_t ** rchip)
+{
+ es1938_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_es1938_dev_free,
+ };
+
+ *rchip = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ /* check, if we can restrict PCI DMA transfers to 24 bits */
+ if (pci_set_dma_mask(pci, 0x00ffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) {
+ snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&chip->reg_lock);
+ spin_lock_init(&chip->mixer_lock);
+ chip->card = card;
+ chip->pci = pci;
+ if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->io_port = pci_resource_start(pci, 0);
+ chip->sb_port = pci_resource_start(pci, 1);
+ chip->vc_port = pci_resource_start(pci, 2);
+ chip->mpu_port = pci_resource_start(pci, 3);
+ chip->game_port = pci_resource_start(pci, 4);
+ if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_es1938_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+#ifdef ES1938_DDEBUG
+ snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
+ chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);
+#endif
+
+ chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */
+
+ snd_es1938_chip_init(chip);
+
+ snd_card_set_pm_callback(card, es1938_suspend, es1938_resume, chip);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_es1938_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = chip;
+ return 0;
+}
+
+/* --------------------------------------------------------------------
+ * Interrupt handler
+ * -------------------------------------------------------------------- */
+static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ es1938_t *chip = dev_id;
+ unsigned char status, audiostatus;
+ int handled = 0;
+
+ status = inb(SLIO_REG(chip, IRQCONTROL));
+#if 0
+ printk("Es1938debug - interrupt status: =0x%x\n", status);
+#endif
+
+ /* AUDIO 1 */
+ if (status & 0x10) {
+#if 0
+ printk("Es1938debug - AUDIO channel 1 interrupt\n");
+ printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT)));
+ printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR)));
+ printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS)));
+#endif
+ /* clear irq */
+ handled = 1;
+ audiostatus = inb(SLSB_REG(chip, STATUS));
+ if (chip->active & ADC1)
+ snd_pcm_period_elapsed(chip->capture_substream);
+ else if (chip->active & DAC1)
+ snd_pcm_period_elapsed(chip->playback2_substream);
+ }
+
+ /* AUDIO 2 */
+ if (status & 0x20) {
+#if 0
+ printk("Es1938debug - AUDIO channel 2 interrupt\n");
+ printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
+ printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR)));
+
+#endif
+ /* clear irq */
+ handled = 1;
+ snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0);
+ if (chip->active & DAC2)
+ snd_pcm_period_elapsed(chip->playback1_substream);
+ }
+
+ /* Hardware volume */
+ if (status & 0x40) {
+ int split = snd_es1938_mixer_read(chip, 0x64) & 0x80;
+ handled = 1;
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+ if (!split) {
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+ }
+ /* ack interrupt */
+ snd_es1938_mixer_write(chip, 0x66, 0x00);
+ }
+
+ /* MPU401 */
+ if (status & 0x80) {
+ // the following line is evil! It switches off MIDI interrupt handling after the first interrupt received.
+ // replacing the last 0 by 0x40 works for ESS-Solo1, but just doing nothing works as well!
+ // andreas@flying-snail.de
+ // snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); /* ack? */
+ if (chip->rmidi) {
+ handled = 1;
+ snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
+
+#define ES1938_DMA_SIZE 64
+
+static int __devinit snd_es1938_mixer(es1938_t *chip)
+{
+ snd_card_t *card;
+ unsigned int idx;
+ int err;
+
+ card = chip->card;
+
+ strcpy(card->mixername, "ESS Solo-1");
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) {
+ snd_kcontrol_t *kctl;
+ kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip);
+ switch (idx) {
+ case 0:
+ chip->master_volume = kctl;
+ kctl->private_free = snd_es1938_hwv_free;
+ break;
+ case 1:
+ chip->master_switch = kctl;
+ kctl->private_free = snd_es1938_hwv_free;
+ break;
+ case 2:
+ chip->hw_volume = kctl;
+ kctl->private_free = snd_es1938_hwv_free;
+ break;
+ case 3:
+ chip->hw_switch = kctl;
+ kctl->private_free = snd_es1938_hwv_free;
+ break;
+ }
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ return err;
+ }
+ return 0;
+}
+
+
+static int __devinit snd_es1938_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ es1938_t *chip;
+ opl3_t *opl3;
+ int idx, err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+ for (idx = 0; idx < 5; idx++) {
+ if (pci_resource_start(pci, idx) == 0 ||
+ !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
+ snd_card_free(card);
+ return -ENODEV;
+ }
+ }
+ if ((err = snd_es1938_create(card, pci, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "ES1938");
+ strcpy(card->shortname, "ESS ES1938 (Solo-1)");
+ sprintf(card->longname, "%s rev %i, irq %i",
+ card->shortname,
+ chip->revision,
+ chip->irq);
+
+ if ((err = snd_es1938_new_pcm(chip, 0)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_es1938_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (snd_opl3_create(card,
+ SLSB_REG(chip, FMLOWADDR),
+ SLSB_REG(chip, FMHIGHADDR),
+ OPL3_HW_OPL3, 1, &opl3) < 0) {
+ printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n",
+ SLSB_REG(chip, FMLOWADDR));
+ } else {
+ if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
+ printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
+ } else {
+ // this line is vital for MIDI interrupt handling on ess-solo1
+ // andreas@flying-snail.de
+ snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0x40);
+ }
+
+ snd_es1938_create_gameport(chip);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_es1938_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ESS ES1938 (Solo-1)",
+ .id_table = snd_es1938_ids,
+ .probe = snd_es1938_probe,
+ .remove = __devexit_p(snd_es1938_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_es1938_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_es1938_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_es1938_init)
+module_exit(alsa_card_es1938_exit)
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
new file mode 100644
index 00000000000..faf63ff19c4
--- /dev/null
+++ b/sound/pci/es1968.c
@@ -0,0 +1,2807 @@
+/*
+ * Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99)
+ * Copyright (c) by Matze Braun <MatzeBraun@gmx.de>.
+ * Takashi Iwai <tiwai@suse.de>
+ *
+ * Most of the driver code comes from Zach Brown(zab@redhat.com)
+ * Alan Cox OSS Driver
+ * Rewritted from card-es1938.c source.
+ *
+ * TODO:
+ * Perhaps Synth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Notes from Zach Brown about the driver code
+ *
+ * Hardware Description
+ *
+ * A working Maestro setup contains the Maestro chip wired to a
+ * codec or 2. In the Maestro we have the APUs, the ASSP, and the
+ * Wavecache. The APUs can be though of as virtual audio routing
+ * channels. They can take data from a number of sources and perform
+ * basic encodings of the data. The wavecache is a storehouse for
+ * PCM data. Typically it deals with PCI and interracts with the
+ * APUs. The ASSP is a wacky DSP like device that ESS is loth
+ * to release docs on. Thankfully it isn't required on the Maestro
+ * until you start doing insane things like FM emulation and surround
+ * encoding. The codecs are almost always AC-97 compliant codecs,
+ * but it appears that early Maestros may have had PT101 (an ESS
+ * part?) wired to them. The only real difference in the Maestro
+ * families is external goop like docking capability, memory for
+ * the ASSP, and initialization differences.
+ *
+ * Driver Operation
+ *
+ * We only drive the APU/Wavecache as typical DACs and drive the
+ * mixers in the codecs. There are 64 APUs. We assign 6 to each
+ * /dev/dsp? device. 2 channels for output, and 4 channels for
+ * input.
+ *
+ * Each APU can do a number of things, but we only really use
+ * 3 basic functions. For playback we use them to convert PCM
+ * data fetched over PCI by the wavecahche into analog data that
+ * is handed to the codec. One APU for mono, and a pair for stereo.
+ * When in stereo, the combination of smarts in the APU and Wavecache
+ * decide which wavecache gets the left or right channel.
+ *
+ * For record we still use the old overly mono system. For each in
+ * coming channel the data comes in from the codec, through a 'input'
+ * APU, through another rate converter APU, and then into memory via
+ * the wavecache and PCI. If its stereo, we mash it back into LRLR in
+ * software. The pass between the 2 APUs is supposedly what requires us
+ * to have a 512 byte buffer sitting around in wavecache/memory.
+ *
+ * The wavecache makes our life even more fun. First off, it can
+ * only address the first 28 bits of PCI address space, making it
+ * useless on quite a few architectures. Secondly, its insane.
+ * It claims to fetch from 4 regions of PCI space, each 4 meg in length.
+ * But that doesn't really work. You can only use 1 region. So all our
+ * allocations have to be in 4meg of each other. Booo. Hiss.
+ * So we have a module parameter, dsps_order, that is the order of
+ * the number of dsps to provide. All their buffer space is allocated
+ * on open time. The sonicvibes OSS routines we inherited really want
+ * power of 2 buffers, so we have all those next to each other, then
+ * 512 byte regions for the recording wavecaches. This ends up
+ * wasting quite a bit of memory. The only fixes I can see would be
+ * getting a kernel allocator that could work in zones, or figuring out
+ * just how to coerce the WP into doing what we want.
+ *
+ * The indirection of the various registers means we have to spinlock
+ * nearly all register accesses. We have the main register indirection
+ * like the wave cache, maestro registers, etc. Then we have beasts
+ * like the APU interface that is indirect registers gotten at through
+ * the main maestro indirection. Ouch. We spinlock around the actual
+ * ports on a per card basis. This means spinlock activity at each IO
+ * operation, but the only IO operation clusters are in non critical
+ * paths and it makes the code far easier to follow. Interrupts are
+ * blocked while holding the locks because the int handler has to
+ * get at some of them :(. The mixer interface doesn't, however.
+ * We also have an OSS state lock that is thrown around in a few
+ * places.
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#define CARD_NAME "ESS Maestro1/2"
+#define DRIVER_NAME "ES1968"
+
+MODULE_DESCRIPTION("ESS Maestro");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e},"
+ "{ESS,Maestro 2},"
+ "{ESS,Maestro 1},"
+ "{TerraTec,DMX}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 };
+static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 };
+static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 };
+static int clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+#ifdef SUPPORT_JOYSTICK
+static int joystick[SNDRV_CARDS];
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+module_param_array(total_bufsize, int, NULL, 0444);
+MODULE_PARM_DESC(total_bufsize, "Total buffer size in kB.");
+module_param_array(pcm_substreams_p, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard.");
+module_param_array(pcm_substreams_c, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard.");
+module_param_array(clock, int, NULL, 0444);
+MODULE_PARM_DESC(clock, "Clock on " CARD_NAME " soundcard. (0 = auto-detect)");
+module_param_array(use_pm, int, NULL, 0444);
+MODULE_PARM_DESC(use_pm, "Toggle power-management. (0 = off, 1 = on, 2 = auto)");
+module_param_array(enable_mpu, int, NULL, 0444);
+MODULE_PARM_DESC(enable_mpu, "Enable MPU401. (0 = off, 1 = on, 2 = auto)");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable joystick.");
+#endif
+
+
+/* PCI Dev ID's */
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS 0x125D
+#endif
+
+#define PCI_VENDOR_ID_ESS_OLD 0x1285 /* Platform Tech, the people the ESS
+ was bought form */
+
+#ifndef PCI_DEVICE_ID_ESS_M2E
+#define PCI_DEVICE_ID_ESS_M2E 0x1978
+#endif
+#ifndef PCI_DEVICE_ID_ESS_M2
+#define PCI_DEVICE_ID_ESS_M2 0x1968
+#endif
+#ifndef PCI_DEVICE_ID_ESS_M1
+#define PCI_DEVICE_ID_ESS_M1 0x0100
+#endif
+
+#define NR_APUS 64
+#define NR_APU_REGS 16
+
+/* NEC Versas ? */
+#define NEC_VERSA_SUBID1 0x80581033
+#define NEC_VERSA_SUBID2 0x803c1033
+
+/* Mode Flags */
+#define ESS_FMT_STEREO 0x01
+#define ESS_FMT_16BIT 0x02
+
+#define DAC_RUNNING 1
+#define ADC_RUNNING 2
+
+/* Values for the ESM_LEGACY_AUDIO_CONTROL */
+
+#define ESS_ENABLE_AUDIO 0x8000
+#define ESS_ENABLE_SERIAL_IRQ 0x4000
+#define IO_ADRESS_ALIAS 0x0020
+#define MPU401_IRQ_ENABLE 0x0010
+#define MPU401_IO_ENABLE 0x0008
+#define GAME_IO_ENABLE 0x0004
+#define FM_IO_ENABLE 0x0002
+#define SB_IO_ENABLE 0x0001
+
+/* Values for the ESM_CONFIG_A */
+
+#define PIC_SNOOP1 0x4000
+#define PIC_SNOOP2 0x2000
+#define SAFEGUARD 0x0800
+#define DMA_CLEAR 0x0700
+#define DMA_DDMA 0x0000
+#define DMA_TDMA 0x0100
+#define DMA_PCPCI 0x0200
+#define POST_WRITE 0x0080
+#define ISA_TIMING 0x0040
+#define SWAP_LR 0x0020
+#define SUBTR_DECODE 0x0002
+
+/* Values for the ESM_CONFIG_B */
+
+#define SPDIF_CONFB 0x0100
+#define HWV_CONFB 0x0080
+#define DEBOUNCE 0x0040
+#define GPIO_CONFB 0x0020
+#define CHI_CONFB 0x0010
+#define IDMA_CONFB 0x0008 /*undoc */
+#define MIDI_FIX 0x0004 /*undoc */
+#define IRQ_TO_ISA 0x0001 /*undoc */
+
+/* Values for Ring Bus Control B */
+#define RINGB_2CODEC_ID_MASK 0x0003
+#define RINGB_DIS_VALIDATION 0x0008
+#define RINGB_EN_SPDIF 0x0010
+#define RINGB_EN_2CODEC 0x0020
+#define RINGB_SING_BIT_DUAL 0x0040
+
+/* ****Port Adresses**** */
+
+/* Write & Read */
+#define ESM_INDEX 0x02
+#define ESM_DATA 0x00
+
+/* AC97 + RingBus */
+#define ESM_AC97_INDEX 0x30
+#define ESM_AC97_DATA 0x32
+#define ESM_RING_BUS_DEST 0x34
+#define ESM_RING_BUS_CONTR_A 0x36
+#define ESM_RING_BUS_CONTR_B 0x38
+#define ESM_RING_BUS_SDO 0x3A
+
+/* WaveCache*/
+#define WC_INDEX 0x10
+#define WC_DATA 0x12
+#define WC_CONTROL 0x14
+
+/* ASSP*/
+#define ASSP_INDEX 0x80
+#define ASSP_MEMORY 0x82
+#define ASSP_DATA 0x84
+#define ASSP_CONTROL_A 0xA2
+#define ASSP_CONTROL_B 0xA4
+#define ASSP_CONTROL_C 0xA6
+#define ASSP_HOSTW_INDEX 0xA8
+#define ASSP_HOSTW_DATA 0xAA
+#define ASSP_HOSTW_IRQ 0xAC
+/* Midi */
+#define ESM_MPU401_PORT 0x98
+/* Others */
+#define ESM_PORT_HOST_IRQ 0x18
+
+#define IDR0_DATA_PORT 0x00
+#define IDR1_CRAM_POINTER 0x01
+#define IDR2_CRAM_DATA 0x02
+#define IDR3_WAVE_DATA 0x03
+#define IDR4_WAVE_PTR_LOW 0x04
+#define IDR5_WAVE_PTR_HI 0x05
+#define IDR6_TIMER_CTRL 0x06
+#define IDR7_WAVE_ROMRAM 0x07
+
+#define WRITEABLE_MAP 0xEFFFFF
+#define READABLE_MAP 0x64003F
+
+/* PCI Register */
+
+#define ESM_LEGACY_AUDIO_CONTROL 0x40
+#define ESM_ACPI_COMMAND 0x54
+#define ESM_CONFIG_A 0x50
+#define ESM_CONFIG_B 0x52
+#define ESM_DDMA 0x60
+
+/* Bob Bits */
+#define ESM_BOB_ENABLE 0x0001
+#define ESM_BOB_START 0x0001
+
+/* Host IRQ Control Bits */
+#define ESM_RESET_MAESTRO 0x8000
+#define ESM_RESET_DIRECTSOUND 0x4000
+#define ESM_HIRQ_ClkRun 0x0100
+#define ESM_HIRQ_HW_VOLUME 0x0040
+#define ESM_HIRQ_HARPO 0x0030 /* What's that? */
+#define ESM_HIRQ_ASSP 0x0010
+#define ESM_HIRQ_DSIE 0x0004
+#define ESM_HIRQ_MPU401 0x0002
+#define ESM_HIRQ_SB 0x0001
+
+/* Host IRQ Status Bits */
+#define ESM_MPU401_IRQ 0x02
+#define ESM_SB_IRQ 0x01
+#define ESM_SOUND_IRQ 0x04
+#define ESM_ASSP_IRQ 0x10
+#define ESM_HWVOL_IRQ 0x40
+
+#define ESS_SYSCLK 50000000
+#define ESM_BOB_FREQ 200
+#define ESM_BOB_FREQ_MAX 800
+
+#define ESM_FREQ_ESM1 (49152000L / 1024L) /* default rate 48000 */
+#define ESM_FREQ_ESM2 (50000000L / 1024L)
+
+/* APU Modes: reg 0x00, bit 4-7 */
+#define ESM_APU_MODE_SHIFT 4
+#define ESM_APU_MODE_MASK (0xf << 4)
+#define ESM_APU_OFF 0x00
+#define ESM_APU_16BITLINEAR 0x01 /* 16-Bit Linear Sample Player */
+#define ESM_APU_16BITSTEREO 0x02 /* 16-Bit Stereo Sample Player */
+#define ESM_APU_8BITLINEAR 0x03 /* 8-Bit Linear Sample Player */
+#define ESM_APU_8BITSTEREO 0x04 /* 8-Bit Stereo Sample Player */
+#define ESM_APU_8BITDIFF 0x05 /* 8-Bit Differential Sample Playrer */
+#define ESM_APU_DIGITALDELAY 0x06 /* Digital Delay Line */
+#define ESM_APU_DUALTAP 0x07 /* Dual Tap Reader */
+#define ESM_APU_CORRELATOR 0x08 /* Correlator */
+#define ESM_APU_INPUTMIXER 0x09 /* Input Mixer */
+#define ESM_APU_WAVETABLE 0x0A /* Wave Table Mode */
+#define ESM_APU_SRCONVERTOR 0x0B /* Sample Rate Convertor */
+#define ESM_APU_16BITPINGPONG 0x0C /* 16-Bit Ping-Pong Sample Player */
+#define ESM_APU_RESERVED1 0x0D /* Reserved 1 */
+#define ESM_APU_RESERVED2 0x0E /* Reserved 2 */
+#define ESM_APU_RESERVED3 0x0F /* Reserved 3 */
+
+/* reg 0x00 */
+#define ESM_APU_FILTER_Q_SHIFT 0
+#define ESM_APU_FILTER_Q_MASK (3 << 0)
+/* APU Filtey Q Control */
+#define ESM_APU_FILTER_LESSQ 0x00
+#define ESM_APU_FILTER_MOREQ 0x03
+
+#define ESM_APU_FILTER_TYPE_SHIFT 2
+#define ESM_APU_FILTER_TYPE_MASK (3 << 2)
+#define ESM_APU_ENV_TYPE_SHIFT 8
+#define ESM_APU_ENV_TYPE_MASK (3 << 8)
+#define ESM_APU_ENV_STATE_SHIFT 10
+#define ESM_APU_ENV_STATE_MASK (3 << 10)
+#define ESM_APU_END_CURVE (1 << 12)
+#define ESM_APU_INT_ON_LOOP (1 << 13)
+#define ESM_APU_DMA_ENABLE (1 << 14)
+
+/* reg 0x02 */
+#define ESM_APU_SUBMIX_GROUP_SHIRT 0
+#define ESM_APU_SUBMIX_GROUP_MASK (7 << 0)
+#define ESM_APU_SUBMIX_MODE (1 << 3)
+#define ESM_APU_6dB (1 << 4)
+#define ESM_APU_DUAL_EFFECT (1 << 5)
+#define ESM_APU_EFFECT_CHANNELS_SHIFT 6
+#define ESM_APU_EFFECT_CHANNELS_MASK (3 << 6)
+
+/* reg 0x03 */
+#define ESM_APU_STEP_SIZE_MASK 0x0fff
+
+/* reg 0x04 */
+#define ESM_APU_PHASE_SHIFT 0
+#define ESM_APU_PHASE_MASK (0xff << 0)
+#define ESM_APU_WAVE64K_PAGE_SHIFT 8 /* most 8bit of wave start offset */
+#define ESM_APU_WAVE64K_PAGE_MASK (0xff << 8)
+
+/* reg 0x05 - wave start offset */
+/* reg 0x06 - wave end offset */
+/* reg 0x07 - wave loop length */
+
+/* reg 0x08 */
+#define ESM_APU_EFFECT_GAIN_SHIFT 0
+#define ESM_APU_EFFECT_GAIN_MASK (0xff << 0)
+#define ESM_APU_TREMOLO_DEPTH_SHIFT 8
+#define ESM_APU_TREMOLO_DEPTH_MASK (0xf << 8)
+#define ESM_APU_TREMOLO_RATE_SHIFT 12
+#define ESM_APU_TREMOLO_RATE_MASK (0xf << 12)
+
+/* reg 0x09 */
+/* bit 0-7 amplitude dest? */
+#define ESM_APU_AMPLITUDE_NOW_SHIFT 8
+#define ESM_APU_AMPLITUDE_NOW_MASK (0xff << 8)
+
+/* reg 0x0a */
+#define ESM_APU_POLAR_PAN_SHIFT 0
+#define ESM_APU_POLAR_PAN_MASK (0x3f << 0)
+/* Polar Pan Control */
+#define ESM_APU_PAN_CENTER_CIRCLE 0x00
+#define ESM_APU_PAN_MIDDLE_RADIUS 0x01
+#define ESM_APU_PAN_OUTSIDE_RADIUS 0x02
+
+#define ESM_APU_FILTER_TUNING_SHIFT 8
+#define ESM_APU_FILTER_TUNING_MASK (0xff << 8)
+
+/* reg 0x0b */
+#define ESM_APU_DATA_SRC_A_SHIFT 0
+#define ESM_APU_DATA_SRC_A_MASK (0x7f << 0)
+#define ESM_APU_INV_POL_A (1 << 7)
+#define ESM_APU_DATA_SRC_B_SHIFT 8
+#define ESM_APU_DATA_SRC_B_MASK (0x7f << 8)
+#define ESM_APU_INV_POL_B (1 << 15)
+
+#define ESM_APU_VIBRATO_RATE_SHIFT 0
+#define ESM_APU_VIBRATO_RATE_MASK (0xf << 0)
+#define ESM_APU_VIBRATO_DEPTH_SHIFT 4
+#define ESM_APU_VIBRATO_DEPTH_MASK (0xf << 4)
+#define ESM_APU_VIBRATO_PHASE_SHIFT 8
+#define ESM_APU_VIBRATO_PHASE_MASK (0xff << 8)
+
+/* reg 0x0c */
+#define ESM_APU_RADIUS_SELECT (1 << 6)
+
+/* APU Filter Control */
+#define ESM_APU_FILTER_2POLE_LOPASS 0x00
+#define ESM_APU_FILTER_2POLE_BANDPASS 0x01
+#define ESM_APU_FILTER_2POLE_HIPASS 0x02
+#define ESM_APU_FILTER_1POLE_LOPASS 0x03
+#define ESM_APU_FILTER_1POLE_HIPASS 0x04
+#define ESM_APU_FILTER_OFF 0x05
+
+/* APU ATFP Type */
+#define ESM_APU_ATFP_AMPLITUDE 0x00
+#define ESM_APU_ATFP_TREMELO 0x01
+#define ESM_APU_ATFP_FILTER 0x02
+#define ESM_APU_ATFP_PAN 0x03
+
+/* APU ATFP Flags */
+#define ESM_APU_ATFP_FLG_OFF 0x00
+#define ESM_APU_ATFP_FLG_WAIT 0x01
+#define ESM_APU_ATFP_FLG_DONE 0x02
+#define ESM_APU_ATFP_FLG_INPROCESS 0x03
+
+
+/* capture mixing buffer size */
+#define ESM_MEM_ALIGN 0x1000
+#define ESM_MIXBUF_SIZE 0x400
+
+#define ESM_MODE_PLAY 0
+#define ESM_MODE_CAPTURE 1
+
+/* acpi states */
+enum {
+ ACPI_D0=0,
+ ACPI_D1,
+ ACPI_D2,
+ ACPI_D3
+};
+
+/* bits in the acpi masks */
+#define ACPI_12MHZ ( 1 << 15)
+#define ACPI_24MHZ ( 1 << 14)
+#define ACPI_978 ( 1 << 13)
+#define ACPI_SPDIF ( 1 << 12)
+#define ACPI_GLUE ( 1 << 11)
+#define ACPI__10 ( 1 << 10) /* reserved */
+#define ACPI_PCIINT ( 1 << 9)
+#define ACPI_HV ( 1 << 8) /* hardware volume */
+#define ACPI_GPIO ( 1 << 7)
+#define ACPI_ASSP ( 1 << 6)
+#define ACPI_SB ( 1 << 5) /* sb emul */
+#define ACPI_FM ( 1 << 4) /* fm emul */
+#define ACPI_RB ( 1 << 3) /* ringbus / aclink */
+#define ACPI_MIDI ( 1 << 2)
+#define ACPI_GP ( 1 << 1) /* game port */
+#define ACPI_WP ( 1 << 0) /* wave processor */
+
+#define ACPI_ALL (0xffff)
+#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \
+ ACPI_MIDI|ACPI_GP|ACPI_WP))
+#define ACPI_NONE (ACPI__10)
+
+/* these masks indicate which units we care about at
+ which states */
+static u16 acpi_state_mask[] = {
+ [ACPI_D0] = ACPI_ALL,
+ [ACPI_D1] = ACPI_SLEEP,
+ [ACPI_D2] = ACPI_SLEEP,
+ [ACPI_D3] = ACPI_NONE
+};
+
+
+typedef struct snd_es1968 es1968_t;
+typedef struct snd_esschan esschan_t;
+typedef struct snd_esm_memory esm_memory_t;
+
+/* APU use in the driver */
+enum snd_enum_apu_type {
+ ESM_APU_PCM_PLAY,
+ ESM_APU_PCM_CAPTURE,
+ ESM_APU_PCM_RATECONV,
+ ESM_APU_FREE
+};
+
+/* chip type */
+enum {
+ TYPE_MAESTRO, TYPE_MAESTRO2, TYPE_MAESTRO2E
+};
+
+/* DMA Hack! */
+struct snd_esm_memory {
+ struct snd_dma_buffer buf;
+ int empty; /* status */
+ struct list_head list;
+};
+
+/* Playback Channel */
+struct snd_esschan {
+ int running;
+
+ u8 apu[4];
+ u8 apu_mode[4];
+
+ /* playback/capture pcm buffer */
+ esm_memory_t *memory;
+ /* capture mixer buffer */
+ esm_memory_t *mixbuf;
+
+ unsigned int hwptr; /* current hw pointer in bytes */
+ unsigned int count; /* sample counter in bytes */
+ unsigned int dma_size; /* total buffer size in bytes */
+ unsigned int frag_size; /* period size in bytes */
+ unsigned int wav_shift;
+ u16 base[4]; /* offset for ptr */
+
+ /* stereo/16bit flag */
+ unsigned char fmt;
+ int mode; /* playback / capture */
+
+ int bob_freq; /* required timer frequency */
+
+ snd_pcm_substream_t *substream;
+
+ /* linked list */
+ struct list_head list;
+
+#ifdef CONFIG_PM
+ u16 wc_map[4];
+#endif
+};
+
+struct snd_es1968 {
+ /* Module Config */
+ int total_bufsize; /* in bytes */
+
+ int playback_streams, capture_streams;
+
+ unsigned int clock; /* clock */
+ /* for clock measurement */
+ unsigned int in_measurement: 1;
+ unsigned int measure_apu;
+ unsigned int measure_lastpos;
+ unsigned int measure_count;
+
+ /* buffer */
+ struct snd_dma_buffer dma;
+
+ /* Resources... */
+ int irq;
+ unsigned long io_port;
+ int type;
+ struct pci_dev *pci;
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ int do_pm; /* power-management enabled */
+
+ /* DMA memory block */
+ struct list_head buf_list;
+
+ /* ALSA Stuff */
+ ac97_t *ac97;
+ snd_kcontrol_t *master_switch; /* for h/w volume control */
+ snd_kcontrol_t *master_volume;
+
+ snd_rawmidi_t *rmidi;
+
+ spinlock_t reg_lock;
+ spinlock_t ac97_lock;
+ struct tasklet_struct hwvol_tq;
+ unsigned int in_suspend;
+
+ /* Maestro Stuff */
+ u16 maestro_map[32];
+ int bobclient; /* active timer instancs */
+ int bob_freq; /* timer frequency */
+ struct semaphore memory_mutex; /* memory lock */
+
+ /* APU states */
+ unsigned char apu[NR_APUS];
+
+ /* active substreams */
+ struct list_head substream_list;
+ spinlock_t substream_lock;
+
+#ifdef CONFIG_PM
+ u16 apu_map[NR_APUS][NR_APU_REGS];
+#endif
+
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+};
+
+static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_es1968_ids[] = {
+ /* Maestro 1 */
+ { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
+ /* Maestro 2 */
+ { 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2 },
+ /* Maestro 2E */
+ { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2E },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_es1968_ids);
+
+/* *********************
+ * Low Level Funcs! *
+ *********************/
+
+/* no spinlock */
+static void __maestro_write(es1968_t *chip, u16 reg, u16 data)
+{
+ outw(reg, chip->io_port + ESM_INDEX);
+ outw(data, chip->io_port + ESM_DATA);
+ chip->maestro_map[reg] = data;
+}
+
+inline static void maestro_write(es1968_t *chip, u16 reg, u16 data)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ __maestro_write(chip, reg, data);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/* no spinlock */
+static u16 __maestro_read(es1968_t *chip, u16 reg)
+{
+ if (READABLE_MAP & (1 << reg)) {
+ outw(reg, chip->io_port + ESM_INDEX);
+ chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA);
+ }
+ return chip->maestro_map[reg];
+}
+
+inline static u16 maestro_read(es1968_t *chip, u16 reg)
+{
+ unsigned long flags;
+ u16 result;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ result = __maestro_read(chip, reg);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return result;
+}
+
+#define big_mdelay(msec) do {\
+ set_current_state(TASK_UNINTERRUPTIBLE);\
+ schedule_timeout(((msec) * HZ + 999) / 1000);\
+} while (0)
+
+/* Wait for the codec bus to be free */
+static int snd_es1968_ac97_wait(es1968_t *chip)
+{
+ int timeout = 100000;
+
+ while (timeout-- > 0) {
+ if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1))
+ return 0;
+ cond_resched();
+ }
+ snd_printd("es1968: ac97 timeout\n");
+ return 1; /* timeout */
+}
+
+static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+ es1968_t *chip = ac97->private_data;
+ unsigned long flags;
+
+ snd_es1968_ac97_wait(chip);
+
+ /* Write the bus */
+ spin_lock_irqsave(&chip->ac97_lock, flags);
+ outw(val, chip->io_port + ESM_AC97_DATA);
+ /*msleep(1);*/
+ outb(reg, chip->io_port + ESM_AC97_INDEX);
+ /*msleep(1);*/
+ spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
+static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ u16 data = 0;
+ es1968_t *chip = ac97->private_data;
+ unsigned long flags;
+
+ snd_es1968_ac97_wait(chip);
+
+ spin_lock_irqsave(&chip->ac97_lock, flags);
+ outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
+ /*msleep(1);*/
+
+ if (! snd_es1968_ac97_wait(chip)) {
+ data = inw(chip->io_port + ESM_AC97_DATA);
+ /*msleep(1);*/
+ }
+ spin_unlock_irqrestore(&chip->ac97_lock, flags);
+
+ return data;
+}
+
+/* no spinlock */
+static void apu_index_set(es1968_t *chip, u16 index)
+{
+ int i;
+ __maestro_write(chip, IDR1_CRAM_POINTER, index);
+ for (i = 0; i < 1000; i++)
+ if (__maestro_read(chip, IDR1_CRAM_POINTER) == index)
+ return;
+ snd_printd("es1968: APU register select failed. (Timeout)\n");
+}
+
+/* no spinlock */
+static void apu_data_set(es1968_t *chip, u16 data)
+{
+ int i;
+ for (i = 0; i < 1000; i++) {
+ if (__maestro_read(chip, IDR0_DATA_PORT) == data)
+ return;
+ __maestro_write(chip, IDR0_DATA_PORT, data);
+ }
+ snd_printd("es1968: APU register set probably failed (Timeout)!\n");
+}
+
+/* no spinlock */
+static void __apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data)
+{
+ snd_assert(channel < NR_APUS, return);
+#ifdef CONFIG_PM
+ chip->apu_map[channel][reg] = data;
+#endif
+ reg |= (channel << 4);
+ apu_index_set(chip, reg);
+ apu_data_set(chip, data);
+}
+
+inline static void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ __apu_set_register(chip, channel, reg, data);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u16 __apu_get_register(es1968_t *chip, u16 channel, u8 reg)
+{
+ snd_assert(channel < NR_APUS, return 0);
+ reg |= (channel << 4);
+ apu_index_set(chip, reg);
+ return __maestro_read(chip, IDR0_DATA_PORT);
+}
+
+inline static u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg)
+{
+ unsigned long flags;
+ u16 v;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ v = __apu_get_register(chip, channel, reg);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return v;
+}
+
+#if 0 /* ASSP is not supported */
+
+static void assp_set_register(es1968_t *chip, u32 reg, u32 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ outl(reg, chip->io_port + ASSP_INDEX);
+ outl(value, chip->io_port + ASSP_DATA);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u32 assp_get_register(es1968_t *chip, u32 reg)
+{
+ unsigned long flags;
+ u32 value;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ outl(reg, chip->io_port + ASSP_INDEX);
+ value = inl(chip->io_port + ASSP_DATA);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ return value;
+}
+
+#endif
+
+static void wave_set_register(es1968_t *chip, u16 reg, u16 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ outw(reg, chip->io_port + WC_INDEX);
+ outw(value, chip->io_port + WC_DATA);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u16 wave_get_register(es1968_t *chip, u16 reg)
+{
+ unsigned long flags;
+ u16 value;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ outw(reg, chip->io_port + WC_INDEX);
+ value = inw(chip->io_port + WC_DATA);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ return value;
+}
+
+/* *******************
+ * Bob the Timer! *
+ *******************/
+
+static void snd_es1968_bob_stop(es1968_t *chip)
+{
+ u16 reg;
+
+ reg = __maestro_read(chip, 0x11);
+ reg &= ~ESM_BOB_ENABLE;
+ __maestro_write(chip, 0x11, reg);
+ reg = __maestro_read(chip, 0x17);
+ reg &= ~ESM_BOB_START;
+ __maestro_write(chip, 0x17, reg);
+}
+
+static void snd_es1968_bob_start(es1968_t *chip)
+{
+ int prescale;
+ int divide;
+
+ /* compute ideal interrupt frequency for buffer size & play rate */
+ /* first, find best prescaler value to match freq */
+ for (prescale = 5; prescale < 12; prescale++)
+ if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9)))
+ break;
+
+ /* next, back off prescaler whilst getting divider into optimum range */
+ divide = 1;
+ while ((prescale > 5) && (divide < 32)) {
+ prescale--;
+ divide <<= 1;
+ }
+ divide >>= 1;
+
+ /* now fine-tune the divider for best match */
+ for (; divide < 31; divide++)
+ if (chip->bob_freq >
+ ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break;
+
+ /* divide = 0 is illegal, but don't let prescale = 4! */
+ if (divide == 0) {
+ divide++;
+ if (prescale > 5)
+ prescale--;
+ } else if (divide > 1)
+ divide--;
+
+ __maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide); /* set reg */
+
+ /* Now set IDR 11/17 */
+ __maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1);
+ __maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1);
+}
+
+/* call with substream spinlock */
+static void snd_es1968_bob_inc(es1968_t *chip, int freq)
+{
+ chip->bobclient++;
+ if (chip->bobclient == 1) {
+ chip->bob_freq = freq;
+ snd_es1968_bob_start(chip);
+ } else if (chip->bob_freq < freq) {
+ snd_es1968_bob_stop(chip);
+ chip->bob_freq = freq;
+ snd_es1968_bob_start(chip);
+ }
+}
+
+/* call with substream spinlock */
+static void snd_es1968_bob_dec(es1968_t *chip)
+{
+ chip->bobclient--;
+ if (chip->bobclient <= 0)
+ snd_es1968_bob_stop(chip);
+ else if (chip->bob_freq > ESM_BOB_FREQ) {
+ /* check reduction of timer frequency */
+ struct list_head *p;
+ int max_freq = ESM_BOB_FREQ;
+ list_for_each(p, &chip->substream_list) {
+ esschan_t *es = list_entry(p, esschan_t, list);
+ if (max_freq < es->bob_freq)
+ max_freq = es->bob_freq;
+ }
+ if (max_freq != chip->bob_freq) {
+ snd_es1968_bob_stop(chip);
+ chip->bob_freq = max_freq;
+ snd_es1968_bob_start(chip);
+ }
+ }
+}
+
+static int
+snd_es1968_calc_bob_rate(es1968_t *chip, esschan_t *es,
+ snd_pcm_runtime_t *runtime)
+{
+ /* we acquire 4 interrupts per period for precise control.. */
+ int freq = runtime->rate * 4;
+ if (es->fmt & ESS_FMT_STEREO)
+ freq <<= 1;
+ if (es->fmt & ESS_FMT_16BIT)
+ freq <<= 1;
+ freq /= es->frag_size;
+ if (freq < ESM_BOB_FREQ)
+ freq = ESM_BOB_FREQ;
+ else if (freq > ESM_BOB_FREQ_MAX)
+ freq = ESM_BOB_FREQ_MAX;
+ return freq;
+}
+
+
+/*************
+ * PCM Part *
+ *************/
+
+static u32 snd_es1968_compute_rate(es1968_t *chip, u32 freq)
+{
+ u32 rate = (freq << 16) / chip->clock;
+#if 0 /* XXX: do we need this? */
+ if (rate > 0x10000)
+ rate = 0x10000;
+#endif
+ return rate;
+}
+
+/* get current pointer */
+inline static unsigned int
+snd_es1968_get_dma_ptr(es1968_t *chip, esschan_t *es)
+{
+ unsigned int offset;
+
+ offset = apu_get_register(chip, es->apu[0], 5);
+
+ offset -= es->base[0];
+
+ return (offset & 0xFFFE); /* hardware is in words */
+}
+
+static void snd_es1968_apu_set_freq(es1968_t *chip, int apu, int freq)
+{
+ apu_set_register(chip, apu, 2,
+ (apu_get_register(chip, apu, 2) & 0x00FF) |
+ ((freq & 0xff) << 8) | 0x10);
+ apu_set_register(chip, apu, 3, freq >> 8);
+}
+
+/* spin lock held */
+inline static void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode)
+{
+ /* set the APU mode */
+ __apu_set_register(esm, apu, 0,
+ (__apu_get_register(esm, apu, 0) & 0xff0f) |
+ (mode << 4));
+}
+
+static void snd_es1968_pcm_start(es1968_t *chip, esschan_t *es)
+{
+ spin_lock(&chip->reg_lock);
+ __apu_set_register(chip, es->apu[0], 5, es->base[0]);
+ snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]);
+ if (es->mode == ESM_MODE_CAPTURE) {
+ __apu_set_register(chip, es->apu[2], 5, es->base[2]);
+ snd_es1968_trigger_apu(chip, es->apu[2], es->apu_mode[2]);
+ }
+ if (es->fmt & ESS_FMT_STEREO) {
+ __apu_set_register(chip, es->apu[1], 5, es->base[1]);
+ snd_es1968_trigger_apu(chip, es->apu[1], es->apu_mode[1]);
+ if (es->mode == ESM_MODE_CAPTURE) {
+ __apu_set_register(chip, es->apu[3], 5, es->base[3]);
+ snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]);
+ }
+ }
+ spin_unlock(&chip->reg_lock);
+}
+
+static void snd_es1968_pcm_stop(es1968_t *chip, esschan_t *es)
+{
+ spin_lock(&chip->reg_lock);
+ snd_es1968_trigger_apu(chip, es->apu[0], 0);
+ snd_es1968_trigger_apu(chip, es->apu[1], 0);
+ if (es->mode == ESM_MODE_CAPTURE) {
+ snd_es1968_trigger_apu(chip, es->apu[2], 0);
+ snd_es1968_trigger_apu(chip, es->apu[3], 0);
+ }
+ spin_unlock(&chip->reg_lock);
+}
+
+/* set the wavecache control reg */
+static void snd_es1968_program_wavecache(es1968_t *chip, esschan_t *es,
+ int channel, u32 addr, int capture)
+{
+ u32 tmpval = (addr - 0x10) & 0xFFF8;
+
+ if (! capture) {
+ if (!(es->fmt & ESS_FMT_16BIT))
+ tmpval |= 4; /* 8bit */
+ if (es->fmt & ESS_FMT_STEREO)
+ tmpval |= 2; /* stereo */
+ }
+
+ /* set the wavecache control reg */
+ wave_set_register(chip, es->apu[channel] << 3, tmpval);
+
+#ifdef CONFIG_PM
+ es->wc_map[channel] = tmpval;
+#endif
+}
+
+
+static void snd_es1968_playback_setup(es1968_t *chip, esschan_t *es,
+ snd_pcm_runtime_t *runtime)
+{
+ u32 pa;
+ int high_apu = 0;
+ int channel, apu;
+ int i, size;
+ unsigned long flags;
+ u32 freq;
+
+ size = es->dma_size >> es->wav_shift;
+
+ if (es->fmt & ESS_FMT_STEREO)
+ high_apu++;
+
+ for (channel = 0; channel <= high_apu; channel++) {
+ apu = es->apu[channel];
+
+ snd_es1968_program_wavecache(chip, es, channel, es->memory->buf.addr, 0);
+
+ /* Offset to PCMBAR */
+ pa = es->memory->buf.addr;
+ pa -= chip->dma.addr;
+ pa >>= 1; /* words */
+
+ pa |= 0x00400000; /* System RAM (Bit 22) */
+
+ if (es->fmt & ESS_FMT_STEREO) {
+ /* Enable stereo */
+ if (channel)
+ pa |= 0x00800000; /* (Bit 23) */
+ if (es->fmt & ESS_FMT_16BIT)
+ pa >>= 1;
+ }
+
+ /* base offset of dma calcs when reading the pointer
+ on this left one */
+ es->base[channel] = pa & 0xFFFF;
+
+ for (i = 0; i < 16; i++)
+ apu_set_register(chip, apu, i, 0x0000);
+
+ /* Load the buffer into the wave engine */
+ apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8);
+ apu_set_register(chip, apu, 5, pa & 0xFFFF);
+ apu_set_register(chip, apu, 6, (pa + size) & 0xFFFF);
+ /* setting loop == sample len */
+ apu_set_register(chip, apu, 7, size);
+
+ /* clear effects/env.. */
+ apu_set_register(chip, apu, 8, 0x0000);
+ /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */
+ apu_set_register(chip, apu, 9, 0xD000);
+
+ /* clear routing stuff */
+ apu_set_register(chip, apu, 11, 0x0000);
+ /* dma on, no envelopes, filter to all 1s) */
+ apu_set_register(chip, apu, 0, 0x400F);
+
+ if (es->fmt & ESS_FMT_16BIT)
+ es->apu_mode[channel] = ESM_APU_16BITLINEAR;
+ else
+ es->apu_mode[channel] = ESM_APU_8BITLINEAR;
+
+ if (es->fmt & ESS_FMT_STEREO) {
+ /* set panning: left or right */
+ /* Check: different panning. On my Canyon 3D Chipset the
+ Channels are swapped. I don't know, about the output
+ to the SPDif Link. Perhaps you have to change this
+ and not the APU Regs 4-5. */
+ apu_set_register(chip, apu, 10,
+ 0x8F00 | (channel ? 0 : 0x10));
+ es->apu_mode[channel] += 1; /* stereo */
+ } else
+ apu_set_register(chip, apu, 10, 0x8F08);
+ }
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ /* clear WP interrupts */
+ outw(1, chip->io_port + 0x04);
+ /* enable WP ints */
+ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ freq = runtime->rate;
+ /* set frequency */
+ if (freq > 48000)
+ freq = 48000;
+ if (freq < 4000)
+ freq = 4000;
+
+ /* hmmm.. */
+ if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO))
+ freq >>= 1;
+
+ freq = snd_es1968_compute_rate(chip, freq);
+
+ /* Load the frequency, turn on 6dB */
+ snd_es1968_apu_set_freq(chip, es->apu[0], freq);
+ snd_es1968_apu_set_freq(chip, es->apu[1], freq);
+}
+
+
+static void init_capture_apu(es1968_t *chip, esschan_t *es, int channel,
+ unsigned int pa, unsigned int bsize,
+ int mode, int route)
+{
+ int i, apu = es->apu[channel];
+
+ es->apu_mode[channel] = mode;
+
+ /* set the wavecache control reg */
+ snd_es1968_program_wavecache(chip, es, channel, pa, 1);
+
+ /* Offset to PCMBAR */
+ pa -= chip->dma.addr;
+ pa >>= 1; /* words */
+
+ /* base offset of dma calcs when reading the pointer
+ on this left one */
+ es->base[channel] = pa & 0xFFFF;
+ pa |= 0x00400000; /* bit 22 -> System RAM */
+
+ /* Begin loading the APU */
+ for (i = 0; i < 16; i++)
+ apu_set_register(chip, apu, i, 0x0000);
+
+ /* need to enable subgroups.. and we should probably
+ have different groups for different /dev/dsps.. */
+ apu_set_register(chip, apu, 2, 0x8);
+
+ /* Load the buffer into the wave engine */
+ apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8);
+ apu_set_register(chip, apu, 5, pa & 0xFFFF);
+ apu_set_register(chip, apu, 6, (pa + bsize) & 0xFFFF);
+ apu_set_register(chip, apu, 7, bsize);
+ /* clear effects/env.. */
+ apu_set_register(chip, apu, 8, 0x00F0);
+ /* amplitude now? sure. why not. */
+ apu_set_register(chip, apu, 9, 0x0000);
+ /* set filter tune, radius, polar pan */
+ apu_set_register(chip, apu, 10, 0x8F08);
+ /* route input */
+ apu_set_register(chip, apu, 11, route);
+ /* dma on, no envelopes, filter to all 1s) */
+ apu_set_register(chip, apu, 0, 0x400F);
+}
+
+static void snd_es1968_capture_setup(es1968_t *chip, esschan_t *es,
+ snd_pcm_runtime_t *runtime)
+{
+ int size;
+ u32 freq;
+ unsigned long flags;
+
+ size = es->dma_size >> es->wav_shift;
+
+ /* APU assignments:
+ 0 = mono/left SRC
+ 1 = right SRC
+ 2 = mono/left Input Mixer
+ 3 = right Input Mixer
+ */
+ /* data seems to flow from the codec, through an apu into
+ the 'mixbuf' bit of page, then through the SRC apu
+ and out to the real 'buffer'. ok. sure. */
+
+ /* input mixer (left/mono) */
+ /* parallel in crap, see maestro reg 0xC [8-11] */
+ init_capture_apu(chip, es, 2,
+ es->mixbuf->buf.addr, ESM_MIXBUF_SIZE/4, /* in words */
+ ESM_APU_INPUTMIXER, 0x14);
+ /* SRC (left/mono); get input from inputing apu */
+ init_capture_apu(chip, es, 0, es->memory->buf.addr, size,
+ ESM_APU_SRCONVERTOR, es->apu[2]);
+ if (es->fmt & ESS_FMT_STEREO) {
+ /* input mixer (right) */
+ init_capture_apu(chip, es, 3,
+ es->mixbuf->buf.addr + ESM_MIXBUF_SIZE/2,
+ ESM_MIXBUF_SIZE/4, /* in words */
+ ESM_APU_INPUTMIXER, 0x15);
+ /* SRC (right) */
+ init_capture_apu(chip, es, 1,
+ es->memory->buf.addr + size*2, size,
+ ESM_APU_SRCONVERTOR, es->apu[3]);
+ }
+
+ freq = runtime->rate;
+ /* Sample Rate conversion APUs don't like 0x10000 for their rate */
+ if (freq > 47999)
+ freq = 47999;
+ if (freq < 4000)
+ freq = 4000;
+
+ freq = snd_es1968_compute_rate(chip, freq);
+
+ /* Load the frequency, turn on 6dB */
+ snd_es1968_apu_set_freq(chip, es->apu[0], freq);
+ snd_es1968_apu_set_freq(chip, es->apu[1], freq);
+
+ /* fix mixer rate at 48khz. and its _must_ be 0x10000. */
+ freq = 0x10000;
+ snd_es1968_apu_set_freq(chip, es->apu[2], freq);
+ snd_es1968_apu_set_freq(chip, es->apu[3], freq);
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ /* clear WP interrupts */
+ outw(1, chip->io_port + 0x04);
+ /* enable WP ints */
+ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*******************
+ * ALSA Interface *
+ *******************/
+
+static int snd_es1968_pcm_prepare(snd_pcm_substream_t *substream)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ esschan_t *es = runtime->private_data;
+
+ es->dma_size = snd_pcm_lib_buffer_bytes(substream);
+ es->frag_size = snd_pcm_lib_period_bytes(substream);
+
+ es->wav_shift = 1; /* maestro handles always 16bit */
+ es->fmt = 0;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ es->fmt |= ESS_FMT_16BIT;
+ if (runtime->channels > 1) {
+ es->fmt |= ESS_FMT_STEREO;
+ if (es->fmt & ESS_FMT_16BIT) /* 8bit is already word shifted */
+ es->wav_shift++;
+ }
+ es->bob_freq = snd_es1968_calc_bob_rate(chip, es, runtime);
+
+ switch (es->mode) {
+ case ESM_MODE_PLAY:
+ snd_es1968_playback_setup(chip, es, runtime);
+ break;
+ case ESM_MODE_CAPTURE:
+ snd_es1968_capture_setup(chip, es, runtime);
+ break;
+ }
+
+ return 0;
+}
+
+static int snd_es1968_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ esschan_t *es = substream->runtime->private_data;
+
+ spin_lock(&chip->substream_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (es->running)
+ break;
+ snd_es1968_bob_inc(chip, es->bob_freq);
+ es->count = 0;
+ es->hwptr = 0;
+ snd_es1968_pcm_start(chip, es);
+ es->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (! es->running)
+ break;
+ snd_es1968_pcm_stop(chip, es);
+ es->running = 0;
+ snd_es1968_bob_dec(chip);
+ break;
+ }
+ spin_unlock(&chip->substream_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ esschan_t *es = substream->runtime->private_data;
+ unsigned int ptr;
+
+ ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift;
+
+ return bytes_to_frames(substream->runtime, ptr % es->dma_size);
+}
+
+static snd_pcm_hardware_t snd_es1968_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ /*SNDRV_PCM_INFO_PAUSE |*/
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 256,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_es1968_capture = {
+ .info = (SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ /*SNDRV_PCM_INFO_PAUSE |*/
+ SNDRV_PCM_INFO_RESUME),
+ .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 256,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/* *************************
+ * DMA memory management *
+ *************************/
+
+/* Because the Maestro can only take addresses relative to the PCM base address
+ register :( */
+
+static int calc_available_memory_size(es1968_t *chip)
+{
+ struct list_head *p;
+ int max_size = 0;
+
+ down(&chip->memory_mutex);
+ list_for_each(p, &chip->buf_list) {
+ esm_memory_t *buf = list_entry(p, esm_memory_t, list);
+ if (buf->empty && buf->buf.bytes > max_size)
+ max_size = buf->buf.bytes;
+ }
+ up(&chip->memory_mutex);
+ if (max_size >= 128*1024)
+ max_size = 127*1024;
+ return max_size;
+}
+
+/* allocate a new memory chunk with the specified size */
+static esm_memory_t *snd_es1968_new_memory(es1968_t *chip, int size)
+{
+ esm_memory_t *buf;
+ struct list_head *p;
+
+ size = ((size + ESM_MEM_ALIGN - 1) / ESM_MEM_ALIGN) * ESM_MEM_ALIGN;
+ down(&chip->memory_mutex);
+ list_for_each(p, &chip->buf_list) {
+ buf = list_entry(p, esm_memory_t, list);
+ if (buf->empty && buf->buf.bytes >= size)
+ goto __found;
+ }
+ up(&chip->memory_mutex);
+ return NULL;
+
+__found:
+ if (buf->buf.bytes > size) {
+ esm_memory_t *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
+ if (chunk == NULL) {
+ up(&chip->memory_mutex);
+ return NULL;
+ }
+ chunk->buf = buf->buf;
+ chunk->buf.bytes -= size;
+ chunk->buf.area += size;
+ chunk->buf.addr += size;
+ chunk->empty = 1;
+ buf->buf.bytes = size;
+ list_add(&chunk->list, &buf->list);
+ }
+ buf->empty = 0;
+ up(&chip->memory_mutex);
+ return buf;
+}
+
+/* free a memory chunk */
+static void snd_es1968_free_memory(es1968_t *chip, esm_memory_t *buf)
+{
+ esm_memory_t *chunk;
+
+ down(&chip->memory_mutex);
+ buf->empty = 1;
+ if (buf->list.prev != &chip->buf_list) {
+ chunk = list_entry(buf->list.prev, esm_memory_t, list);
+ if (chunk->empty) {
+ chunk->buf.bytes += buf->buf.bytes;
+ list_del(&buf->list);
+ kfree(buf);
+ buf = chunk;
+ }
+ }
+ if (buf->list.next != &chip->buf_list) {
+ chunk = list_entry(buf->list.next, esm_memory_t, list);
+ if (chunk->empty) {
+ buf->buf.bytes += chunk->buf.bytes;
+ list_del(&chunk->list);
+ kfree(chunk);
+ }
+ }
+ up(&chip->memory_mutex);
+}
+
+static void snd_es1968_free_dmabuf(es1968_t *chip)
+{
+ struct list_head *p;
+
+ if (! chip->dma.area)
+ return;
+ snd_dma_reserve_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci));
+ while ((p = chip->buf_list.next) != &chip->buf_list) {
+ esm_memory_t *chunk = list_entry(p, esm_memory_t, list);
+ list_del(p);
+ kfree(chunk);
+ }
+}
+
+static int __devinit
+snd_es1968_init_dmabuf(es1968_t *chip)
+{
+ int err;
+ esm_memory_t *chunk;
+
+ chip->dma.dev.type = SNDRV_DMA_TYPE_DEV;
+ chip->dma.dev.dev = snd_dma_pci_data(chip->pci);
+ if (! snd_dma_get_reserved_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci))) {
+ err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ chip->total_bufsize, &chip->dma);
+ if (err < 0 || ! chip->dma.area) {
+ snd_printk("es1968: can't allocate dma pages for size %d\n",
+ chip->total_bufsize);
+ return -ENOMEM;
+ }
+ if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) {
+ snd_dma_free_pages(&chip->dma);
+ snd_printk("es1968: DMA buffer beyond 256MB.\n");
+ return -ENOMEM;
+ }
+ }
+
+ INIT_LIST_HEAD(&chip->buf_list);
+ /* allocate an empty chunk */
+ chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
+ if (chunk == NULL) {
+ snd_es1968_free_dmabuf(chip);
+ return -ENOMEM;
+ }
+ memset(chip->dma.area, 0, ESM_MEM_ALIGN);
+ chunk->buf = chip->dma;
+ chunk->buf.area += ESM_MEM_ALIGN;
+ chunk->buf.addr += ESM_MEM_ALIGN;
+ chunk->buf.bytes -= ESM_MEM_ALIGN;
+ chunk->empty = 1;
+ list_add(&chunk->list, &chip->buf_list);
+
+ return 0;
+}
+
+/* setup the dma_areas */
+/* buffer is extracted from the pre-allocated memory chunk */
+static int snd_es1968_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *hw_params)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ esschan_t *chan = runtime->private_data;
+ int size = params_buffer_bytes(hw_params);
+
+ if (chan->memory) {
+ if (chan->memory->buf.bytes >= size) {
+ runtime->dma_bytes = size;
+ return 0;
+ }
+ snd_es1968_free_memory(chip, chan->memory);
+ }
+ chan->memory = snd_es1968_new_memory(chip, size);
+ if (chan->memory == NULL) {
+ // snd_printd("cannot allocate dma buffer: size = %d\n", size);
+ return -ENOMEM;
+ }
+ snd_pcm_set_runtime_buffer(substream, &chan->memory->buf);
+ return 1; /* area was changed */
+}
+
+/* remove dma areas if allocated */
+static int snd_es1968_hw_free(snd_pcm_substream_t * substream)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ esschan_t *chan;
+
+ if (runtime->private_data == NULL)
+ return 0;
+ chan = runtime->private_data;
+ if (chan->memory) {
+ snd_es1968_free_memory(chip, chan->memory);
+ chan->memory = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * allocate APU pair
+ */
+static int snd_es1968_alloc_apu_pair(es1968_t *chip, int type)
+{
+ int apu;
+
+ for (apu = 0; apu < NR_APUS; apu += 2) {
+ if (chip->apu[apu] == ESM_APU_FREE &&
+ chip->apu[apu + 1] == ESM_APU_FREE) {
+ chip->apu[apu] = chip->apu[apu + 1] = type;
+ return apu;
+ }
+ }
+ return -EBUSY;
+}
+
+/*
+ * release APU pair
+ */
+static void snd_es1968_free_apu_pair(es1968_t *chip, int apu)
+{
+ chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE;
+}
+
+
+/******************
+ * PCM open/close *
+ ******************/
+
+static int snd_es1968_playback_open(snd_pcm_substream_t *substream)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ esschan_t *es;
+ int apu1;
+
+ /* search 2 APUs */
+ apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY);
+ if (apu1 < 0)
+ return apu1;
+
+ es = kcalloc(1, sizeof(*es), GFP_KERNEL);
+ if (!es) {
+ snd_es1968_free_apu_pair(chip, apu1);
+ return -ENOMEM;
+ }
+
+ es->apu[0] = apu1;
+ es->apu[1] = apu1 + 1;
+ es->apu_mode[0] = 0;
+ es->apu_mode[1] = 0;
+ es->running = 0;
+ es->substream = substream;
+ es->mode = ESM_MODE_PLAY;
+
+ runtime->private_data = es;
+ runtime->hw = snd_es1968_playback;
+ runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
+ calc_available_memory_size(chip);
+#if 0
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ 1024);
+#endif
+ spin_lock_irq(&chip->substream_lock);
+ list_add(&es->list, &chip->substream_list);
+ spin_unlock_irq(&chip->substream_lock);
+
+ return 0;
+}
+
+static int snd_es1968_capture_open(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ esschan_t *es;
+ int apu1, apu2;
+
+ apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE);
+ if (apu1 < 0)
+ return apu1;
+ apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV);
+ if (apu2 < 0) {
+ snd_es1968_free_apu_pair(chip, apu1);
+ return apu2;
+ }
+
+ es = kcalloc(1, sizeof(*es), GFP_KERNEL);
+ if (!es) {
+ snd_es1968_free_apu_pair(chip, apu1);
+ snd_es1968_free_apu_pair(chip, apu2);
+ return -ENOMEM;
+ }
+
+ es->apu[0] = apu1;
+ es->apu[1] = apu1 + 1;
+ es->apu[2] = apu2;
+ es->apu[3] = apu2 + 1;
+ es->apu_mode[0] = 0;
+ es->apu_mode[1] = 0;
+ es->apu_mode[2] = 0;
+ es->apu_mode[3] = 0;
+ es->running = 0;
+ es->substream = substream;
+ es->mode = ESM_MODE_CAPTURE;
+
+ /* get mixbuffer */
+ if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) {
+ snd_es1968_free_apu_pair(chip, apu1);
+ snd_es1968_free_apu_pair(chip, apu2);
+ kfree(es);
+ return -ENOMEM;
+ }
+ memset(es->mixbuf->buf.area, 0, ESM_MIXBUF_SIZE);
+
+ runtime->private_data = es;
+ runtime->hw = snd_es1968_capture;
+ runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
+ calc_available_memory_size(chip) - 1024; /* keep MIXBUF size */
+#if 0
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ 1024);
+#endif
+ spin_lock_irq(&chip->substream_lock);
+ list_add(&es->list, &chip->substream_list);
+ spin_unlock_irq(&chip->substream_lock);
+
+ return 0;
+}
+
+static int snd_es1968_playback_close(snd_pcm_substream_t * substream)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ esschan_t *es;
+
+ if (substream->runtime->private_data == NULL)
+ return 0;
+ es = substream->runtime->private_data;
+ spin_lock_irq(&chip->substream_lock);
+ list_del(&es->list);
+ spin_unlock_irq(&chip->substream_lock);
+ snd_es1968_free_apu_pair(chip, es->apu[0]);
+ kfree(es);
+
+ return 0;
+}
+
+static int snd_es1968_capture_close(snd_pcm_substream_t * substream)
+{
+ es1968_t *chip = snd_pcm_substream_chip(substream);
+ esschan_t *es;
+
+ if (substream->runtime->private_data == NULL)
+ return 0;
+ es = substream->runtime->private_data;
+ spin_lock_irq(&chip->substream_lock);
+ list_del(&es->list);
+ spin_unlock_irq(&chip->substream_lock);
+ snd_es1968_free_memory(chip, es->mixbuf);
+ snd_es1968_free_apu_pair(chip, es->apu[0]);
+ snd_es1968_free_apu_pair(chip, es->apu[2]);
+ kfree(es);
+
+ return 0;
+}
+
+static snd_pcm_ops_t snd_es1968_playback_ops = {
+ .open = snd_es1968_playback_open,
+ .close = snd_es1968_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_es1968_hw_params,
+ .hw_free = snd_es1968_hw_free,
+ .prepare = snd_es1968_pcm_prepare,
+ .trigger = snd_es1968_pcm_trigger,
+ .pointer = snd_es1968_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_es1968_capture_ops = {
+ .open = snd_es1968_capture_open,
+ .close = snd_es1968_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_es1968_hw_params,
+ .hw_free = snd_es1968_hw_free,
+ .prepare = snd_es1968_pcm_prepare,
+ .trigger = snd_es1968_pcm_trigger,
+ .pointer = snd_es1968_pcm_pointer,
+};
+
+
+/*
+ * measure clock
+ */
+#define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */
+
+static void __devinit es1968_measure_clock(es1968_t *chip)
+{
+ int i, apu;
+ unsigned int pa, offset, t;
+ esm_memory_t *memory;
+ struct timeval start_time, stop_time;
+
+ if (chip->clock == 0)
+ chip->clock = 48000; /* default clock value */
+
+ /* search 2 APUs (although one apu is enough) */
+ if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) {
+ snd_printk("Hmm, cannot find empty APU pair!?\n");
+ return;
+ }
+ if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) {
+ snd_printk("cannot allocate dma buffer - using default clock %d\n", chip->clock);
+ snd_es1968_free_apu_pair(chip, apu);
+ return;
+ }
+
+ memset(memory->buf.area, 0, CLOCK_MEASURE_BUFSIZE);
+
+ wave_set_register(chip, apu << 3, (memory->buf.addr - 0x10) & 0xfff8);
+
+ pa = (unsigned int)((memory->buf.addr - chip->dma.addr) >> 1);
+ pa |= 0x00400000; /* System RAM (Bit 22) */
+
+ /* initialize apu */
+ for (i = 0; i < 16; i++)
+ apu_set_register(chip, apu, i, 0x0000);
+
+ apu_set_register(chip, apu, 0, 0x400f);
+ apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8);
+ apu_set_register(chip, apu, 5, pa & 0xffff);
+ apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff);
+ apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2);
+ apu_set_register(chip, apu, 8, 0x0000);
+ apu_set_register(chip, apu, 9, 0xD000);
+ apu_set_register(chip, apu, 10, 0x8F08);
+ apu_set_register(chip, apu, 11, 0x0000);
+ spin_lock_irq(&chip->reg_lock);
+ outw(1, chip->io_port + 0x04); /* clear WP interrupts */
+ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */
+ spin_unlock_irq(&chip->reg_lock);
+
+ snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */
+
+ chip->in_measurement = 1;
+ chip->measure_apu = apu;
+ spin_lock_irq(&chip->reg_lock);
+ snd_es1968_bob_inc(chip, ESM_BOB_FREQ);
+ __apu_set_register(chip, apu, 5, pa & 0xffff);
+ snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR);
+ do_gettimeofday(&start_time);
+ spin_unlock_irq(&chip->reg_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 20); /* 50 msec */
+ spin_lock_irq(&chip->reg_lock);
+ offset = __apu_get_register(chip, apu, 5);
+ do_gettimeofday(&stop_time);
+ snd_es1968_trigger_apu(chip, apu, 0); /* stop */
+ snd_es1968_bob_dec(chip);
+ chip->in_measurement = 0;
+ spin_unlock_irq(&chip->reg_lock);
+
+ /* check the current position */
+ offset -= (pa & 0xffff);
+ offset &= 0xfffe;
+ offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2);
+
+ t = stop_time.tv_sec - start_time.tv_sec;
+ t *= 1000000;
+ if (stop_time.tv_usec < start_time.tv_usec)
+ t -= start_time.tv_usec - stop_time.tv_usec;
+ else
+ t += stop_time.tv_usec - start_time.tv_usec;
+ if (t == 0) {
+ snd_printk("?? calculation error..\n");
+ } else {
+ offset *= 1000;
+ offset = (offset / t) * 1000 + ((offset % t) * 1000) / t;
+ if (offset < 47500 || offset > 48500) {
+ if (offset >= 40000 && offset <= 50000)
+ chip->clock = (chip->clock * offset) / 48000;
+ }
+ printk(KERN_INFO "es1968: clocking to %d\n", chip->clock);
+ }
+ snd_es1968_free_memory(chip, memory);
+ snd_es1968_free_apu_pair(chip, apu);
+}
+
+
+/*
+ */
+
+static void snd_es1968_pcm_free(snd_pcm_t *pcm)
+{
+ es1968_t *esm = pcm->private_data;
+ snd_es1968_free_dmabuf(esm);
+ esm->pcm = NULL;
+}
+
+static int __devinit
+snd_es1968_pcm(es1968_t *chip, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ /* get DMA buffer */
+ if ((err = snd_es1968_init_dmabuf(chip)) < 0)
+ return err;
+
+ /* set PCMBAR */
+ wave_set_register(chip, 0x01FC, chip->dma.addr >> 12);
+ wave_set_register(chip, 0x01FD, chip->dma.addr >> 12);
+ wave_set_register(chip, 0x01FE, chip->dma.addr >> 12);
+ wave_set_register(chip, 0x01FF, chip->dma.addr >> 12);
+
+ if ((err = snd_pcm_new(chip->card, "ESS Maestro", device,
+ chip->playback_streams,
+ chip->capture_streams, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_es1968_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops);
+
+ pcm->info_flags = 0;
+
+ strcpy(pcm->name, "ESS Maestro");
+
+ chip->pcm = pcm;
+
+ return 0;
+}
+
+/*
+ * update pointer
+ */
+static void snd_es1968_update_pcm(es1968_t *chip, esschan_t *es)
+{
+ unsigned int hwptr;
+ unsigned int diff;
+ snd_pcm_substream_t *subs = es->substream;
+
+ if (subs == NULL || !es->running)
+ return;
+
+ hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift;
+ hwptr %= es->dma_size;
+
+ diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size;
+
+ es->hwptr = hwptr;
+ es->count += diff;
+
+ if (es->count > es->frag_size) {
+ spin_unlock(&chip->substream_lock);
+ snd_pcm_period_elapsed(subs);
+ spin_lock(&chip->substream_lock);
+ es->count %= es->frag_size;
+ }
+}
+
+/*
+ */
+static void es1968_update_hw_volume(unsigned long private_data)
+{
+ es1968_t *chip = (es1968_t *) private_data;
+ int x, val;
+ unsigned long flags;
+
+ /* Figure out which volume control button was pushed,
+ based on differences from the default register
+ values. */
+ x = inb(chip->io_port + 0x1c);
+ /* Reset the volume control registers. */
+ outb(0x88, chip->io_port + 0x1c);
+ outb(0x88, chip->io_port + 0x1d);
+ outb(0x88, chip->io_port + 0x1e);
+ outb(0x88, chip->io_port + 0x1f);
+
+ if (chip->in_suspend)
+ return;
+
+ if (! chip->master_switch || ! chip->master_volume)
+ return;
+
+ /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
+ spin_lock_irqsave(&chip->ac97_lock, flags);
+ val = chip->ac97->regs[AC97_MASTER];
+ if (x & 1) {
+ /* mute */
+ val ^= 0x8000;
+ chip->ac97->regs[AC97_MASTER] = val;
+ outw(val, chip->io_port + ESM_AC97_DATA);
+ outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_switch->id);
+ } else {
+ val &= 0x7fff;
+ if (((x>>1) & 7) > 4) {
+ /* volume up */
+ if ((val & 0xff) > 0)
+ val--;
+ if ((val & 0xff00) > 0)
+ val -= 0x0100;
+ } else {
+ /* volume down */
+ if ((val & 0xff) < 0x1f)
+ val++;
+ if ((val & 0xff00) < 0x1f00)
+ val += 0x0100;
+ }
+ chip->ac97->regs[AC97_MASTER] = val;
+ outw(val, chip->io_port + ESM_AC97_DATA);
+ outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
+ }
+ spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ es1968_t *chip = dev_id;
+ u32 event;
+
+ if (!(event = inb(chip->io_port + 0x1A)))
+ return IRQ_NONE;
+
+ outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
+
+ if (event & ESM_HWVOL_IRQ)
+ tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */
+
+ /* else ack 'em all, i imagine */
+ outb(0xFF, chip->io_port + 0x1A);
+
+ if ((event & ESM_MPU401_IRQ) && chip->rmidi) {
+ snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+ }
+
+ if (event & ESM_SOUND_IRQ) {
+ struct list_head *p;
+ spin_lock(&chip->substream_lock);
+ list_for_each(p, &chip->substream_list) {
+ esschan_t *es = list_entry(p, esschan_t, list);
+ if (es->running)
+ snd_es1968_update_pcm(chip, es);
+ }
+ spin_unlock(&chip->substream_lock);
+ if (chip->in_measurement) {
+ unsigned int curp = __apu_get_register(chip, chip->measure_apu, 5);
+ if (curp < chip->measure_lastpos)
+ chip->measure_count++;
+ chip->measure_lastpos = curp;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Mixer stuff
+ */
+
+static int __devinit
+snd_es1968_mixer(es1968_t *chip)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ snd_ctl_elem_id_t id;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_es1968_ac97_write,
+ .read = snd_es1968_ac97_read,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+ pbus->no_vra = 1; /* ES1968 doesn't need VRA */
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+ return err;
+
+ /* attach master switch / volumes for h/w volume control */
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, "Master Playback Switch");
+ chip->master_switch = snd_ctl_find_id(chip->card, &id);
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, "Master Playback Volume");
+ chip->master_volume = snd_ctl_find_id(chip->card, &id);
+
+ return 0;
+}
+
+/*
+ * reset ac97 codec
+ */
+
+static void snd_es1968_ac97_reset(es1968_t *chip)
+{
+ unsigned long ioaddr = chip->io_port;
+
+ unsigned short save_ringbus_a;
+ unsigned short save_68;
+ unsigned short w;
+ unsigned int vend;
+
+ /* save configuration */
+ save_ringbus_a = inw(ioaddr + 0x36);
+
+ //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */
+ /* set command/status address i/o to 1st codec */
+ outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+ outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+ /* disable ac link */
+ outw(0x0000, ioaddr + 0x36);
+ save_68 = inw(ioaddr + 0x68);
+ pci_read_config_word(chip->pci, 0x58, &w); /* something magical with gpio and bus arb. */
+ pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+ if (w & 1)
+ save_68 |= 0x10;
+ outw(0xfffe, ioaddr + 0x64); /* unmask gpio 0 */
+ outw(0x0001, ioaddr + 0x68); /* gpio write */
+ outw(0x0000, ioaddr + 0x60); /* write 0 to gpio 0 */
+ udelay(20);
+ outw(0x0001, ioaddr + 0x60); /* write 1 to gpio 1 */
+ big_mdelay(20);
+
+ outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */
+ outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38);
+ outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a);
+ outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c);
+
+ /* now the second codec */
+ /* disable ac link */
+ outw(0x0000, ioaddr + 0x36);
+ outw(0xfff7, ioaddr + 0x64); /* unmask gpio 3 */
+ save_68 = inw(ioaddr + 0x68);
+ outw(0x0009, ioaddr + 0x68); /* gpio write 0 & 3 ?? */
+ outw(0x0001, ioaddr + 0x60); /* write 1 to gpio */
+ udelay(20);
+ outw(0x0009, ioaddr + 0x60); /* write 9 to gpio */
+ big_mdelay(500);
+ //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
+ outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+ outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+#if 0 /* the loop here needs to be much better if we want it.. */
+ snd_printk("trying software reset\n");
+ /* try and do a software reset */
+ outb(0x80 | 0x7c, ioaddr + 0x30);
+ for (w = 0;; w++) {
+ if ((inw(ioaddr + 0x30) & 1) == 0) {
+ if (inb(ioaddr + 0x32) != 0)
+ break;
+
+ outb(0x80 | 0x7d, ioaddr + 0x30);
+ if (((inw(ioaddr + 0x30) & 1) == 0)
+ && (inb(ioaddr + 0x32) != 0))
+ break;
+ outb(0x80 | 0x7f, ioaddr + 0x30);
+ if (((inw(ioaddr + 0x30) & 1) == 0)
+ && (inb(ioaddr + 0x32) != 0))
+ break;
+ }
+
+ if (w > 10000) {
+ outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */
+ big_mdelay(500); /* oh my.. */
+ outb(inb(ioaddr + 0x37) & ~0x08,
+ ioaddr + 0x37);
+ udelay(1);
+ outw(0x80, ioaddr + 0x30);
+ for (w = 0; w < 10000; w++) {
+ if ((inw(ioaddr + 0x30) & 1) == 0)
+ break;
+ }
+ }
+ }
+#endif
+ if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) {
+ /* turn on external amp? */
+ outw(0xf9ff, ioaddr + 0x64);
+ outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68);
+ outw(0x0209, ioaddr + 0x60);
+ }
+
+ /* restore.. */
+ outw(save_ringbus_a, ioaddr + 0x36);
+
+ /* Turn on the 978 docking chip.
+ First frob the "master output enable" bit,
+ then set most of the playback volume control registers to max. */
+ outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0);
+ outb(0xff, ioaddr+0xc3);
+ outb(0xff, ioaddr+0xc4);
+ outb(0xff, ioaddr+0xc6);
+ outb(0xff, ioaddr+0xc8);
+ outb(0x3f, ioaddr+0xcf);
+ outb(0x3f, ioaddr+0xd0);
+}
+
+static void snd_es1968_reset(es1968_t *chip)
+{
+ /* Reset */
+ outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND,
+ chip->io_port + ESM_PORT_HOST_IRQ);
+ udelay(10);
+ outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ);
+ udelay(10);
+}
+
+/*
+ * power management
+ */
+static void snd_es1968_set_acpi(es1968_t *chip, int state)
+{
+ u16 active_mask = acpi_state_mask[state];
+
+ pci_set_power_state(chip->pci, state);
+ /* make sure the units we care about are on
+ XXX we might want to do this before state flipping? */
+ pci_write_config_word(chip->pci, 0x54, ~ active_mask);
+ pci_write_config_word(chip->pci, 0x56, ~ active_mask);
+}
+
+
+/*
+ * initialize maestro chip
+ */
+static void snd_es1968_chip_init(es1968_t *chip)
+{
+ struct pci_dev *pci = chip->pci;
+ int i;
+ unsigned long iobase = chip->io_port;
+ u16 w;
+ u32 n;
+
+ /* We used to muck around with pci config space that
+ * we had no business messing with. We don't know enough
+ * about the machine to know which DMA mode is appropriate,
+ * etc. We were guessing wrong on some machines and making
+ * them unhappy. We now trust in the BIOS to do things right,
+ * which almost certainly means a new host of problems will
+ * arise with broken BIOS implementations. screw 'em.
+ * We're already intolerant of machines that don't assign
+ * IRQs.
+ */
+
+ /* do config work at full power */
+ snd_es1968_set_acpi(chip, ACPI_D0);
+
+ /* Config Reg A */
+ pci_read_config_word(pci, ESM_CONFIG_A, &w);
+
+ /* Use TDMA for now. TDMA works on all boards, so while its
+ * not the most efficient its the simplest. */
+ w &= ~DMA_CLEAR; /* Clear DMA bits */
+ w |= DMA_TDMA; /* TDMA on */
+ w &= ~(PIC_SNOOP1 | PIC_SNOOP2); /* Clear Pic Snoop Mode Bits */
+ w &= ~SAFEGUARD; /* Safeguard off */
+ w |= POST_WRITE; /* Posted write */
+ w |= ISA_TIMING; /* ISA timing on */
+ /* XXX huh? claims to be reserved.. */
+ w &= ~SWAP_LR; /* swap left/right
+ seems to only have effect on SB
+ Emulation */
+ w &= ~SUBTR_DECODE; /* Subtractive decode off */
+
+ pci_write_config_word(pci, ESM_CONFIG_A, w);
+
+ /* Config Reg B */
+
+ pci_read_config_word(pci, ESM_CONFIG_B, &w);
+
+ w &= ~(1 << 15); /* Turn off internal clock multiplier */
+ /* XXX how do we know which to use? */
+ w &= ~(1 << 14); /* External clock */
+
+ w &= ~SPDIF_CONFB; /* disable S/PDIF output */
+ w |= HWV_CONFB; /* HWV on */
+ w |= DEBOUNCE; /* Debounce off: easier to push the HW buttons */
+ w &= ~GPIO_CONFB; /* GPIO 4:5 */
+ w |= CHI_CONFB; /* Disconnect from the CHI. Enabling this made a dell 7500 work. */
+ w &= ~IDMA_CONFB; /* IDMA off (undocumented) */
+ w &= ~MIDI_FIX; /* MIDI fix off (undoc) */
+ w &= ~(1 << 1); /* reserved, always write 0 */
+ w &= ~IRQ_TO_ISA; /* IRQ to ISA off (undoc) */
+
+ pci_write_config_word(pci, ESM_CONFIG_B, w);
+
+ /* DDMA off */
+
+ pci_read_config_word(pci, ESM_DDMA, &w);
+ w &= ~(1 << 0);
+ pci_write_config_word(pci, ESM_DDMA, w);
+
+ /*
+ * Legacy mode
+ */
+
+ pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w);
+
+ w &= ~ESS_ENABLE_AUDIO; /* Disable Legacy Audio */
+ w &= ~ESS_ENABLE_SERIAL_IRQ; /* Disable SIRQ */
+ w &= ~(0x1f); /* disable mpu irq/io, game port, fm, SB */
+
+ pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w);
+
+ /* Set up 978 docking control chip. */
+ pci_read_config_word(pci, 0x58, &w);
+ w|=1<<2; /* Enable 978. */
+ w|=1<<3; /* Turn on 978 hardware volume control. */
+ w&=~(1<<11); /* Turn on 978 mixer volume control. */
+ pci_write_config_word(pci, 0x58, w);
+
+ /* Sound Reset */
+
+ snd_es1968_reset(chip);
+
+ /*
+ * Ring Bus Setup
+ */
+
+ /* setup usual 0x34 stuff.. 0x36 may be chip specific */
+ outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */
+ udelay(20);
+ outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */
+ udelay(20);
+
+ /*
+ * Reset the CODEC
+ */
+
+ snd_es1968_ac97_reset(chip);
+
+ /* Ring Bus Control B */
+
+ n = inl(iobase + ESM_RING_BUS_CONTR_B);
+ n &= ~RINGB_EN_SPDIF; /* SPDIF off */
+ //w |= RINGB_EN_2CODEC; /* enable 2nd codec */
+ outl(n, iobase + ESM_RING_BUS_CONTR_B);
+
+ /* Set hardware volume control registers to midpoints.
+ We can tell which button was pushed based on how they change. */
+ outb(0x88, iobase+0x1c);
+ outb(0x88, iobase+0x1d);
+ outb(0x88, iobase+0x1e);
+ outb(0x88, iobase+0x1f);
+
+ /* it appears some maestros (dell 7500) only work if these are set,
+ regardless of wether we use the assp or not. */
+
+ outb(0, iobase + ASSP_CONTROL_B);
+ outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */
+ outb(0, iobase + ASSP_CONTROL_C); /* M: Disable ASSP, ASSP IRQ's and FM Port */
+
+ /*
+ * set up wavecache
+ */
+ for (i = 0; i < 16; i++) {
+ /* Write 0 into the buffer area 0x1E0->1EF */
+ outw(0x01E0 + i, iobase + WC_INDEX);
+ outw(0x0000, iobase + WC_DATA);
+
+ /* The 1.10 test program seem to write 0 into the buffer area
+ * 0x1D0-0x1DF too.*/
+ outw(0x01D0 + i, iobase + WC_INDEX);
+ outw(0x0000, iobase + WC_DATA);
+ }
+ wave_set_register(chip, IDR7_WAVE_ROMRAM,
+ (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00));
+ wave_set_register(chip, IDR7_WAVE_ROMRAM,
+ wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100);
+ wave_set_register(chip, IDR7_WAVE_ROMRAM,
+ wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200);
+ wave_set_register(chip, IDR7_WAVE_ROMRAM,
+ wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400);
+
+
+ maestro_write(chip, IDR2_CRAM_DATA, 0x0000);
+ /* Now back to the DirectSound stuff */
+ /* audio serial configuration.. ? */
+ maestro_write(chip, 0x08, 0xB004);
+ maestro_write(chip, 0x09, 0x001B);
+ maestro_write(chip, 0x0A, 0x8000);
+ maestro_write(chip, 0x0B, 0x3F37);
+ maestro_write(chip, 0x0C, 0x0098);
+
+ /* parallel in, has something to do with recording :) */
+ maestro_write(chip, 0x0C,
+ (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000);
+ /* parallel out */
+ maestro_write(chip, 0x0C,
+ (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500);
+
+ maestro_write(chip, 0x0D, 0x7632);
+
+ /* Wave cache control on - test off, sg off,
+ enable, enable extra chans 1Mb */
+
+ w = inw(iobase + WC_CONTROL);
+
+ w &= ~0xFA00; /* Seems to be reserved? I don't know */
+ w |= 0xA000; /* reserved... I don't know */
+ w &= ~0x0200; /* Channels 56,57,58,59 as Extra Play,Rec Channel enable
+ Seems to crash the Computer if enabled... */
+ w |= 0x0100; /* Wave Cache Operation Enabled */
+ w |= 0x0080; /* Channels 60/61 as Placback/Record enabled */
+ w &= ~0x0060; /* Clear Wavtable Size */
+ w |= 0x0020; /* Wavetable Size : 1MB */
+ /* Bit 4 is reserved */
+ w &= ~0x000C; /* DMA Stuff? I don't understand what the datasheet means */
+ /* Bit 1 is reserved */
+ w &= ~0x0001; /* Test Mode off */
+
+ outw(w, iobase + WC_CONTROL);
+
+ /* Now clear the APU control ram */
+ for (i = 0; i < NR_APUS; i++) {
+ for (w = 0; w < NR_APU_REGS; w++)
+ apu_set_register(chip, i, w, 0);
+
+ }
+}
+
+/* Enable IRQ's */
+static void snd_es1968_start_irq(es1968_t *chip)
+{
+ unsigned short w;
+ w = ESM_HIRQ_DSIE | ESM_HIRQ_HW_VOLUME;
+ if (chip->rmidi)
+ w |= ESM_HIRQ_MPU401;
+ outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
+}
+
+#ifdef CONFIG_PM
+/*
+ * PM support
+ */
+static int es1968_suspend(snd_card_t *card, pm_message_t state)
+{
+ es1968_t *chip = card->pm_private_data;
+
+ if (! chip->do_pm)
+ return 0;
+
+ chip->in_suspend = 1;
+ snd_pcm_suspend_all(chip->pcm);
+ snd_ac97_suspend(chip->ac97);
+ snd_es1968_bob_stop(chip);
+ snd_es1968_set_acpi(chip, ACPI_D3);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int es1968_resume(snd_card_t *card)
+{
+ es1968_t *chip = card->pm_private_data;
+ struct list_head *p;
+
+ if (! chip->do_pm)
+ return 0;
+
+ /* restore all our config */
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+ snd_es1968_chip_init(chip);
+
+ /* need to restore the base pointers.. */
+ if (chip->dma.addr) {
+ /* set PCMBAR */
+ wave_set_register(chip, 0x01FC, chip->dma.addr >> 12);
+ }
+
+ snd_es1968_start_irq(chip);
+
+ /* restore ac97 state */
+ snd_ac97_resume(chip->ac97);
+
+ list_for_each(p, &chip->substream_list) {
+ esschan_t *es = list_entry(p, esschan_t, list);
+ switch (es->mode) {
+ case ESM_MODE_PLAY:
+ snd_es1968_playback_setup(chip, es, es->substream->runtime);
+ break;
+ case ESM_MODE_CAPTURE:
+ snd_es1968_capture_setup(chip, es, es->substream->runtime);
+ break;
+ }
+ }
+
+ /* start timer again */
+ if (chip->bobclient)
+ snd_es1968_bob_start(chip);
+
+ chip->in_suspend = 0;
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef SUPPORT_JOYSTICK
+#define JOYSTICK_ADDR 0x200
+static int __devinit snd_es1968_create_gameport(es1968_t *chip, int dev)
+{
+ struct gameport *gp;
+ struct resource *r;
+ u16 val;
+
+ if (!joystick[dev])
+ return -ENODEV;
+
+ r = request_region(JOYSTICK_ADDR, 8, "ES1968 gameport");
+ if (!r)
+ return -EBUSY;
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "es1968: cannot allocate memory for gameport\n");
+ release_resource(r);
+ kfree_nocheck(r);
+ return -ENOMEM;
+ }
+
+ pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &val);
+ pci_write_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, val | 0x04);
+
+ gameport_set_name(gp, "ES1968 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gp->io = JOYSTICK_ADDR;
+ gameport_set_port_data(gp, r);
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static void snd_es1968_free_gameport(es1968_t *chip)
+{
+ if (chip->gameport) {
+ struct resource *r = gameport_get_port_data(chip->gameport);
+
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+
+ release_resource(r);
+ kfree_nocheck(r);
+ }
+}
+#else
+static inline int snd_es1968_create_gameport(es1968_t *chip, int dev) { return -ENOSYS; }
+static inline void snd_es1968_free_gameport(es1968_t *chip) { }
+#endif
+
+static int snd_es1968_free(es1968_t *chip)
+{
+ if (chip->io_port) {
+ synchronize_irq(chip->irq);
+ outw(1, chip->io_port + 0x04); /* clear WP interrupts */
+ outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
+ }
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ snd_es1968_free_gameport(chip);
+ snd_es1968_set_acpi(chip, ACPI_D3);
+ chip->master_switch = NULL;
+ chip->master_volume = NULL;
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_es1968_dev_free(snd_device_t *device)
+{
+ es1968_t *chip = device->device_data;
+ return snd_es1968_free(chip);
+}
+
+struct ess_device_list {
+ unsigned short type; /* chip type */
+ unsigned short vendor; /* subsystem vendor id */
+};
+
+static struct ess_device_list pm_whitelist[] __devinitdata = {
+ { TYPE_MAESTRO2E, 0x0e11 }, /* Compaq Armada */
+ { TYPE_MAESTRO2E, 0x1028 },
+ { TYPE_MAESTRO2E, 0x103c },
+ { TYPE_MAESTRO2E, 0x1179 },
+ { TYPE_MAESTRO2E, 0x14c0 }, /* HP omnibook 4150 */
+};
+
+static struct ess_device_list mpu_blacklist[] __devinitdata = {
+ { TYPE_MAESTRO2, 0x125d },
+};
+
+static int __devinit snd_es1968_create(snd_card_t * card,
+ struct pci_dev *pci,
+ int total_bufsize,
+ int play_streams,
+ int capt_streams,
+ int chip_type,
+ int do_pm,
+ es1968_t **chip_ret)
+{
+ static snd_device_ops_t ops = {
+ .dev_free = snd_es1968_dev_free,
+ };
+ es1968_t *chip;
+ int i, err;
+
+ *chip_ret = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ /* check, if we can restrict PCI DMA transfers to 28 bits */
+ if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
+ snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (! chip) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ /* Set Vars */
+ chip->type = chip_type;
+ spin_lock_init(&chip->reg_lock);
+ spin_lock_init(&chip->substream_lock);
+ INIT_LIST_HEAD(&chip->buf_list);
+ INIT_LIST_HEAD(&chip->substream_list);
+ spin_lock_init(&chip->ac97_lock);
+ init_MUTEX(&chip->memory_mutex);
+ tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ chip->total_bufsize = total_bufsize; /* in bytes */
+ chip->playback_streams = play_streams;
+ chip->capture_streams = capt_streams;
+
+ if ((err = pci_request_regions(pci, "ESS Maestro")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->io_port = pci_resource_start(pci, 0);
+ if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ "ESS Maestro", (void*)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_es1968_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ /* Clear Maestro_map */
+ for (i = 0; i < 32; i++)
+ chip->maestro_map[i] = 0;
+
+ /* Clear Apu Map */
+ for (i = 0; i < NR_APUS; i++)
+ chip->apu[i] = ESM_APU_FREE;
+
+ /* just to be sure */
+ pci_set_master(pci);
+
+ if (do_pm > 1) {
+ /* disable power-management if not on the whitelist */
+ unsigned short vend;
+ pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+ for (i = 0; i < (int)ARRAY_SIZE(pm_whitelist); i++) {
+ if (chip->type == pm_whitelist[i].type &&
+ vend == pm_whitelist[i].vendor) {
+ do_pm = 1;
+ break;
+ }
+ }
+ if (do_pm > 1) {
+ /* not matched; disabling pm */
+ printk(KERN_INFO "es1968: not attempting power management.\n");
+ do_pm = 0;
+ }
+ }
+ chip->do_pm = do_pm;
+
+ snd_es1968_chip_init(chip);
+
+ if (chip->do_pm)
+ snd_card_set_pm_callback(card, es1968_suspend, es1968_resume, chip);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_es1968_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *chip_ret = chip;
+
+ return 0;
+}
+
+
+/*
+ */
+static int __devinit snd_es1968_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ es1968_t *chip;
+ unsigned int i;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (!card)
+ return -ENOMEM;
+
+ if (total_bufsize[dev] < 128)
+ total_bufsize[dev] = 128;
+ if (total_bufsize[dev] > 4096)
+ total_bufsize[dev] = 4096;
+ if ((err = snd_es1968_create(card, pci,
+ total_bufsize[dev] * 1024, /* in bytes */
+ pcm_substreams_p[dev],
+ pcm_substreams_c[dev],
+ pci_id->driver_data,
+ use_pm[dev],
+ &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ switch (chip->type) {
+ case TYPE_MAESTRO2E:
+ strcpy(card->driver, "ES1978");
+ strcpy(card->shortname, "ESS ES1978 (Maestro 2E)");
+ break;
+ case TYPE_MAESTRO2:
+ strcpy(card->driver, "ES1968");
+ strcpy(card->shortname, "ESS ES1968 (Maestro 2)");
+ break;
+ case TYPE_MAESTRO:
+ strcpy(card->driver, "ESM1");
+ strcpy(card->shortname, "ESS Maestro 1");
+ break;
+ }
+
+ if ((err = snd_es1968_pcm(chip, 0)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_es1968_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (enable_mpu[dev] == 2) {
+ /* check the black list */
+ unsigned short vend;
+ pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+ for (i = 0; i < ARRAY_SIZE(mpu_blacklist); i++) {
+ if (chip->type == mpu_blacklist[i].type &&
+ vend == mpu_blacklist[i].vendor) {
+ enable_mpu[dev] = 0;
+ break;
+ }
+ }
+ }
+ if (enable_mpu[dev]) {
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ chip->io_port + ESM_MPU401_PORT, 1,
+ chip->irq, 0, &chip->rmidi)) < 0) {
+ printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
+ }
+ }
+
+ snd_es1968_create_gameport(chip, dev);
+
+ snd_es1968_start_irq(chip);
+
+ chip->clock = clock[dev];
+ if (! chip->clock)
+ es1968_measure_clock(chip);
+
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, chip->io_port, chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_es1968_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ES1968 (ESS Maestro)",
+ .id_table = snd_es1968_ids,
+ .probe = snd_es1968_probe,
+ .remove = __devexit_p(snd_es1968_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_es1968_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_es1968_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_es1968_init)
+module_exit(alsa_card_es1968_exit)
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
new file mode 100644
index 00000000000..08e7c5a296d
--- /dev/null
+++ b/sound/pci/fm801.c
@@ -0,0 +1,1480 @@
+/*
+ * The driver for the ForteMedia FM801 based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+#if (defined(CONFIG_SND_FM801_TEA575X) || defined(CONFIG_SND_FM801_TEA575X_MODULE)) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE))
+#include <sound/tea575x-tuner.h>
+#define TEA575X_RADIO 1
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ForteMedia FM801");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
+ "{Genius,SoundMaker Live 5.1}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+/*
+ * Enable TEA575x tuner
+ * 1 = MediaForte 256-PCS
+ * 2 = MediaForte 256-PCPR
+ * 3 = MediaForte 64-PCR
+ * High 16-bits are video (radio) device number + 1
+ */
+static int tea575x_tuner[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 };
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
+module_param_array(tea575x_tuner, int, NULL, 0444);
+MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner.");
+
+/*
+ * Direct registers
+ */
+
+#define FM801_REG(chip, reg) (chip->port + FM801_##reg)
+
+#define FM801_PCM_VOL 0x00 /* PCM Output Volume */
+#define FM801_FM_VOL 0x02 /* FM Output Volume */
+#define FM801_I2S_VOL 0x04 /* I2S Volume */
+#define FM801_REC_SRC 0x06 /* Record Source */
+#define FM801_PLY_CTRL 0x08 /* Playback Control */
+#define FM801_PLY_COUNT 0x0a /* Playback Count */
+#define FM801_PLY_BUF1 0x0c /* Playback Bufer I */
+#define FM801_PLY_BUF2 0x10 /* Playback Buffer II */
+#define FM801_CAP_CTRL 0x14 /* Capture Control */
+#define FM801_CAP_COUNT 0x16 /* Capture Count */
+#define FM801_CAP_BUF1 0x18 /* Capture Buffer I */
+#define FM801_CAP_BUF2 0x1c /* Capture Buffer II */
+#define FM801_CODEC_CTRL 0x22 /* Codec Control */
+#define FM801_I2S_MODE 0x24 /* I2S Mode Control */
+#define FM801_VOLUME 0x26 /* Volume Up/Down/Mute Status */
+#define FM801_I2C_CTRL 0x29 /* I2C Control */
+#define FM801_AC97_CMD 0x2a /* AC'97 Command */
+#define FM801_AC97_DATA 0x2c /* AC'97 Data */
+#define FM801_MPU401_DATA 0x30 /* MPU401 Data */
+#define FM801_MPU401_CMD 0x31 /* MPU401 Command */
+#define FM801_GPIO_CTRL 0x52 /* General Purpose I/O Control */
+#define FM801_GEN_CTRL 0x54 /* General Control */
+#define FM801_IRQ_MASK 0x56 /* Interrupt Mask */
+#define FM801_IRQ_STATUS 0x5a /* Interrupt Status */
+#define FM801_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */
+#define FM801_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */
+#define FM801_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */
+#define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */
+#define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */
+
+#define FM801_AC97_ADDR_SHIFT 10
+
+/* playback and record control register bits */
+#define FM801_BUF1_LAST (1<<1)
+#define FM801_BUF2_LAST (1<<2)
+#define FM801_START (1<<5)
+#define FM801_PAUSE (1<<6)
+#define FM801_IMMED_STOP (1<<7)
+#define FM801_RATE_SHIFT 8
+#define FM801_RATE_MASK (15 << FM801_RATE_SHIFT)
+#define FM801_CHANNELS_4 (1<<12) /* playback only */
+#define FM801_CHANNELS_6 (2<<12) /* playback only */
+#define FM801_CHANNELS_6MS (3<<12) /* playback only */
+#define FM801_CHANNELS_MASK (3<<12)
+#define FM801_16BIT (1<<14)
+#define FM801_STEREO (1<<15)
+
+/* IRQ status bits */
+#define FM801_IRQ_PLAYBACK (1<<8)
+#define FM801_IRQ_CAPTURE (1<<9)
+#define FM801_IRQ_VOLUME (1<<14)
+#define FM801_IRQ_MPU (1<<15)
+
+/* GPIO control register */
+#define FM801_GPIO_GP0 (1<<0) /* read/write */
+#define FM801_GPIO_GP1 (1<<1)
+#define FM801_GPIO_GP2 (1<<2)
+#define FM801_GPIO_GP3 (1<<3)
+#define FM801_GPIO_GP(x) (1<<(0+(x)))
+#define FM801_GPIO_GD0 (1<<8) /* directions: 1 = input, 0 = output*/
+#define FM801_GPIO_GD1 (1<<9)
+#define FM801_GPIO_GD2 (1<<10)
+#define FM801_GPIO_GD3 (1<<11)
+#define FM801_GPIO_GD(x) (1<<(8+(x)))
+#define FM801_GPIO_GS0 (1<<12) /* function select: */
+#define FM801_GPIO_GS1 (1<<13) /* 1 = GPIO */
+#define FM801_GPIO_GS2 (1<<14) /* 0 = other (S/PDIF, VOL) */
+#define FM801_GPIO_GS3 (1<<15)
+#define FM801_GPIO_GS(x) (1<<(12+(x)))
+
+/*
+
+ */
+
+typedef struct _snd_fm801 fm801_t;
+
+struct _snd_fm801 {
+ int irq;
+
+ unsigned long port; /* I/O port number */
+ unsigned int multichannel: 1, /* multichannel support */
+ secondary: 1; /* secondary codec */
+ unsigned char secondary_addr; /* address of the secondary codec */
+
+ unsigned short ply_ctrl; /* playback control */
+ unsigned short cap_ctrl; /* capture control */
+
+ unsigned long ply_buffer;
+ unsigned int ply_buf;
+ unsigned int ply_count;
+ unsigned int ply_size;
+ unsigned int ply_pos;
+
+ unsigned long cap_buffer;
+ unsigned int cap_buf;
+ unsigned int cap_count;
+ unsigned int cap_size;
+ unsigned int cap_pos;
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97;
+ ac97_t *ac97_sec;
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ snd_rawmidi_t *rmidi;
+ snd_pcm_substream_t *playback_substream;
+ snd_pcm_substream_t *capture_substream;
+ unsigned int p_dma_size;
+ unsigned int c_dma_size;
+
+ spinlock_t reg_lock;
+ snd_info_entry_t *proc_entry;
+
+#ifdef TEA575X_RADIO
+ tea575x_t tea;
+#endif
+};
+
+static struct pci_device_id snd_fm801_ids[] = {
+ { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
+
+/*
+ * common I/O routines
+ */
+
+static int snd_fm801_update_bits(fm801_t *chip, unsigned short reg,
+ unsigned short mask, unsigned short value)
+{
+ int change;
+ unsigned long flags;
+ unsigned short old, new;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ old = inw(chip->port + reg);
+ new = (old & ~mask) | value;
+ change = old != new;
+ if (change)
+ outw(new, chip->port + reg);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return change;
+}
+
+static void snd_fm801_codec_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ fm801_t *chip = ac97->private_data;
+ int idx;
+
+ /*
+ * Wait until the codec interface is not ready..
+ */
+ for (idx = 0; idx < 100; idx++) {
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ goto ok1;
+ udelay(10);
+ }
+ snd_printk("AC'97 interface is busy (1)\n");
+ return;
+
+ ok1:
+ /* write data and address */
+ outw(val, FM801_REG(chip, AC97_DATA));
+ outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+ /*
+ * Wait until the write command is not completed..
+ */
+ for (idx = 0; idx < 1000; idx++) {
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ return;
+ udelay(10);
+ }
+ snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num);
+}
+
+static unsigned short snd_fm801_codec_read(ac97_t *ac97, unsigned short reg)
+{
+ fm801_t *chip = ac97->private_data;
+ int idx;
+
+ /*
+ * Wait until the codec interface is not ready..
+ */
+ for (idx = 0; idx < 100; idx++) {
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ goto ok1;
+ udelay(10);
+ }
+ snd_printk("AC'97 interface is busy (1)\n");
+ return 0;
+
+ ok1:
+ /* read command */
+ outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD));
+ for (idx = 0; idx < 100; idx++) {
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ goto ok2;
+ udelay(10);
+ }
+ snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num);
+ return 0;
+
+ ok2:
+ for (idx = 0; idx < 1000; idx++) {
+ if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8))
+ goto ok3;
+ udelay(10);
+ }
+ snd_printk("AC'97 interface #%d is not valid (2)\n", ac97->num);
+ return 0;
+
+ ok3:
+ return inw(FM801_REG(chip, AC97_DATA));
+}
+
+static unsigned int rates[] = {
+ 5500, 8000, 9600, 11025,
+ 16000, 19200, 22050, 32000,
+ 38400, 44100, 48000
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static unsigned int channels[] = {
+ 2, 4, 6
+};
+
+#define CHANNELS sizeof(channels) / sizeof(channels[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
+ .count = CHANNELS,
+ .list = channels,
+ .mask = 0,
+};
+
+/*
+ * Sample rate routines
+ */
+
+static unsigned short snd_fm801_rate_bits(unsigned int rate)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(rates); idx++)
+ if (rates[idx] == rate)
+ return idx;
+ snd_BUG();
+ return ARRAY_SIZE(rates) - 1;
+}
+
+/*
+ * PCM part
+ */
+
+static int snd_fm801_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ chip->ply_ctrl &= ~(FM801_BUF1_LAST |
+ FM801_BUF2_LAST |
+ FM801_PAUSE);
+ chip->ply_ctrl |= FM801_START |
+ FM801_IMMED_STOP;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ chip->ply_ctrl |= FM801_PAUSE;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ chip->ply_ctrl &= ~FM801_PAUSE;
+ break;
+ default:
+ spin_unlock(&chip->reg_lock);
+ snd_BUG();
+ return -EINVAL;
+ }
+ outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_fm801_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ chip->cap_ctrl &= ~(FM801_BUF1_LAST |
+ FM801_BUF2_LAST |
+ FM801_PAUSE);
+ chip->cap_ctrl |= FM801_START |
+ FM801_IMMED_STOP;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ chip->cap_ctrl |= FM801_PAUSE;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ chip->cap_ctrl &= ~FM801_PAUSE;
+ break;
+ default:
+ spin_unlock(&chip->reg_lock);
+ snd_BUG();
+ return -EINVAL;
+ }
+ outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_fm801_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_fm801_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_fm801_playback_prepare(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ chip->ply_size = snd_pcm_lib_buffer_bytes(substream);
+ chip->ply_count = snd_pcm_lib_period_bytes(substream);
+ spin_lock_irq(&chip->reg_lock);
+ chip->ply_ctrl &= ~(FM801_START | FM801_16BIT |
+ FM801_STEREO | FM801_RATE_MASK |
+ FM801_CHANNELS_MASK);
+ if (snd_pcm_format_width(runtime->format) == 16)
+ chip->ply_ctrl |= FM801_16BIT;
+ if (runtime->channels > 1) {
+ chip->ply_ctrl |= FM801_STEREO;
+ if (runtime->channels == 4)
+ chip->ply_ctrl |= FM801_CHANNELS_4;
+ else if (runtime->channels == 6)
+ chip->ply_ctrl |= FM801_CHANNELS_6;
+ }
+ chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
+ chip->ply_buf = 0;
+ outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+ outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
+ chip->ply_buffer = runtime->dma_addr;
+ chip->ply_pos = 0;
+ outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
+ outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_fm801_capture_prepare(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ chip->cap_size = snd_pcm_lib_buffer_bytes(substream);
+ chip->cap_count = snd_pcm_lib_period_bytes(substream);
+ spin_lock_irq(&chip->reg_lock);
+ chip->cap_ctrl &= ~(FM801_START | FM801_16BIT |
+ FM801_STEREO | FM801_RATE_MASK);
+ if (snd_pcm_format_width(runtime->format) == 16)
+ chip->cap_ctrl |= FM801_16BIT;
+ if (runtime->channels > 1)
+ chip->cap_ctrl |= FM801_STEREO;
+ chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
+ chip->cap_buf = 0;
+ outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+ outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
+ chip->cap_buffer = runtime->dma_addr;
+ chip->cap_pos = 0;
+ outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
+ outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_fm801_playback_pointer(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(chip->ply_ctrl & FM801_START))
+ return 0;
+ spin_lock(&chip->reg_lock);
+ ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
+ if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
+ ptr += chip->ply_count;
+ ptr %= chip->ply_size;
+ }
+ spin_unlock(&chip->reg_lock);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_fm801_capture_pointer(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(chip->cap_ctrl & FM801_START))
+ return 0;
+ spin_lock(&chip->reg_lock);
+ ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
+ if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
+ ptr += chip->cap_count;
+ ptr %= chip->cap_size;
+ }
+ spin_unlock(&chip->reg_lock);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ fm801_t *chip = dev_id;
+ unsigned short status;
+ unsigned int tmp;
+
+ status = inw(FM801_REG(chip, IRQ_STATUS));
+ status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
+ if (! status)
+ return IRQ_NONE;
+ /* ack first */
+ outw(status, FM801_REG(chip, IRQ_STATUS));
+ if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
+ spin_lock(&chip->reg_lock);
+ chip->ply_buf++;
+ chip->ply_pos += chip->ply_count;
+ chip->ply_pos %= chip->ply_size;
+ tmp = chip->ply_pos + chip->ply_count;
+ tmp %= chip->ply_size;
+ outl(chip->ply_buffer + tmp,
+ (chip->ply_buf & 1) ?
+ FM801_REG(chip, PLY_BUF1) :
+ FM801_REG(chip, PLY_BUF2));
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(chip->playback_substream);
+ }
+ if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) {
+ spin_lock(&chip->reg_lock);
+ chip->cap_buf++;
+ chip->cap_pos += chip->cap_count;
+ chip->cap_pos %= chip->cap_size;
+ tmp = chip->cap_pos + chip->cap_count;
+ tmp %= chip->cap_size;
+ outl(chip->cap_buffer + tmp,
+ (chip->cap_buf & 1) ?
+ FM801_REG(chip, CAP_BUF1) :
+ FM801_REG(chip, CAP_BUF2));
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(chip->capture_substream);
+ }
+ if (chip->rmidi && (status & FM801_IRQ_MPU))
+ snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+ if (status & FM801_IRQ_VOLUME)
+ ;/* TODO */
+
+ return IRQ_HANDLED;
+}
+
+static snd_pcm_hardware_t snd_fm801_playback =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5500,
+ .rate_max = 48000,
+ .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 snd_pcm_hardware_t snd_fm801_capture =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5500,
+ .rate_max = 48000,
+ .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 int snd_fm801_playback_open(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ chip->playback_substream = substream;
+ runtime->hw = snd_fm801_playback;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+ if (chip->multichannel) {
+ runtime->hw.channels_max = 6;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
+ }
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ return 0;
+}
+
+static int snd_fm801_capture_open(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ chip->capture_substream = substream;
+ runtime->hw = snd_fm801_capture;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ return 0;
+}
+
+static int snd_fm801_playback_close(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->playback_substream = NULL;
+ return 0;
+}
+
+static int snd_fm801_capture_close(snd_pcm_substream_t * substream)
+{
+ fm801_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->capture_substream = NULL;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_fm801_playback_ops = {
+ .open = snd_fm801_playback_open,
+ .close = snd_fm801_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_fm801_hw_params,
+ .hw_free = snd_fm801_hw_free,
+ .prepare = snd_fm801_playback_prepare,
+ .trigger = snd_fm801_playback_trigger,
+ .pointer = snd_fm801_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_fm801_capture_ops = {
+ .open = snd_fm801_capture_open,
+ .close = snd_fm801_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_fm801_hw_params,
+ .hw_free = snd_fm801_hw_free,
+ .prepare = snd_fm801_capture_prepare,
+ .trigger = snd_fm801_capture_trigger,
+ .pointer = snd_fm801_capture_pointer,
+};
+
+static void snd_fm801_pcm_free(snd_pcm_t *pcm)
+{
+ fm801_t *chip = pcm->private_data;
+ chip->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_fm801_pcm(fm801_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops);
+
+ pcm->private_data = chip;
+ pcm->private_free = snd_fm801_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "FM801");
+ chip->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ chip->multichannel ? 128*1024 : 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+/*
+ * TEA5757 radio
+ */
+
+#ifdef TEA575X_RADIO
+
+/* 256PCS GPIO numbers */
+#define TEA_256PCS_DATA 1
+#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
+#define TEA_256PCS_BUS_CLOCK 3
+
+static void snd_fm801_tea575x_256pcs_write(tea575x_t *tea, unsigned int val)
+{
+ fm801_t *chip = tea->private_data;
+ unsigned short reg;
+ int i = 25;
+
+ spin_lock_irq(&chip->reg_lock);
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ /* use GPIO lines and set write enable bit */
+ reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
+ FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
+ FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
+ FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
+ FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_256PCS_DATA) |
+ FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+
+ while (i--) {
+ if (val & (1 << i))
+ reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
+ else
+ reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ }
+
+ /* and reset the write enable bit */
+ reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
+ FM801_GPIO_GP(TEA_256PCS_DATA);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int snd_fm801_tea575x_256pcs_read(tea575x_t *tea)
+{
+ fm801_t *chip = tea->private_data;
+ unsigned short reg;
+ unsigned int val = 0;
+ int i;
+
+ spin_lock_irq(&chip->reg_lock);
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
+ FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
+ FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
+ FM801_GPIO_GD(TEA_256PCS_DATA) |
+ FM801_GPIO_GP(TEA_256PCS_DATA) |
+ FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
+ FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
+
+ for (i = 0; i < 24; i++) {
+ reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ val <<= 1;
+ if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
+ val |= 1;
+ }
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ return val;
+}
+
+/* 256PCPR GPIO numbers */
+#define TEA_256PCPR_BUS_CLOCK 0
+#define TEA_256PCPR_DATA 1
+#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */
+
+static void snd_fm801_tea575x_256pcpr_write(tea575x_t *tea, unsigned int val)
+{
+ fm801_t *chip = tea->private_data;
+ unsigned short reg;
+ int i = 25;
+
+ spin_lock_irq(&chip->reg_lock);
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ /* use GPIO lines and set write enable bit */
+ reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
+ FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
+ FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
+ FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
+ FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_256PCPR_DATA) |
+ FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+
+ while (i--) {
+ if (val & (1 << i))
+ reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
+ else
+ reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ }
+
+ /* and reset the write enable bit */
+ reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
+ FM801_GPIO_GP(TEA_256PCPR_DATA);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int snd_fm801_tea575x_256pcpr_read(tea575x_t *tea)
+{
+ fm801_t *chip = tea->private_data;
+ unsigned short reg;
+ unsigned int val = 0;
+ int i;
+
+ spin_lock_irq(&chip->reg_lock);
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
+ FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
+ FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
+ FM801_GPIO_GD(TEA_256PCPR_DATA) |
+ FM801_GPIO_GP(TEA_256PCPR_DATA) |
+ FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
+ FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
+
+ for (i = 0; i < 24; i++) {
+ reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ val <<= 1;
+ if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
+ val |= 1;
+ }
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ return val;
+}
+
+/* 64PCR GPIO numbers */
+#define TEA_64PCR_BUS_CLOCK 0
+#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */
+#define TEA_64PCR_DATA 2
+
+static void snd_fm801_tea575x_64pcr_write(tea575x_t *tea, unsigned int val)
+{
+ fm801_t *chip = tea->private_data;
+ unsigned short reg;
+ int i = 25;
+
+ spin_lock_irq(&chip->reg_lock);
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ /* use GPIO lines and set write enable bit */
+ reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
+ FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
+ FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
+ FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
+ FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_64PCR_DATA) |
+ FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+
+ while (i--) {
+ if (val & (1 << i))
+ reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
+ else
+ reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ }
+
+ /* and reset the write enable bit */
+ reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
+ FM801_GPIO_GP(TEA_64PCR_DATA);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int snd_fm801_tea575x_64pcr_read(tea575x_t *tea)
+{
+ fm801_t *chip = tea->private_data;
+ unsigned short reg;
+ unsigned int val = 0;
+ int i;
+
+ spin_lock_irq(&chip->reg_lock);
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
+ FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
+ FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
+ FM801_GPIO_GD(TEA_64PCR_DATA) |
+ FM801_GPIO_GP(TEA_64PCR_DATA) |
+ FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
+ FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
+ FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
+
+ for (i = 0; i < 24; i++) {
+ reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+ val <<= 1;
+ if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
+ val |= 1;
+ }
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ return val;
+}
+
+static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
+ {
+ /* 1 = MediaForte 256-PCS */
+ .write = snd_fm801_tea575x_256pcs_write,
+ .read = snd_fm801_tea575x_256pcs_read,
+ },
+ {
+ /* 2 = MediaForte 256-PCPR */
+ .write = snd_fm801_tea575x_256pcpr_write,
+ .read = snd_fm801_tea575x_256pcpr_read,
+ },
+ {
+ /* 3 = MediaForte 64-PCR */
+ .write = snd_fm801_tea575x_64pcr_write,
+ .read = snd_fm801_tea575x_64pcr_read,
+ }
+};
+#endif
+
+/*
+ * Mixer routines
+ */
+
+#define FM801_SINGLE(xname, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \
+ .get = snd_fm801_get_single, .put = snd_fm801_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_fm801_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ fm801_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+
+ ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int snd_fm801_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ fm801_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+ unsigned short val;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = mask - val;
+ return snd_fm801_update_bits(chip, reg, mask << shift, val << shift);
+}
+
+#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \
+ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
+ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+
+static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_fm801_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ fm801_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+ int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+
+ spin_lock_irq(&chip->reg_lock);
+ ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask;
+ ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask;
+ spin_unlock_irq(&chip->reg_lock);
+ if (invert) {
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ }
+ return 0;
+}
+
+static int snd_fm801_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ fm801_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+ int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+ unsigned short val1, val2;
+
+ val1 = ucontrol->value.integer.value[0] & mask;
+ val2 = ucontrol->value.integer.value[1] & mask;
+ if (invert) {
+ val1 = mask - val1;
+ val2 = mask - val2;
+ }
+ return snd_fm801_update_bits(chip, reg,
+ (mask << shift_left) | (mask << shift_right),
+ (val1 << shift_left ) | (val2 << shift_right));
+}
+
+static int snd_fm801_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[5] = {
+ "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 5;
+ if (uinfo->value.enumerated.item > 4)
+ uinfo->value.enumerated.item = 4;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_fm801_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ fm801_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ val = inw(FM801_REG(chip, REC_SRC)) & 7;
+ if (val > 4)
+ val = 4;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int snd_fm801_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ fm801_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ if ((val = ucontrol->value.enumerated.item[0]) > 4)
+ return -EINVAL;
+ return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
+}
+
+#define FM801_CONTROLS (sizeof(snd_fm801_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_fm801_controls[] __devinitdata = {
+FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1),
+FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
+FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1),
+FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
+FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1),
+FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Capture Source",
+ .info = snd_fm801_info_mux,
+ .get = snd_fm801_get_mux,
+ .put = snd_fm801_put_mux,
+}
+};
+
+#define FM801_CONTROLS_MULTI (sizeof(snd_fm801_controls_multi)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = {
+FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),
+FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),
+FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0),
+FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0),
+FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0),
+FM801_SINGLE("IEC958 Playback Switch", FM801_GEN_CTRL, 2, 1, 0),
+};
+
+static void snd_fm801_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ fm801_t *chip = bus->private_data;
+ chip->ac97_bus = NULL;
+}
+
+static void snd_fm801_mixer_free_ac97(ac97_t *ac97)
+{
+ fm801_t *chip = ac97->private_data;
+ if (ac97->num == 0) {
+ chip->ac97 = NULL;
+ } else {
+ chip->ac97_sec = NULL;
+ }
+}
+
+static int __devinit snd_fm801_mixer(fm801_t *chip)
+{
+ ac97_template_t ac97;
+ unsigned int i;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_fm801_codec_write,
+ .read = snd_fm801_codec_read,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ return err;
+ chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_fm801_mixer_free_ac97;
+ if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ return err;
+ if (chip->secondary) {
+ ac97.num = 1;
+ ac97.addr = chip->secondary_addr;
+ if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0)
+ return err;
+ }
+ for (i = 0; i < FM801_CONTROLS; i++)
+ snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip));
+ if (chip->multichannel) {
+ for (i = 0; i < FM801_CONTROLS_MULTI; i++)
+ snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+ }
+ return 0;
+}
+
+/*
+ * initialization routines
+ */
+
+static int snd_fm801_free(fm801_t *chip)
+{
+ unsigned short cmdw;
+
+ if (chip->irq < 0)
+ goto __end_hw;
+
+ /* interrupt setup - mask everything */
+ cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw |= 0x00c3;
+ outw(cmdw, FM801_REG(chip, IRQ_MASK));
+
+ __end_hw:
+#ifdef TEA575X_RADIO
+ snd_tea575x_exit(&chip->tea);
+#endif
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+
+ kfree(chip);
+ return 0;
+}
+
+static int snd_fm801_dev_free(snd_device_t *device)
+{
+ fm801_t *chip = device->device_data;
+ return snd_fm801_free(chip);
+}
+
+static int __devinit snd_fm801_create(snd_card_t * card,
+ struct pci_dev * pci,
+ int tea575x_tuner,
+ fm801_t ** rchip)
+{
+ fm801_t *chip;
+ unsigned char rev, id;
+ unsigned short cmdw;
+ unsigned long timeout;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_fm801_dev_free,
+ };
+
+ *rchip = NULL;
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&chip->reg_lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ if ((err = pci_request_regions(pci, "FM801")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->port = pci_resource_start(pci, 0);
+ if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", chip->irq);
+ snd_fm801_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
+
+ pci_read_config_byte(pci, PCI_REVISION_ID, &rev);
+ if (rev >= 0xb1) /* FM801-AU */
+ chip->multichannel = 1;
+
+ /* codec cold reset + AC'97 warm reset */
+ outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL));
+ inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
+ udelay(100);
+ outw(0, FM801_REG(chip, CODEC_CTRL));
+
+ timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */
+
+ outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+ udelay(5);
+ do {
+ if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
+ goto __ac97_secondary;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after(timeout, jiffies));
+ snd_printk("Primary AC'97 codec not found\n");
+ snd_fm801_free(chip);
+ return -EIO;
+
+ __ac97_secondary:
+ if (!chip->multichannel) /* lookup is not required */
+ goto __ac97_ok;
+ for (id = 3; id > 0; id--) { /* my card has the secondary codec */
+ /* at address #3, so the loop is inverted */
+
+ timeout = jiffies + HZ / 20;
+
+ outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, FM801_REG(chip, AC97_CMD));
+ udelay(5);
+ do {
+ if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) {
+ cmdw = inw(FM801_REG(chip, AC97_DATA));
+ if (cmdw != 0xffff && cmdw != 0) {
+ chip->secondary = 1;
+ chip->secondary_addr = id;
+ goto __ac97_ok;
+ }
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after(timeout, jiffies));
+ }
+
+ /* the recovery phase, it seems that probing for non-existing codec might */
+ /* cause timeout problems */
+ timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */
+
+ outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+ udelay(5);
+ do {
+ if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
+ goto __ac97_ok;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (time_after(timeout, jiffies));
+ snd_printk("Primary AC'97 codec not responding\n");
+ snd_fm801_free(chip);
+ return -EIO;
+
+ __ac97_ok:
+
+ /* init volume */
+ outw(0x0808, FM801_REG(chip, PCM_VOL));
+ outw(0x9f1f, FM801_REG(chip, FM_VOL));
+ outw(0x8808, FM801_REG(chip, I2S_VOL));
+
+ /* I2S control - I2S mode */
+ outw(0x0003, FM801_REG(chip, I2S_MODE));
+
+ /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */
+ cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw &= ~0x0083;
+ outw(cmdw, FM801_REG(chip, IRQ_MASK));
+
+ /* interrupt clear */
+ outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_fm801_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+#ifdef TEA575X_RADIO
+ if (tea575x_tuner > 0 && (tea575x_tuner & 0xffff) < 4) {
+ chip->tea.dev_nr = tea575x_tuner >> 16;
+ chip->tea.card = card;
+ chip->tea.freq_fixup = 10700;
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0xffff) - 1];
+ snd_tea575x_init(&chip->tea);
+ }
+#endif
+
+ *rchip = chip;
+ return 0;
+}
+
+static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ fm801_t *chip;
+ opl3_t *opl3;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+ if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "FM801");
+ strcpy(card->shortname, "ForteMedia FM801-");
+ strcat(card->shortname, chip->multichannel ? "AU" : "AS");
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, chip->port, chip->irq);
+
+ if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_fm801_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
+ FM801_REG(chip, MPU401_DATA), 1,
+ chip->irq, 0, &chip->rmidi)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
+ FM801_REG(chip, OPL3_BANK1),
+ OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_card_fm801_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "FM801",
+ .id_table = snd_fm801_ids,
+ .probe = snd_card_fm801_probe,
+ .remove = __devexit_p(snd_card_fm801_remove),
+};
+
+static int __init alsa_card_fm801_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_fm801_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_fm801_init)
+module_exit(alsa_card_fm801_exit)
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
new file mode 100644
index 00000000000..570a59d33b4
--- /dev/null
+++ b/sound/pci/hda/Makefile
@@ -0,0 +1,7 @@
+snd-hda-intel-objs := hda_intel.o
+snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o
+ifdef CONFIG_PROC_FS
+snd-hda-codec-objs += hda_proc.o
+endif
+
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o snd-hda-codec.o
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
new file mode 100644
index 00000000000..9ed117ac0c0
--- /dev/null
+++ b/sound/pci/hda/hda_codec.c
@@ -0,0 +1,1856 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include "hda_local.h"
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Universal interface for High Definition Audio Codec");
+MODULE_LICENSE("GPL");
+
+
+/*
+ * vendor / preset table
+ */
+
+struct hda_vendor_id {
+ unsigned int id;
+ const char *name;
+};
+
+/* codec vendor labels */
+static struct hda_vendor_id hda_vendor_ids[] = {
+ { 0x10ec, "Realtek" },
+ { 0x13f6, "C-Media" },
+ { 0x434d, "C-Media" },
+ {} /* terminator */
+};
+
+/* codec presets */
+#include "hda_patch.h"
+
+
+/**
+ * snd_hda_codec_read - send a command and get the response
+ * @codec: the HDA codec
+ * @nid: NID to send the command
+ * @direct: direct flag
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int parm)
+{
+ unsigned int res;
+ down(&codec->bus->cmd_mutex);
+ if (! codec->bus->ops.command(codec, nid, direct, verb, parm))
+ res = codec->bus->ops.get_response(codec);
+ else
+ res = (unsigned int)-1;
+ up(&codec->bus->cmd_mutex);
+ return res;
+}
+
+/**
+ * snd_hda_codec_write - send a single command without waiting for response
+ * @codec: the HDA codec
+ * @nid: NID to send the command
+ * @direct: direct flag
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int parm)
+{
+ int err;
+ down(&codec->bus->cmd_mutex);
+ err = codec->bus->ops.command(codec, nid, direct, verb, parm);
+ up(&codec->bus->cmd_mutex);
+ return err;
+}
+
+/**
+ * snd_hda_sequence_write - sequence writes
+ * @codec: the HDA codec
+ * @seq: VERB array to send
+ *
+ * Send the commands sequentially from the given array.
+ * The array must be terminated with NID=0.
+ */
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
+{
+ for (; seq->nid; seq++)
+ snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
+}
+
+/**
+ * snd_hda_get_sub_nodes - get the range of sub nodes
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @start_id: the pointer to store the start NID
+ *
+ * Parse the NID and store the start NID of its sub-nodes.
+ * Returns the number of sub-nodes.
+ */
+int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id)
+{
+ unsigned int parm;
+
+ parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT);
+ *start_id = (parm >> 16) & 0x7fff;
+ return (int)(parm & 0x7fff);
+}
+
+/**
+ * snd_hda_get_connections - get connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns)
+{
+ unsigned int parm;
+ int i, j, conn_len, num_tupples, conns;
+ unsigned int shift, num_elems, mask;
+
+ snd_assert(conn_list && max_conns > 0, return -EINVAL);
+
+ parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
+ if (parm & AC_CLIST_LONG) {
+ /* long form */
+ shift = 16;
+ num_elems = 2;
+ } else {
+ /* short form */
+ shift = 8;
+ num_elems = 4;
+ }
+ conn_len = parm & AC_CLIST_LENGTH;
+ num_tupples = num_elems / 2;
+ mask = (1 << (shift-1)) - 1;
+
+ if (! conn_len)
+ return 0; /* no connection */
+
+ if (conn_len == 1) {
+ /* single connection */
+ parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, 0);
+ conn_list[0] = parm & mask;
+ return 1;
+ }
+
+ /* multi connection */
+ conns = 0;
+ for (i = 0; i < conn_len; i += num_elems) {
+ parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, i);
+ for (j = 0; j < num_tupples; j++) {
+ int range_val;
+ hda_nid_t val1, val2, n;
+ range_val = parm & (1 << (shift-1)); /* ranges */
+ val1 = parm & mask;
+ parm >>= shift;
+ val2 = parm & mask;
+ parm >>= shift;
+ if (range_val) {
+ /* ranges between val1 and val2 */
+ if (val1 > val2) {
+ snd_printk(KERN_WARNING "hda_codec: invalid dep_range_val %x:%x\n", val1, val2);
+ continue;
+ }
+ for (n = val1; n <= val2; n++) {
+ if (conns >= max_conns)
+ return -EINVAL;
+ conn_list[conns++] = n;
+ }
+ } else {
+ if (! val1)
+ break;
+ if (conns >= max_conns)
+ return -EINVAL;
+ conn_list[conns++] = val1;
+ if (! val2)
+ break;
+ if (conns >= max_conns)
+ return -EINVAL;
+ conn_list[conns++] = val2;
+ }
+ }
+ }
+ return conns;
+}
+
+
+/**
+ * snd_hda_queue_unsol_event - add an unsolicited event to queue
+ * @bus: the BUS
+ * @res: unsolicited event (lower 32bit of RIRB entry)
+ * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
+ *
+ * Adds the given event to the queue. The events are processed in
+ * the workqueue asynchronously. Call this function in the interrupt
+ * hanlder when RIRB receives an unsolicited event.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
+{
+ struct hda_bus_unsolicited *unsol;
+ unsigned int wp;
+
+ if ((unsol = bus->unsol) == NULL)
+ return 0;
+
+ wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE;
+ unsol->wp = wp;
+
+ wp <<= 1;
+ unsol->queue[wp] = res;
+ unsol->queue[wp + 1] = res_ex;
+
+ queue_work(unsol->workq, &unsol->work);
+
+ return 0;
+}
+
+/*
+ * process queueud unsolicited events
+ */
+static void process_unsol_events(void *data)
+{
+ struct hda_bus *bus = data;
+ struct hda_bus_unsolicited *unsol = bus->unsol;
+ struct hda_codec *codec;
+ unsigned int rp, caddr, res;
+
+ while (unsol->rp != unsol->wp) {
+ rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE;
+ unsol->rp = rp;
+ rp <<= 1;
+ res = unsol->queue[rp];
+ caddr = unsol->queue[rp + 1];
+ if (! (caddr & (1 << 4))) /* no unsolicited event? */
+ continue;
+ codec = bus->caddr_tbl[caddr & 0x0f];
+ if (codec && codec->patch_ops.unsol_event)
+ codec->patch_ops.unsol_event(codec, res);
+ }
+}
+
+/*
+ * initialize unsolicited queue
+ */
+static int init_unsol_queue(struct hda_bus *bus)
+{
+ struct hda_bus_unsolicited *unsol;
+
+ unsol = kcalloc(1, sizeof(*unsol), GFP_KERNEL);
+ if (! unsol) {
+ snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
+ return -ENOMEM;
+ }
+ unsol->workq = create_workqueue("hda_codec");
+ if (! unsol->workq) {
+ snd_printk(KERN_ERR "hda_codec: can't create workqueue\n");
+ kfree(unsol);
+ return -ENOMEM;
+ }
+ INIT_WORK(&unsol->work, process_unsol_events, bus);
+ bus->unsol = unsol;
+ return 0;
+}
+
+/*
+ * destructor
+ */
+static void snd_hda_codec_free(struct hda_codec *codec);
+
+static int snd_hda_bus_free(struct hda_bus *bus)
+{
+ struct list_head *p, *n;
+
+ if (! bus)
+ return 0;
+ if (bus->unsol) {
+ destroy_workqueue(bus->unsol->workq);
+ kfree(bus->unsol);
+ }
+ list_for_each_safe(p, n, &bus->codec_list) {
+ struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+ snd_hda_codec_free(codec);
+ }
+ if (bus->ops.private_free)
+ bus->ops.private_free(bus);
+ kfree(bus);
+ return 0;
+}
+
+static int snd_hda_bus_dev_free(snd_device_t *device)
+{
+ struct hda_bus *bus = device->device_data;
+ return snd_hda_bus_free(bus);
+}
+
+/**
+ * snd_hda_bus_new - create a HDA bus
+ * @card: the card entry
+ * @temp: the template for hda_bus information
+ * @busp: the pointer to store the created bus instance
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp,
+ struct hda_bus **busp)
+{
+ struct hda_bus *bus;
+ int err;
+ static snd_device_ops_t dev_ops = {
+ .dev_free = snd_hda_bus_dev_free,
+ };
+
+ snd_assert(temp, return -EINVAL);
+ snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL);
+
+ if (busp)
+ *busp = NULL;
+
+ bus = kcalloc(1, sizeof(*bus), GFP_KERNEL);
+ if (bus == NULL) {
+ snd_printk(KERN_ERR "can't allocate struct hda_bus\n");
+ return -ENOMEM;
+ }
+
+ bus->card = card;
+ bus->private_data = temp->private_data;
+ bus->pci = temp->pci;
+ bus->modelname = temp->modelname;
+ bus->ops = temp->ops;
+
+ init_MUTEX(&bus->cmd_mutex);
+ INIT_LIST_HEAD(&bus->codec_list);
+
+ init_unsol_queue(bus);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
+ snd_hda_bus_free(bus);
+ return err;
+ }
+ if (busp)
+ *busp = bus;
+ return 0;
+}
+
+
+/*
+ * find a matching codec preset
+ */
+static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec)
+{
+ const struct hda_codec_preset **tbl, *preset;
+
+ for (tbl = hda_preset_tables; *tbl; tbl++) {
+ for (preset = *tbl; preset->id; preset++) {
+ u32 mask = preset->mask;
+ if (! mask)
+ mask = ~0;
+ if (preset->id == (codec->vendor_id & mask))
+ return preset;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * snd_hda_get_codec_name - store the codec name
+ */
+void snd_hda_get_codec_name(struct hda_codec *codec,
+ char *name, int namelen)
+{
+ const struct hda_vendor_id *c;
+ const char *vendor = NULL;
+ u16 vendor_id = codec->vendor_id >> 16;
+ char tmp[16];
+
+ for (c = hda_vendor_ids; c->id; c++) {
+ if (c->id == vendor_id) {
+ vendor = c->name;
+ break;
+ }
+ }
+ if (! vendor) {
+ sprintf(tmp, "Generic %04x", vendor_id);
+ vendor = tmp;
+ }
+ if (codec->preset && codec->preset->name)
+ snprintf(name, namelen, "%s %s", vendor, codec->preset->name);
+ else
+ snprintf(name, namelen, "%s ID %x", vendor, codec->vendor_id & 0xffff);
+}
+
+/*
+ * look for an AFG node
+ *
+ * return 0 if not found
+ */
+static int look_for_afg_node(struct hda_codec *codec)
+{
+ int i, total_nodes;
+ hda_nid_t nid;
+
+ total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
+ for (i = 0; i < total_nodes; i++, nid++) {
+ if ((snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE) & 0xff) ==
+ AC_GRP_AUDIO_FUNCTION)
+ return nid;
+ }
+ return 0;
+}
+
+/*
+ * codec destructor
+ */
+static void snd_hda_codec_free(struct hda_codec *codec)
+{
+ if (! codec)
+ return;
+ list_del(&codec->list);
+ codec->bus->caddr_tbl[codec->addr] = NULL;
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+ kfree(codec);
+}
+
+static void init_amp_hash(struct hda_codec *codec);
+
+/**
+ * snd_hda_codec_new - create a HDA codec
+ * @bus: the bus to assign
+ * @codec_addr: the codec address
+ * @codecp: the pointer to store the generated codec
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+ struct hda_codec **codecp)
+{
+ struct hda_codec *codec;
+ char component[13];
+ int err;
+
+ snd_assert(bus, return -EINVAL);
+ snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL);
+
+ if (bus->caddr_tbl[codec_addr]) {
+ snd_printk(KERN_ERR "hda_codec: address 0x%x is already occupied\n", codec_addr);
+ return -EBUSY;
+ }
+
+ codec = kcalloc(1, sizeof(*codec), GFP_KERNEL);
+ if (codec == NULL) {
+ snd_printk(KERN_ERR "can't allocate struct hda_codec\n");
+ return -ENOMEM;
+ }
+
+ codec->bus = bus;
+ codec->addr = codec_addr;
+ init_MUTEX(&codec->spdif_mutex);
+ init_amp_hash(codec);
+
+ list_add_tail(&codec->list, &bus->codec_list);
+ bus->caddr_tbl[codec_addr] = codec;
+
+ codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID);
+ codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
+ codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID);
+
+ /* FIXME: support for multiple AFGs? */
+ codec->afg = look_for_afg_node(codec);
+ if (! codec->afg) {
+ snd_printk(KERN_ERR "hda_codec: no AFG node found\n");
+ snd_hda_codec_free(codec);
+ return -ENODEV;
+ }
+
+ codec->preset = find_codec_preset(codec);
+ if (! *bus->card->mixername)
+ snd_hda_get_codec_name(codec, bus->card->mixername,
+ sizeof(bus->card->mixername));
+
+ if (codec->preset && codec->preset->patch)
+ err = codec->preset->patch(codec);
+ else
+ err = snd_hda_parse_generic_codec(codec);
+ if (err < 0) {
+ snd_hda_codec_free(codec);
+ return err;
+ }
+
+ snd_hda_codec_proc_new(codec);
+
+ sprintf(component, "HDA:%08x", codec->vendor_id);
+ snd_component_add(codec->bus->card, component);
+
+ if (codecp)
+ *codecp = codec;
+ return 0;
+}
+
+/**
+ * snd_hda_codec_setup_stream - set up the codec for streaming
+ * @codec: the CODEC to set up
+ * @nid: the NID to set up
+ * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
+ * @channel_id: channel id to pass, zero based.
+ * @format: stream format.
+ */
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
+ int channel_id, int format)
+{
+ snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+ nid, stream_tag, channel_id, format);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID,
+ (stream_tag << 4) | channel_id);
+ msleep(1);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
+}
+
+
+/*
+ * amp access functions
+ */
+
+#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + (idx) * 32 + (dir) * 64)
+#define INFO_AMP_CAPS (1<<0)
+#define INFO_AMP_VOL (1<<1)
+
+/* initialize the hash table */
+static void init_amp_hash(struct hda_codec *codec)
+{
+ memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash));
+ codec->num_amp_entries = 0;
+}
+
+/* query the hash. allocate an entry if not found. */
+static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key)
+{
+ u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash);
+ u16 cur = codec->amp_hash[idx];
+ struct hda_amp_info *info;
+
+ while (cur != 0xffff) {
+ info = &codec->amp_info[cur];
+ if (info->key == key)
+ return info;
+ cur = info->next;
+ }
+
+ /* add a new hash entry */
+ if (codec->num_amp_entries >= ARRAY_SIZE(codec->amp_info)) {
+ snd_printk(KERN_ERR "hda_codec: Tooooo many amps!\n");
+ return NULL;
+ }
+ cur = codec->num_amp_entries++;
+ info = &codec->amp_info[cur];
+ info->key = key;
+ info->status = 0; /* not initialized yet */
+ info->next = codec->amp_hash[idx];
+ codec->amp_hash[idx] = cur;
+
+ return info;
+}
+
+/*
+ * query AMP capabilities for the given widget and direction
+ */
+static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+{
+ struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
+
+ if (! info)
+ return 0;
+ if (! (info->status & INFO_AMP_CAPS)) {
+ if (!(snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_AMP_OVRD))
+ nid = codec->afg;
+ info->amp_caps = snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ info->status |= INFO_AMP_CAPS;
+ }
+ return info->amp_caps;
+}
+
+/*
+ * read the current volume to info
+ * if the cache exists, read from the cache.
+ */
+static void get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+ hda_nid_t nid, int ch, int direction, int index)
+{
+ u32 val, parm;
+
+ if (info->status & (INFO_AMP_VOL << ch))
+ return;
+
+ parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+ parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+ parm |= index;
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm);
+ info->vol[ch] = val & 0xff;
+ info->status |= INFO_AMP_VOL << ch;
+}
+
+/*
+ * write the current volume in info to the h/w
+ */
+static void put_vol_mute(struct hda_codec *codec,
+ hda_nid_t nid, int ch, int direction, int index, int val)
+{
+ u32 parm;
+
+ parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
+ parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
+ parm |= index << AC_AMP_SET_INDEX_SHIFT;
+ parm |= val;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
+}
+
+/*
+ * read/write AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
+ */
+int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index)
+{
+ struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+ if (! info)
+ return 0;
+ get_vol_mute(codec, info, nid, ch, direction, index);
+ return info->vol[ch];
+}
+
+int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val)
+{
+ struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
+ if (! info)
+ return 0;
+ get_vol_mute(codec, info, nid, ch, direction, idx);
+ if (info->vol[ch] == val && ! codec->in_resume)
+ return 0;
+ put_vol_mute(codec, nid, ch, direction, idx, val);
+ info->vol[ch] = val;
+ return 1;
+}
+
+
+/*
+ * AMP control callbacks
+ */
+/* retrieve parameters from private_value */
+#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
+#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
+#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
+#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+
+/* volume */
+int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 nid = get_amp_nid(kcontrol);
+ u8 chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ u32 caps;
+
+ caps = query_amp_caps(codec, nid, dir);
+ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; /* num steps */
+ if (! caps) {
+ printk(KERN_WARNING "hda_codec: num_steps = 0 for NID=0x%x\n", nid);
+ return -EINVAL;
+ }
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = caps;
+ return 0;
+}
+
+int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ if (chs & 1)
+ *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f;
+ if (chs & 2)
+ *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f;
+ return 0;
+}
+
+int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ int val;
+ long *valp = ucontrol->value.integer.value;
+ int change = 0;
+
+ if (chs & 1) {
+ val = *valp & 0x7f;
+ val |= snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80;
+ change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val);
+ valp++;
+ }
+ if (chs & 2) {
+ val = *valp & 0x7f;
+ val |= snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80;
+ change |= snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val);
+ }
+ return change;
+}
+
+/* switch */
+int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ if (chs & 1)
+ *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80) ? 0 : 1;
+ if (chs & 2)
+ *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80) ? 0 : 1;
+ return 0;
+}
+
+int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ int val;
+ long *valp = ucontrol->value.integer.value;
+ int change = 0;
+
+ if (chs & 1) {
+ val = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f;
+ val |= *valp ? 0 : 0x80;
+ change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val);
+ valp++;
+ }
+ if (chs & 2) {
+ val = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f;
+ val |= *valp ? 0 : 0x80;
+ change = snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val);
+ }
+ return change;
+}
+
+/*
+ * SPDIF out controls
+ */
+
+static int snd_hda_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_hda_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_NONAUDIO |
+ IEC958_AES0_CON_EMPHASIS_5015 |
+ IEC958_AES0_CON_NOT_COPYRIGHT;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+ IEC958_AES1_CON_ORIGINAL;
+ return 0;
+}
+
+static int snd_hda_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PRO_EMPHASIS_5015;
+ return 0;
+}
+
+static int snd_hda_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
+ ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
+
+ return 0;
+}
+
+/* convert from SPDIF status bits to HDA SPDIF bits
+ * bit 0 (DigEn) is always set zero (to be filled later)
+ */
+static unsigned short convert_from_spdif_status(unsigned int sbits)
+{
+ unsigned short val = 0;
+
+ if (sbits & IEC958_AES0_PROFESSIONAL)
+ val |= 1 << 6;
+ if (sbits & IEC958_AES0_NONAUDIO)
+ val |= 1 << 5;
+ if (sbits & IEC958_AES0_PROFESSIONAL) {
+ if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
+ val |= 1 << 3;
+ } else {
+ if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
+ val |= 1 << 3;
+ if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
+ val |= 1 << 4;
+ if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
+ val |= 1 << 7;
+ val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
+ }
+ return val;
+}
+
+/* convert to SPDIF status bits from HDA SPDIF bits
+ */
+static unsigned int convert_to_spdif_status(unsigned short val)
+{
+ unsigned int sbits = 0;
+
+ if (val & (1 << 5))
+ sbits |= IEC958_AES0_NONAUDIO;
+ if (val & (1 << 6))
+ sbits |= IEC958_AES0_PROFESSIONAL;
+ if (sbits & IEC958_AES0_PROFESSIONAL) {
+ if (sbits & (1 << 3))
+ sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
+ } else {
+ if (val & (1 << 3))
+ sbits |= IEC958_AES0_CON_EMPHASIS_5015;
+ if (! (val & (1 << 4)))
+ sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
+ if (val & (1 << 7))
+ sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
+ sbits |= val & (0x7f << 8);
+ }
+ return sbits;
+}
+
+static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned short val;
+ int change;
+
+ down(&codec->spdif_mutex);
+ codec->spdif_status = ucontrol->value.iec958.status[0] |
+ ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
+ ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
+ ((unsigned int)ucontrol->value.iec958.status[3] << 24);
+ val = convert_from_spdif_status(codec->spdif_status);
+ val |= codec->spdif_ctls & 1;
+ change = codec->spdif_ctls != val;
+ codec->spdif_ctls = val;
+
+ if (change || codec->in_resume) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8);
+ }
+
+ up(&codec->spdif_mutex);
+ return change;
+}
+
+static int snd_hda_spdif_out_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = codec->spdif_ctls & 1;
+ return 0;
+}
+
+static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned short val;
+ int change;
+
+ down(&codec->spdif_mutex);
+ val = codec->spdif_ctls & ~1;
+ if (ucontrol->value.integer.value[0])
+ val |= 1;
+ change = codec->spdif_ctls != val;
+ if (change || codec->in_resume) {
+ codec->spdif_ctls = val;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT |
+ AC_AMP_SET_OUTPUT | ((val & 1) ? 0 : 0x80));
+ }
+ up(&codec->spdif_mutex);
+ return change;
+}
+
+static snd_kcontrol_new_t dig_mixes[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_cmask_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_pmask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_default_get,
+ .put = snd_hda_spdif_default_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+ .info = snd_hda_spdif_out_switch_info,
+ .get = snd_hda_spdif_out_switch_get,
+ .put = snd_hda_spdif_out_switch_put,
+ },
+ { } /* end */
+};
+
+/**
+ * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio out widget NID
+ *
+ * Creates controls related with the SPDIF output.
+ * Called from each patch supporting the SPDIF out.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+ snd_kcontrol_new_t *dig_mix;
+
+ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+ kctl = snd_ctl_new1(dig_mix, codec);
+ kctl->private_value = nid;
+ if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+ return err;
+ }
+ codec->spdif_ctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+ codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
+ return 0;
+}
+
+/*
+ * SPDIF input
+ */
+
+#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info
+
+static int snd_hda_spdif_in_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = codec->spdif_in_enable;
+ return 0;
+}
+
+static int snd_hda_spdif_in_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int val = !!ucontrol->value.integer.value[0];
+ int change;
+
+ down(&codec->spdif_mutex);
+ change = codec->spdif_in_enable != val;
+ if (change || codec->in_resume) {
+ codec->spdif_in_enable = val;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val);
+ }
+ up(&codec->spdif_mutex);
+ return change;
+}
+
+static int snd_hda_spdif_in_status_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned short val;
+ unsigned int sbits;
+
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+ sbits = convert_to_spdif_status(val);
+ ucontrol->value.iec958.status[0] = sbits;
+ ucontrol->value.iec958.status[1] = sbits >> 8;
+ ucontrol->value.iec958.status[2] = sbits >> 16;
+ ucontrol->value.iec958.status[3] = sbits >> 24;
+ return 0;
+}
+
+static snd_kcontrol_new_t dig_in_ctls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+ .info = snd_hda_spdif_in_switch_info,
+ .get = snd_hda_spdif_in_switch_get,
+ .put = snd_hda_spdif_in_switch_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_in_status_get,
+ },
+ { } /* end */
+};
+
+/**
+ * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio in widget NID
+ *
+ * Creates controls related with the SPDIF input.
+ * Called from each patch supporting the SPDIF in.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+ snd_kcontrol_new_t *dig_mix;
+
+ for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
+ kctl = snd_ctl_new1(dig_mix, codec);
+ kctl->private_value = nid;
+ if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+ return err;
+ }
+ codec->spdif_in_enable = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) & 1;
+ return 0;
+}
+
+
+/**
+ * snd_hda_build_controls - build mixer controls
+ * @bus: the BUS
+ *
+ * Creates mixer controls for each codec included in the bus.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hda_build_controls(struct hda_bus *bus)
+{
+ struct list_head *p;
+
+ /* build controls */
+ list_for_each(p, &bus->codec_list) {
+ struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+ int err;
+ if (! codec->patch_ops.build_controls)
+ continue;
+ err = codec->patch_ops.build_controls(codec);
+ if (err < 0)
+ return err;
+ }
+
+ /* initialize */
+ list_for_each(p, &bus->codec_list) {
+ struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+ int err;
+ if (! codec->patch_ops.init)
+ continue;
+ err = codec->patch_ops.init(codec);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+
+/*
+ * stream formats
+ */
+static unsigned int rate_bits[][3] = {
+ /* rate in Hz, ALSA rate bitmask, HDA format value */
+ { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */
+ { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */
+ { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */
+ { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */
+ { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */
+ { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */
+ { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */
+ { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */
+ { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */
+ { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */
+ { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */
+ { 0 }
+};
+
+/**
+ * snd_hda_calc_stream_format - calculate format bitset
+ * @rate: the sample rate
+ * @channels: the number of channels
+ * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
+ * @maxbps: the max. bps
+ *
+ * Calculate the format bitset from the given rate, channels and th PCM format.
+ *
+ * Return zero if invalid.
+ */
+unsigned int snd_hda_calc_stream_format(unsigned int rate,
+ unsigned int channels,
+ unsigned int format,
+ unsigned int maxbps)
+{
+ int i;
+ unsigned int val = 0;
+
+ for (i = 0; rate_bits[i][0]; i++)
+ if (rate_bits[i][0] == rate) {
+ val = rate_bits[i][2];
+ break;
+ }
+ if (! rate_bits[i][0]) {
+ snd_printdd("invalid rate %d\n", rate);
+ return 0;
+ }
+
+ if (channels == 0 || channels > 8) {
+ snd_printdd("invalid channels %d\n", channels);
+ return 0;
+ }
+ val |= channels - 1;
+
+ switch (snd_pcm_format_width(format)) {
+ case 8: val |= 0x00; break;
+ case 16: val |= 0x10; break;
+ case 20:
+ case 24:
+ case 32:
+ if (maxbps >= 32)
+ val |= 0x40;
+ else if (maxbps >= 24)
+ val |= 0x30;
+ else
+ val |= 0x20;
+ break;
+ default:
+ snd_printdd("invalid format width %d\n", snd_pcm_format_width(format));
+ return 0;
+ }
+
+ return val;
+}
+
+/**
+ * snd_hda_query_supported_pcm - query the supported PCM rates and formats
+ * @codec: the HDA codec
+ * @nid: NID to query
+ * @ratesp: the pointer to store the detected rate bitflags
+ * @formatsp: the pointer to store the detected formats
+ * @bpsp: the pointer to store the detected format widths
+ *
+ * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
+ * or @bsps argument is ignored.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+ u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
+{
+ int i;
+ unsigned int val, streams;
+
+ val = 0;
+ if (nid != codec->afg &&
+ snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+ val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+ if (val == -1)
+ return -EIO;
+ }
+ if (! val)
+ val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+
+ if (ratesp) {
+ u32 rates = 0;
+ for (i = 0; rate_bits[i][0]; i++) {
+ if (val & (1 << i))
+ rates |= rate_bits[i][1];
+ }
+ *ratesp = rates;
+ }
+
+ if (formatsp || bpsp) {
+ u64 formats = 0;
+ unsigned int bps;
+ unsigned int wcaps;
+
+ wcaps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+ if (streams == -1)
+ return -EIO;
+ if (! streams) {
+ streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+ if (streams == -1)
+ return -EIO;
+ }
+
+ bps = 0;
+ if (streams & AC_SUPFMT_PCM) {
+ if (val & AC_SUPPCM_BITS_8) {
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (val & AC_SUPPCM_BITS_16) {
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+ bps = 16;
+ }
+ if (wcaps & AC_WCAP_DIGITAL) {
+ if (val & AC_SUPPCM_BITS_32)
+ formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (val & AC_SUPPCM_BITS_24)
+ bps = 24;
+ else if (val & AC_SUPPCM_BITS_20)
+ bps = 20;
+ } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|AC_SUPPCM_BITS_32)) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (val & AC_SUPPCM_BITS_32)
+ bps = 32;
+ else if (val & AC_SUPPCM_BITS_20)
+ bps = 20;
+ else if (val & AC_SUPPCM_BITS_24)
+ bps = 24;
+ }
+ }
+ else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */
+ formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+ bps = 32;
+ } else if (streams == AC_SUPFMT_AC3) { /* should be exclusive */
+ /* temporary hack: we have still no proper support
+ * for the direct AC3 stream...
+ */
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (formatsp)
+ *formatsp = formats;
+ if (bpsp)
+ *bpsp = bps;
+ }
+
+ return 0;
+}
+
+/**
+ * snd_hda_is_supported_format - check whether the given node supports the format val
+ *
+ * Returns 1 if supported, 0 if not.
+ */
+int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int format)
+{
+ int i;
+ unsigned int val = 0, rate, stream;
+
+ if (nid != codec->afg &&
+ snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+ val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+ if (val == -1)
+ return 0;
+ }
+ if (! val) {
+ val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+ if (val == -1)
+ return 0;
+ }
+
+ rate = format & 0xff00;
+ for (i = 0; rate_bits[i][0]; i++)
+ if (rate_bits[i][2] == rate) {
+ if (val & (1 << i))
+ break;
+ return 0;
+ }
+ if (! rate_bits[i][0])
+ return 0;
+
+ stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+ if (stream == -1)
+ return 0;
+ if (! stream && nid != codec->afg)
+ stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+ if (! stream || stream == -1)
+ return 0;
+
+ if (stream & AC_SUPFMT_PCM) {
+ switch (format & 0xf0) {
+ case 0x00:
+ if (! (val & AC_SUPPCM_BITS_8))
+ return 0;
+ break;
+ case 0x10:
+ if (! (val & AC_SUPPCM_BITS_16))
+ return 0;
+ break;
+ case 0x20:
+ if (! (val & AC_SUPPCM_BITS_20))
+ return 0;
+ break;
+ case 0x30:
+ if (! (val & AC_SUPPCM_BITS_24))
+ return 0;
+ break;
+ case 0x40:
+ if (! (val & AC_SUPPCM_BITS_32))
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ } else {
+ /* FIXME: check for float32 and AC3? */
+ }
+
+ return 1;
+}
+
+/*
+ * PCM stuff
+ */
+static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ return 0;
+}
+
+static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ return 0;
+}
+
+static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
+ return 0;
+}
+
+static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info)
+{
+ if (info->nid) {
+ /* query support PCM information from the given NID */
+ if (! info->rates || ! info->formats)
+ snd_hda_query_supported_pcm(codec, info->nid,
+ info->rates ? NULL : &info->rates,
+ info->formats ? NULL : &info->formats,
+ info->maxbps ? NULL : &info->maxbps);
+ }
+ if (info->ops.open == NULL)
+ info->ops.open = hda_pcm_default_open_close;
+ if (info->ops.close == NULL)
+ info->ops.close = hda_pcm_default_open_close;
+ if (info->ops.prepare == NULL) {
+ snd_assert(info->nid, return -EINVAL);
+ info->ops.prepare = hda_pcm_default_prepare;
+ }
+ if (info->ops.prepare == NULL) {
+ snd_assert(info->nid, return -EINVAL);
+ info->ops.prepare = hda_pcm_default_prepare;
+ }
+ if (info->ops.cleanup == NULL) {
+ snd_assert(info->nid, return -EINVAL);
+ info->ops.cleanup = hda_pcm_default_cleanup;
+ }
+ return 0;
+}
+
+/**
+ * snd_hda_build_pcms - build PCM information
+ * @bus: the BUS
+ *
+ * Create PCM information for each codec included in the bus.
+ *
+ * The build_pcms codec patch is requested to set up codec->num_pcms and
+ * codec->pcm_info properly. The array is referred by the top-level driver
+ * to create its PCM instances.
+ * The allocated codec->pcm_info should be released in codec->patch_ops.free
+ * callback.
+ *
+ * At least, substreams, channels_min and channels_max must be filled for
+ * each stream. substreams = 0 indicates that the stream doesn't exist.
+ * When rates and/or formats are zero, the supported values are queried
+ * from the given nid. The nid is used also by the default ops.prepare
+ * and ops.cleanup callbacks.
+ *
+ * The driver needs to call ops.open in its open callback. Similarly,
+ * ops.close is supposed to be called in the close callback.
+ * ops.prepare should be called in the prepare or hw_params callback
+ * with the proper parameters for set up.
+ * ops.cleanup should be called in hw_free for clean up of streams.
+ *
+ * This function returns 0 if successfull, or a negative error code.
+ */
+int snd_hda_build_pcms(struct hda_bus *bus)
+{
+ struct list_head *p;
+
+ list_for_each(p, &bus->codec_list) {
+ struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+ unsigned int pcm, s;
+ int err;
+ if (! codec->patch_ops.build_pcms)
+ continue;
+ err = codec->patch_ops.build_pcms(codec);
+ if (err < 0)
+ return err;
+ for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+ for (s = 0; s < 2; s++) {
+ struct hda_pcm_stream *info;
+ info = &codec->pcm_info[pcm].stream[s];
+ if (! info->substreams)
+ continue;
+ err = set_pcm_default_values(codec, info);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * snd_hda_check_board_config - compare the current codec with the config table
+ * @codec: the HDA codec
+ * @tbl: configuration table, terminated by null entries
+ *
+ * Compares the modelname or PCI subsystem id of the current codec with the
+ * given configuration table. If a matching entry is found, returns its
+ * config value (supposed to be 0 or positive).
+ *
+ * If no entries are matching, the function returns a negative value.
+ */
+int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl)
+{
+ struct hda_board_config *c;
+
+ if (codec->bus->modelname) {
+ for (c = tbl; c->modelname || c->pci_vendor; c++) {
+ if (c->modelname &&
+ ! strcmp(codec->bus->modelname, c->modelname)) {
+ snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname);
+ return c->config;
+ }
+ }
+ }
+
+ if (codec->bus->pci) {
+ u16 subsystem_vendor, subsystem_device;
+ pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+ pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+ for (c = tbl; c->modelname || c->pci_vendor; c++) {
+ if (c->pci_vendor == subsystem_vendor &&
+ c->pci_device == subsystem_device)
+ return c->config;
+ }
+ }
+ return -1;
+}
+
+/**
+ * snd_hda_add_new_ctls - create controls from the array
+ * @codec: the HDA codec
+ * @knew: the array of snd_kcontrol_new_t
+ *
+ * This helper function creates and add new controls in the given array.
+ * The array must be terminated with an empty entry as terminator.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew)
+{
+ int err;
+
+ for (; knew->name; knew++) {
+ err = snd_ctl_add(codec->bus->card, snd_ctl_new1(knew, codec));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+
+/*
+ * input MUX helper
+ */
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo)
+{
+ unsigned int index;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = imux->num_items;
+ index = uinfo->value.enumerated.item;
+ if (index >= imux->num_items)
+ index = imux->num_items - 1;
+ strcpy(uinfo->value.enumerated.name, imux->items[index].label);
+ return 0;
+}
+
+int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
+ snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
+ unsigned int *cur_val)
+{
+ unsigned int idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (*cur_val == idx && ! codec->in_resume)
+ return 0;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+ imux->items[idx].index);
+ *cur_val = idx;
+ return 1;
+}
+
+
+/*
+ * Multi-channel / digital-out PCM helper functions
+ */
+
+/*
+ * open the digital out in the exclusive mode
+ */
+int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+ down(&codec->spdif_mutex);
+ if (mout->dig_out_used) {
+ up(&codec->spdif_mutex);
+ return -EBUSY; /* already being used */
+ }
+ mout->dig_out_used = HDA_DIG_EXCLUSIVE;
+ up(&codec->spdif_mutex);
+ return 0;
+}
+
+/*
+ * release the digital out
+ */
+int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+ down(&codec->spdif_mutex);
+ mout->dig_out_used = 0;
+ up(&codec->spdif_mutex);
+ return 0;
+}
+
+/*
+ * set up more restrictions for analog out
+ */
+int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
+ snd_pcm_substream_t *substream)
+{
+ substream->runtime->hw.channels_max = mout->max_channels;
+ return snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+}
+
+/*
+ * set up the i/o for analog out
+ * when the digital out is available, copy the front out to digital out, too.
+ */
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ hda_nid_t *nids = mout->dac_nids;
+ int chs = substream->runtime->channels;
+ int i;
+
+ down(&codec->spdif_mutex);
+ if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+ if (chs == 2 &&
+ snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
+ ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+ mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+ /* setup digital receiver */
+ snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+ stream_tag, 0, format);
+ } else {
+ mout->dig_out_used = 0;
+ snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+ }
+ }
+ up(&codec->spdif_mutex);
+
+ /* front */
+ snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
+ if (mout->hp_nid)
+ /* headphone out will just decode front left/right (stereo) */
+ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
+ /* surrounds */
+ for (i = 1; i < mout->num_dacs; i++) {
+ if (i == HDA_REAR && chs == 2) /* copy front to rear */
+ snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format);
+ else if (chs >= (i + 1) * 2) /* independent out */
+ snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2,
+ format);
+ }
+ return 0;
+}
+
+/*
+ * clean up the setting for analog out
+ */
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+ hda_nid_t *nids = mout->dac_nids;
+ int i;
+
+ for (i = 0; i < mout->num_dacs; i++)
+ snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
+ if (mout->hp_nid)
+ snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
+ down(&codec->spdif_mutex);
+ if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+ snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+ mout->dig_out_used = 0;
+ }
+ up(&codec->spdif_mutex);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+
+/**
+ * snd_hda_suspend - suspend the codecs
+ * @bus: the HDA bus
+ * @state: suspsend state
+ *
+ * Returns 0 if successful.
+ */
+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
+{
+ struct list_head *p;
+
+ /* FIXME: should handle power widget capabilities */
+ list_for_each(p, &bus->codec_list) {
+ struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+ if (codec->patch_ops.suspend)
+ codec->patch_ops.suspend(codec, state);
+ }
+ return 0;
+}
+
+/**
+ * snd_hda_resume - resume the codecs
+ * @bus: the HDA bus
+ * @state: resume state
+ *
+ * Returns 0 if successful.
+ */
+int snd_hda_resume(struct hda_bus *bus)
+{
+ struct list_head *p;
+
+ list_for_each(p, &bus->codec_list) {
+ struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+ if (codec->patch_ops.resume)
+ codec->patch_ops.resume(codec);
+ }
+ return 0;
+}
+
+/**
+ * snd_hda_resume_ctls - resume controls in the new control list
+ * @codec: the HDA codec
+ * @knew: the array of snd_kcontrol_new_t
+ *
+ * This function resumes the mixer controls in the snd_kcontrol_new_t array,
+ * originally for snd_hda_add_new_ctls().
+ * The array must be terminated with an empty entry as terminator.
+ */
+int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew)
+{
+ snd_ctl_elem_value_t *val;
+
+ val = kmalloc(sizeof(*val), GFP_KERNEL);
+ if (! val)
+ return -ENOMEM;
+ codec->in_resume = 1;
+ for (; knew->name; knew++) {
+ int i, count;
+ count = knew->count ? knew->count : 1;
+ for (i = 0; i < count; i++) {
+ memset(val, 0, sizeof(*val));
+ val->id.iface = knew->iface;
+ val->id.device = knew->device;
+ val->id.subdevice = knew->subdevice;
+ strcpy(val->id.name, knew->name);
+ val->id.index = knew->index ? knew->index : i;
+ /* Assume that get callback reads only from cache,
+ * not accessing to the real hardware
+ */
+ if (snd_ctl_elem_read(codec->bus->card, val) < 0)
+ continue;
+ snd_ctl_elem_write(codec->bus->card, NULL, val);
+ }
+ }
+ codec->in_resume = 0;
+ kfree(val);
+ return 0;
+}
+
+/**
+ * snd_hda_resume_spdif_out - resume the digital out
+ * @codec: the HDA codec
+ */
+int snd_hda_resume_spdif_out(struct hda_codec *codec)
+{
+ return snd_hda_resume_ctls(codec, dig_mixes);
+}
+
+/**
+ * snd_hda_resume_spdif_in - resume the digital in
+ * @codec: the HDA codec
+ */
+int snd_hda_resume_spdif_in(struct hda_codec *codec)
+{
+ return snd_hda_resume_ctls(codec, dig_in_ctls);
+}
+#endif
+
+/*
+ * symbols exported for controller modules
+ */
+EXPORT_SYMBOL(snd_hda_codec_read);
+EXPORT_SYMBOL(snd_hda_codec_write);
+EXPORT_SYMBOL(snd_hda_sequence_write);
+EXPORT_SYMBOL(snd_hda_get_sub_nodes);
+EXPORT_SYMBOL(snd_hda_queue_unsol_event);
+EXPORT_SYMBOL(snd_hda_bus_new);
+EXPORT_SYMBOL(snd_hda_codec_new);
+EXPORT_SYMBOL(snd_hda_codec_setup_stream);
+EXPORT_SYMBOL(snd_hda_calc_stream_format);
+EXPORT_SYMBOL(snd_hda_build_pcms);
+EXPORT_SYMBOL(snd_hda_build_controls);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_hda_suspend);
+EXPORT_SYMBOL(snd_hda_resume);
+#endif
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_hda_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_hda_exit(void)
+{
+}
+
+module_init(alsa_hda_init)
+module_exit(alsa_hda_exit)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
new file mode 100644
index 00000000000..c9e9dc9c7c9
--- /dev/null
+++ b/sound/pci/hda/hda_codec.h
@@ -0,0 +1,604 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SOUND_HDA_CODEC_H
+#define __SOUND_HDA_CODEC_H
+
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+
+/*
+ * nodes
+ */
+#define AC_NODE_ROOT 0x00
+
+/*
+ * function group types
+ */
+enum {
+ AC_GRP_AUDIO_FUNCTION = 0x01,
+ AC_GRP_MODEM_FUNCTION = 0x02,
+};
+
+/*
+ * widget types
+ */
+enum {
+ AC_WID_AUD_OUT, /* Audio Out */
+ AC_WID_AUD_IN, /* Audio In */
+ AC_WID_AUD_MIX, /* Audio Mixer */
+ AC_WID_AUD_SEL, /* Audio Selector */
+ AC_WID_PIN, /* Pin Complex */
+ AC_WID_POWER, /* Power */
+ AC_WID_VOL_KNB, /* Volume Knob */
+ AC_WID_BEEP, /* Beep Generator */
+ AC_WID_VENDOR = 0x0f /* Vendor specific */
+};
+
+/*
+ * GET verbs
+ */
+#define AC_VERB_GET_STREAM_FORMAT 0x0a00
+#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
+#define AC_VERB_GET_PROC_COEF 0x0c00
+#define AC_VERB_GET_COEF_INDEX 0x0d00
+#define AC_VERB_PARAMETERS 0x0f00
+#define AC_VERB_GET_CONNECT_SEL 0x0f01
+#define AC_VERB_GET_CONNECT_LIST 0x0f02
+#define AC_VERB_GET_PROC_STATE 0x0f03
+#define AC_VERB_GET_SDI_SELECT 0x0f04
+#define AC_VERB_GET_POWER_STATE 0x0f05
+#define AC_VERB_GET_CONV 0x0f06
+#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
+#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
+#define AC_VERB_GET_PIN_SENSE 0x0f09
+#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
+#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
+#define AC_VERB_GET_DIGI_CONVERT 0x0f0d
+#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
+/* f10-f1a: GPIO */
+#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
+
+/*
+ * SET verbs
+ */
+#define AC_VERB_SET_STREAM_FORMAT 0x200
+#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
+#define AC_VERB_SET_PROC_COEF 0x400
+#define AC_VERB_SET_COEF_INDEX 0x500
+#define AC_VERB_SET_CONNECT_SEL 0x701
+#define AC_VERB_SET_PROC_STATE 0x703
+#define AC_VERB_SET_SDI_SELECT 0x704
+#define AC_VERB_SET_POWER_STATE 0x705
+#define AC_VERB_SET_CHANNEL_STREAMID 0x706
+#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
+#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define AC_VERB_SET_PIN_SENSE 0x709
+#define AC_VERB_SET_BEEP_CONTROL 0x70a
+#define AC_VERB_SET_EAPD_BTLENALBE 0x70c
+#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
+#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
+#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
+#define AC_VERB_SET_CODEC_RESET 0x7ff
+
+/*
+ * Parameter IDs
+ */
+#define AC_PAR_VENDOR_ID 0x00
+#define AC_PAR_SUBSYSTEM_ID 0x01
+#define AC_PAR_REV_ID 0x02
+#define AC_PAR_NODE_COUNT 0x04
+#define AC_PAR_FUNCTION_TYPE 0x05
+#define AC_PAR_AUDIO_FG_CAP 0x08
+#define AC_PAR_AUDIO_WIDGET_CAP 0x09
+#define AC_PAR_PCM 0x0a
+#define AC_PAR_STREAM 0x0b
+#define AC_PAR_PIN_CAP 0x0c
+#define AC_PAR_AMP_IN_CAP 0x0d
+#define AC_PAR_CONNLIST_LEN 0x0e
+#define AC_PAR_POWER_STATE 0x0f
+#define AC_PAR_PROC_CAP 0x10
+#define AC_PAR_GPIO_CAP 0x11
+#define AC_PAR_AMP_OUT_CAP 0x12
+
+/*
+ * AC_VERB_PARAMETERS results (32bit)
+ */
+
+/* Function Group Type */
+#define AC_FGT_TYPE (0xff<<0)
+#define AC_FGT_TYPE_SHIFT 0
+#define AC_FGT_UNSOL_CAP (1<<8)
+
+/* Audio Function Group Capabilities */
+#define AC_AFG_OUT_DELAY (0xf<<0)
+#define AC_AFG_IN_DELAY (0xf<<8)
+#define AC_AFG_BEEP_GEN (1<<16)
+
+/* Audio Widget Capabilities */
+#define AC_WCAP_STEREO (1<<0) /* stereo I/O */
+#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */
+#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */
+#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */
+#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */
+#define AC_WCAP_STRIPE (1<<5) /* stripe */
+#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */
+#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */
+#define AC_WCAP_CONN_LIST (1<<8) /* connection list */
+#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
+#define AC_WCAP_POWER (1<<10) /* power control */
+#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
+#define AC_WCAP_DELAY (0xf<<16)
+#define AC_WCAP_DELAY_SHIFT 16
+#define AC_WCAP_TYPE (0xf<<20)
+#define AC_WCAP_TYPE_SHIFT 20
+
+/* supported PCM rates and bits */
+#define AC_SUPPCM_RATES (0xfff << 0)
+#define AC_SUPPCM_BITS_8 (1<<16)
+#define AC_SUPPCM_BITS_16 (1<<17)
+#define AC_SUPPCM_BITS_20 (1<<18)
+#define AC_SUPPCM_BITS_24 (1<<19)
+#define AC_SUPPCM_BITS_32 (1<<20)
+
+/* supported PCM stream format */
+#define AC_SUPFMT_PCM (1<<0)
+#define AC_SUPFMT_FLOAT32 (1<<1)
+#define AC_SUPFMT_AC3 (1<<2)
+
+/* Pin widget capabilies */
+#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
+#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
+#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */
+#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */
+#define AC_PINCAP_OUT (1<<4) /* output capable */
+#define AC_PINCAP_IN (1<<5) /* input capable */
+#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
+#define AC_PINCAP_VREF (7<<8)
+#define AC_PINCAP_VREF_SHIFT 8
+#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
+/* Vref status (used in pin cap and pin ctl) */
+#define AC_PIN_VREF_HIZ (1<<0) /* Hi-Z */
+#define AC_PIN_VREF_50 (1<<1) /* 50% */
+#define AC_PIN_VREF_GRD (1<<2) /* ground */
+#define AC_PIN_VREF_80 (1<<4) /* 80% */
+#define AC_PIN_VREF_100 (1<<5) /* 100% */
+
+
+/* Amplifier capabilities */
+#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */
+#define AC_AMPCAP_OFFSET_SHIFT 0
+#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */
+#define AC_AMPCAP_NUM_STEPS_SHIFT 8
+#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB in 0.25dB */
+#define AC_AMPCAP_STEP_SIZE_SHIFT 16
+#define AC_AMPCAP_MUTE (1<<31) /* mute capable */
+#define AC_AMPCAP_MUTE_SHIFT 31
+
+/* Connection list */
+#define AC_CLIST_LENGTH (0x7f<<0)
+#define AC_CLIST_LONG (1<<7)
+
+/* Supported power status */
+#define AC_PWRST_D0SUP (1<<0)
+#define AC_PWRST_D1SUP (1<<1)
+#define AC_PWRST_D2SUP (1<<2)
+#define AC_PWRST_D3SUP (1<<3)
+
+/* Processing capabilies */
+#define AC_PCAP_BENIGN (1<<0)
+#define AC_PCAP_NUM_COEF (0xff<<8)
+
+/* Volume knobs capabilities */
+#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
+#define AC_KNBCAP_DELTA (1<<8)
+
+/*
+ * Control Parameters
+ */
+
+/* Amp gain/mute */
+#define AC_AMP_MUTE (1<<8)
+#define AC_AMP_GAIN (0x7f)
+#define AC_AMP_GET_INDEX (0xf<<0)
+
+#define AC_AMP_GET_LEFT (1<<13)
+#define AC_AMP_GET_RIGHT (0<<13)
+#define AC_AMP_GET_OUTPUT (1<<15)
+#define AC_AMP_GET_INPUT (0<<15)
+
+#define AC_AMP_SET_INDEX (0xf<<8)
+#define AC_AMP_SET_INDEX_SHIFT 8
+#define AC_AMP_SET_RIGHT (1<<12)
+#define AC_AMP_SET_LEFT (1<<13)
+#define AC_AMP_SET_INPUT (1<<14)
+#define AC_AMP_SET_OUTPUT (1<<15)
+
+/* DIGITAL1 bits */
+#define AC_DIG1_ENABLE (1<<0)
+#define AC_DIG1_V (1<<1)
+#define AC_DIG1_VCFG (1<<2)
+#define AC_DIG1_EMPHASIS (1<<3)
+#define AC_DIG1_COPYRIGHT (1<<4)
+#define AC_DIG1_NONAUDIO (1<<5)
+#define AC_DIG1_PROFESSIONAL (1<<6)
+#define AC_DIG1_LEVEL (1<<7)
+
+/* Pin widget control - 8bit */
+#define AC_PINCTL_VREFEN (0x7<<0)
+#define AC_PINCTL_IN_EN (1<<5)
+#define AC_PINCTL_OUT_EN (1<<6)
+#define AC_PINCTL_HP_EN (1<<7)
+
+/* configuration default - 32bit */
+#define AC_DEFCFG_SEQUENCE (0xf<<0)
+#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
+#define AC_DEFCFG_MISC (0xf<<8)
+#define AC_DEFCFG_COLOR (0xf<<12)
+#define AC_DEFCFG_COLOR_SHIFT 12
+#define AC_DEFCFG_CONN_TYPE (0xf<<16)
+#define AC_DEFCFG_CONN_TYPE_SHIFT 16
+#define AC_DEFCFG_DEVICE (0xf<<20)
+#define AC_DEFCFG_DEVICE_SHIFT 20
+#define AC_DEFCFG_LOCATION (0x3f<<24)
+#define AC_DEFCFG_LOCATION_SHIFT 24
+#define AC_DEFCFG_PORT_CONN (0x3<<30)
+#define AC_DEFCFG_PORT_CONN_SHIFT 30
+
+/* device device types (0x0-0xf) */
+enum {
+ AC_JACK_LINE_OUT,
+ AC_JACK_SPEAKER,
+ AC_JACK_HP_OUT,
+ AC_JACK_CD,
+ AC_JACK_SPDIF_OUT,
+ AC_JACK_DIG_OTHER_OUT,
+ AC_JACK_MODEM_LINE_SIDE,
+ AC_JACK_MODEM_HAND_SIDE,
+ AC_JACK_LINE_IN,
+ AC_JACK_AUX,
+ AC_JACK_MIC_IN,
+ AC_JACK_TELEPHONY,
+ AC_JACK_SPDIF_IN,
+ AC_JACK_DIG_OTHER_IN,
+ AC_JACK_OTHER = 0xf,
+};
+
+/* jack connection types (0x0-0xf) */
+enum {
+ AC_JACK_CONN_UNKNOWN,
+ AC_JACK_CONN_1_8,
+ AC_JACK_CONN_1_4,
+ AC_JACK_CONN_ATAPI,
+ AC_JACK_CONN_RCA,
+ AC_JACK_CONN_OPTICAL,
+ AC_JACK_CONN_OTHER_DIGITAL,
+ AC_JACK_CONN_OTHER_ANALOG,
+ AC_JACK_CONN_DIN,
+ AC_JACK_CONN_XLR,
+ AC_JACK_CONN_RJ11,
+ AC_JACK_CONN_COMB,
+ AC_JACK_CONN_OTHER = 0xf,
+};
+
+/* jack colors (0x0-0xf) */
+enum {
+ AC_JACK_COLOR_UNKNOWN,
+ AC_JACK_COLOR_BLACK,
+ AC_JACK_COLOR_GREY,
+ AC_JACK_COLOR_BLUE,
+ AC_JACK_COLOR_GREEN,
+ AC_JACK_COLOR_RED,
+ AC_JACK_COLOR_ORANGE,
+ AC_JACK_COLOR_YELLOW,
+ AC_JACK_COLOR_PURPLE,
+ AC_JACK_COLOR_PINK,
+ AC_JACK_COLOR_WHITE = 0xe,
+ AC_JACK_COLOR_OTHER,
+};
+
+/* Jack location (0x0-0x3f) */
+/* common case */
+enum {
+ AC_JACK_LOC_NONE,
+ AC_JACK_LOC_REAR,
+ AC_JACK_LOC_FRONT,
+ AC_JACK_LOC_LEFT,
+ AC_JACK_LOC_RIGHT,
+ AC_JACK_LOC_TOP,
+ AC_JACK_LOC_BOTTOM,
+};
+/* bits 4-5 */
+enum {
+ AC_JACK_LOC_EXTERNAL = 0x00,
+ AC_JACK_LOC_INTERNAL = 0x10,
+ AC_JACK_LOC_SEPARATE = 0x20,
+ AC_JACK_LOC_OTHER = 0x30,
+};
+enum {
+ /* external on primary chasis */
+ AC_JACK_LOC_REAR_PANEL = 0x07,
+ AC_JACK_LOC_DRIVE_BAY,
+ /* internal */
+ AC_JACK_LOC_RISER = 0x17,
+ AC_JACK_LOC_HDMI,
+ AC_JACK_LOC_ATAPI,
+ /* others */
+ AC_JACK_LOC_MOBILE_IN = 0x37,
+ AC_JACK_LOC_MOBILE_OUT,
+};
+
+/* Port connectivity (0-3) */
+enum {
+ AC_JACK_PORT_COMPLEX,
+ AC_JACK_PORT_NONE,
+ AC_JACK_PORT_FIXED,
+ AC_JACK_PORT_BOTH,
+};
+
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS 16
+
+/* max. codec address */
+#define HDA_MAX_CODEC_ADDRESS 0x0f
+
+/*
+ * Structures
+ */
+
+struct hda_bus;
+struct hda_codec;
+struct hda_pcm;
+struct hda_pcm_stream;
+struct hda_bus_unsolicited;
+
+/* NID type */
+typedef u16 hda_nid_t;
+
+/* bus operators */
+struct hda_bus_ops {
+ /* send a single command */
+ int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int parm);
+ /* get a response from the last command */
+ unsigned int (*get_response)(struct hda_codec *codec);
+ /* free the private data */
+ void (*private_free)(struct hda_bus *);
+};
+
+/* template to pass to the bus constructor */
+struct hda_bus_template {
+ void *private_data;
+ struct pci_dev *pci;
+ const char *modelname;
+ struct hda_bus_ops ops;
+};
+
+/*
+ * codec bus
+ *
+ * each controller needs to creata a hda_bus to assign the accessor.
+ * A hda_bus contains several codecs in the list codec_list.
+ */
+struct hda_bus {
+ snd_card_t *card;
+
+ /* copied from template */
+ void *private_data;
+ struct pci_dev *pci;
+ const char *modelname;
+ struct hda_bus_ops ops;
+
+ /* codec linked list */
+ struct list_head codec_list;
+ struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS]; /* caddr -> codec */
+
+ struct semaphore cmd_mutex;
+
+ /* unsolicited event queue */
+ struct hda_bus_unsolicited *unsol;
+
+ snd_info_entry_t *proc;
+};
+
+/*
+ * codec preset
+ *
+ * Known codecs have the patch to build and set up the controls/PCMs
+ * better than the generic parser.
+ */
+struct hda_codec_preset {
+ unsigned int id;
+ unsigned int mask;
+ unsigned int subs;
+ unsigned int subs_mask;
+ unsigned int rev;
+ const char *name;
+ int (*patch)(struct hda_codec *codec);
+};
+
+/* ops set by the preset patch */
+struct hda_codec_ops {
+ int (*build_controls)(struct hda_codec *codec);
+ int (*build_pcms)(struct hda_codec *codec);
+ int (*init)(struct hda_codec *codec);
+ void (*free)(struct hda_codec *codec);
+ void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+#ifdef CONFIG_PM
+ int (*suspend)(struct hda_codec *codec, pm_message_t state);
+ int (*resume)(struct hda_codec *codec);
+#endif
+};
+
+/* record for amp information cache */
+struct hda_amp_info {
+ u32 key; /* hash key */
+ u32 amp_caps; /* amp capabilities */
+ u16 vol[2]; /* current volume & mute*/
+ u16 status; /* update flag */
+ u16 next; /* next link */
+};
+
+/* PCM callbacks */
+struct hda_pcm_ops {
+ int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
+ snd_pcm_substream_t *substream);
+ int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
+ snd_pcm_substream_t *substream);
+ int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
+ unsigned int stream_tag, unsigned int format,
+ snd_pcm_substream_t *substream);
+ int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
+ snd_pcm_substream_t *substream);
+};
+
+/* PCM information for each substream */
+struct hda_pcm_stream {
+ unsigned int substreams; /* number of substreams, 0 = not exist */
+ unsigned int channels_min; /* min. number of channels */
+ unsigned int channels_max; /* max. number of channels */
+ hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */
+ u32 rates; /* supported rates */
+ u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */
+ unsigned int maxbps; /* supported max. bit per sample */
+ struct hda_pcm_ops ops;
+};
+
+/* for PCM creation */
+struct hda_pcm {
+ char *name;
+ struct hda_pcm_stream stream[2];
+};
+
+/* codec information */
+struct hda_codec {
+ struct hda_bus *bus;
+ unsigned int addr; /* codec addr*/
+ struct list_head list; /* list point */
+
+ hda_nid_t afg; /* AFG node id */
+
+ /* ids */
+ u32 vendor_id;
+ u32 subsystem_id;
+ u32 revision_id;
+
+ /* detected preset */
+ const struct hda_codec_preset *preset;
+
+ /* set by patch */
+ struct hda_codec_ops patch_ops;
+
+ /* resume phase - all controls should update even if
+ * the values are not changed
+ */
+ unsigned int in_resume;
+
+ /* PCM to create, set by patch_ops.build_pcms callback */
+ unsigned int num_pcms;
+ struct hda_pcm *pcm_info;
+
+ /* codec specific info */
+ void *spec;
+
+ /* hash for amp access */
+ u16 amp_hash[32];
+ int num_amp_entries;
+ struct hda_amp_info amp_info[128]; /* big enough? */
+
+ struct semaphore spdif_mutex;
+ unsigned int spdif_status; /* IEC958 status bits */
+ unsigned short spdif_ctls; /* SPDIF control bits */
+ unsigned int spdif_in_enable; /* SPDIF input enable? */
+};
+
+/* direction */
+enum {
+ HDA_INPUT, HDA_OUTPUT
+};
+
+
+/*
+ * constructors
+ */
+int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp,
+ struct hda_bus **busp);
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+ struct hda_codec **codecp);
+
+/*
+ * low level functions
+ */
+unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int parm);
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int parm);
+#define snd_hda_param_read(codec, nid, param) snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
+int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id);
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns);
+
+struct hda_verb {
+ hda_nid_t nid;
+ u32 verb;
+ u32 param;
+};
+
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq);
+
+/* unsolicited event */
+int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
+
+/*
+ * Mixer
+ */
+int snd_hda_build_controls(struct hda_bus *bus);
+
+/*
+ * PCM
+ */
+int snd_hda_build_pcms(struct hda_bus *bus);
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
+ int channel_id, int format);
+unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels,
+ unsigned int format, unsigned int maxbps);
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+ u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
+int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int format);
+
+/*
+ * Misc
+ */
+void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+
+/*
+ * power management
+ */
+#ifdef CONFIG_PM
+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
+int snd_hda_resume(struct hda_bus *bus);
+#endif
+
+#endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
new file mode 100644
index 00000000000..69f7b6c4cf8
--- /dev/null
+++ b/sound/pci/hda/hda_generic.c
@@ -0,0 +1,906 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Generic widget tree parser
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/* widget node for parsing */
+struct hda_gnode {
+ hda_nid_t nid; /* NID of this widget */
+ unsigned short nconns; /* number of input connections */
+ hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; /* input connections */
+ unsigned int wid_caps; /* widget capabilities */
+ unsigned char type; /* widget type */
+ unsigned char pin_ctl; /* pin controls */
+ unsigned char checked; /* the flag indicates that the node is already parsed */
+ unsigned int pin_caps; /* pin widget capabilities */
+ unsigned int def_cfg; /* default configuration */
+ unsigned int amp_out_caps; /* AMP out capabilities */
+ unsigned int amp_in_caps; /* AMP in capabilities */
+ struct list_head list;
+};
+
+/* pathc-specific record */
+struct hda_gspec {
+ struct hda_gnode *dac_node; /* DAC node */
+ struct hda_gnode *out_pin_node; /* Output pin (Line-Out) node */
+ struct hda_gnode *pcm_vol_node; /* Node for PCM volume */
+ unsigned int pcm_vol_index; /* connection of PCM volume */
+
+ struct hda_gnode *adc_node; /* ADC node */
+ struct hda_gnode *cap_vol_node; /* Node for capture volume */
+ unsigned int cur_cap_src; /* current capture source */
+ struct hda_input_mux input_mux;
+ char cap_labels[HDA_MAX_NUM_INPUTS][16];
+
+ unsigned int def_amp_in_caps;
+ unsigned int def_amp_out_caps;
+
+ struct hda_pcm pcm_rec; /* PCM information */
+
+ struct list_head nid_list; /* list of widgets */
+};
+
+/*
+ * retrieve the default device type from the default config value
+ */
+#define get_defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
+#define get_defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+
+/*
+ * destructor
+ */
+static void snd_hda_generic_free(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ struct list_head *p, *n;
+
+ if (! spec)
+ return;
+ /* free all widgets */
+ list_for_each_safe(p, n, &spec->nid_list) {
+ struct hda_gnode *node = list_entry(p, struct hda_gnode, list);
+ kfree(node);
+ }
+ kfree(spec);
+}
+
+
+/*
+ * add a new widget node and read its attributes
+ */
+static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
+{
+ struct hda_gnode *node;
+ int nconns;
+
+ node = kcalloc(1, sizeof(*node), GFP_KERNEL);
+ if (node == NULL)
+ return -ENOMEM;
+ node->nid = nid;
+ nconns = snd_hda_get_connections(codec, nid, node->conn_list, HDA_MAX_CONNECTIONS);
+ if (nconns < 0) {
+ kfree(node);
+ return nconns;
+ }
+ node->nconns = nconns;
+ node->wid_caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+
+ if (node->type == AC_WID_PIN) {
+ node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP);
+ node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ }
+
+ if (node->wid_caps & AC_WCAP_OUT_AMP) {
+ if (node->wid_caps & AC_WCAP_AMP_OVRD)
+ node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
+ if (! node->amp_out_caps)
+ node->amp_out_caps = spec->def_amp_out_caps;
+ }
+ if (node->wid_caps & AC_WCAP_IN_AMP) {
+ if (node->wid_caps & AC_WCAP_AMP_OVRD)
+ node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
+ if (! node->amp_in_caps)
+ node->amp_in_caps = spec->def_amp_in_caps;
+ }
+ list_add_tail(&node->list, &spec->nid_list);
+ return 0;
+}
+
+/*
+ * build the AFG subtree
+ */
+static int build_afg_tree(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ int i, nodes, err;
+ hda_nid_t nid;
+
+ snd_assert(spec, return -EINVAL);
+
+ spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
+ spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
+
+ nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+ if (! nid || nodes < 0) {
+ printk(KERN_ERR "Invalid AFG subtree\n");
+ return -EINVAL;
+ }
+
+ /* parse all nodes belonging to the AFG */
+ for (i = 0; i < nodes; i++, nid++) {
+ if ((err = add_new_node(codec, spec, nid)) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+
+/*
+ * look for the node record for the given NID
+ */
+/* FIXME: should avoid the braindead linear search */
+static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
+{
+ struct list_head *p;
+ struct hda_gnode *node;
+
+ list_for_each(p, &spec->nid_list) {
+ node = list_entry(p, struct hda_gnode, list);
+ if (node->nid == nid)
+ return node;
+ }
+ return NULL;
+}
+
+/*
+ * unmute (and set max vol) the output amplifier
+ */
+static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
+{
+ unsigned int val, ofs;
+ snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
+ val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ if (val >= ofs)
+ val -= ofs;
+ val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+ val |= AC_AMP_SET_OUTPUT;
+ return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+/*
+ * unmute (and set max vol) the input amplifier
+ */
+static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
+{
+ unsigned int val, ofs;
+ snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
+ val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ if (val >= ofs)
+ val -= ofs;
+ val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+ val |= AC_AMP_SET_INPUT;
+ // awk added - fixed to allow unmuting of indexed amps
+ val |= index << AC_AMP_SET_INDEX_SHIFT;
+ return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+/*
+ * select the input connection of the given node.
+ */
+static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
+ unsigned int index)
+{
+ snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
+ return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_CONNECT_SEL, index);
+}
+
+/*
+ * clear checked flag of each node in the node list
+ */
+static void clear_check_flags(struct hda_gspec *spec)
+{
+ struct list_head *p;
+ struct hda_gnode *node;
+
+ list_for_each(p, &spec->nid_list) {
+ node = list_entry(p, struct hda_gnode, list);
+ node->checked = 0;
+ }
+}
+
+/*
+ * parse the output path recursively until reach to an audio output widget
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
+ struct hda_gnode *node)
+{
+ int i, err;
+ struct hda_gnode *child;
+
+ if (node->checked)
+ return 0;
+
+ node->checked = 1;
+ if (node->type == AC_WID_AUD_OUT) {
+ if (node->wid_caps & AC_WCAP_DIGITAL) {
+ snd_printdd("Skip Digital OUT node %x\n", node->nid);
+ return 0;
+ }
+ snd_printdd("AUD_OUT found %x\n", node->nid);
+ if (spec->dac_node) {
+ /* already DAC node is assigned, just unmute & connect */
+ return node == spec->dac_node;
+ }
+ spec->dac_node = node;
+ if (node->wid_caps & AC_WCAP_OUT_AMP) {
+ spec->pcm_vol_node = node;
+ spec->pcm_vol_index = 0;
+ }
+ return 1; /* found */
+ }
+
+ for (i = 0; i < node->nconns; i++) {
+ child = hda_get_node(spec, node->conn_list[i]);
+ if (! child)
+ continue;
+ err = parse_output_path(codec, spec, child);
+ if (err < 0)
+ return err;
+ else if (err > 0) {
+ /* found one,
+ * select the path, unmute both input and output
+ */
+ if (node->nconns > 1)
+ select_input_connection(codec, node, i);
+ unmute_input(codec, node, i);
+ unmute_output(codec, node);
+ if (! spec->pcm_vol_node) {
+ if (node->wid_caps & AC_WCAP_IN_AMP) {
+ spec->pcm_vol_node = node;
+ spec->pcm_vol_index = i;
+ } else if (node->wid_caps & AC_WCAP_OUT_AMP) {
+ spec->pcm_vol_node = node;
+ spec->pcm_vol_index = 0;
+ }
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Look for the output PIN widget with the given jack type
+ * and parse the output path to that PIN.
+ *
+ * Returns the PIN node when the path to DAC is established.
+ */
+static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
+ struct hda_gspec *spec,
+ int jack_type)
+{
+ struct list_head *p;
+ struct hda_gnode *node;
+ int err;
+
+ list_for_each(p, &spec->nid_list) {
+ node = list_entry(p, struct hda_gnode, list);
+ if (node->type != AC_WID_PIN)
+ continue;
+ /* output capable? */
+ if (! (node->pin_caps & AC_PINCAP_OUT))
+ continue;
+ if (jack_type >= 0) {
+ if (jack_type != get_defcfg_type(node))
+ continue;
+ if (node->wid_caps & AC_WCAP_DIGITAL)
+ continue; /* skip SPDIF */
+ } else {
+ /* output as default? */
+ if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
+ continue;
+ }
+ clear_check_flags(spec);
+ err = parse_output_path(codec, spec, node);
+ if (err < 0)
+ return NULL;
+ else if (err > 0) {
+ /* unmute the PIN output */
+ unmute_output(codec, node);
+ /* set PIN-Out enable */
+ snd_hda_codec_write(codec, node->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ return node;
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * parse outputs
+ */
+static int parse_output(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *node;
+
+ /*
+ * Look for the output PIN widget
+ */
+ /* first, look for the line-out pin */
+ node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
+ if (node) /* found, remember the PIN node */
+ spec->out_pin_node = node;
+ /* look for the HP-out pin */
+ node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
+ if (node) {
+ if (! spec->out_pin_node)
+ spec->out_pin_node = node;
+ }
+
+ if (! spec->out_pin_node) {
+ /* no line-out or HP pins found,
+ * then choose for the first output pin
+ */
+ spec->out_pin_node = parse_output_jack(codec, spec, -1);
+ if (! spec->out_pin_node)
+ snd_printd("hda_generic: no proper output path found\n");
+ }
+
+ return 0;
+}
+
+/*
+ * input MUX
+ */
+
+/* control callbacks */
+static int capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gspec *spec = codec->spec;
+ return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gspec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
+ return 0;
+}
+
+static int capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gspec *spec = codec->spec;
+ return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+ spec->adc_node->nid, &spec->cur_cap_src);
+}
+
+/*
+ * return the string name of the given input PIN widget
+ */
+static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
+{
+ unsigned int location = get_defcfg_location(node);
+ switch (get_defcfg_type(node)) {
+ case AC_JACK_LINE_IN:
+ if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+ return "Front Line";
+ return "Line";
+ case AC_JACK_CD:
+ if (pinctl)
+ *pinctl |= AC_PIN_VREF_GRD;
+ return "CD";
+ case AC_JACK_AUX:
+ if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+ return "Front Aux";
+ return "Aux";
+ case AC_JACK_MIC_IN:
+ if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+ return "Front Mic";
+ return "Mic";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital";
+ }
+ return NULL;
+}
+
+/*
+ * parse the nodes recursively until reach to the input PIN
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
+ struct hda_gnode *node)
+{
+ int i, err;
+ unsigned int pinctl;
+ char *label;
+ const char *type;
+
+ if (node->checked)
+ return 0;
+
+ node->checked = 1;
+ if (node->type != AC_WID_PIN) {
+ for (i = 0; i < node->nconns; i++) {
+ struct hda_gnode *child;
+ child = hda_get_node(spec, node->conn_list[i]);
+ if (! child)
+ continue;
+ err = parse_adc_sub_nodes(codec, spec, child);
+ if (err < 0)
+ return err;
+ if (err > 0) {
+ /* found one,
+ * select the path, unmute both input and output
+ */
+ if (node->nconns > 1)
+ select_input_connection(codec, node, i);
+ unmute_input(codec, node, i);
+ unmute_output(codec, node);
+ return err;
+ }
+ }
+ return 0;
+ }
+
+ /* input capable? */
+ if (! (node->pin_caps & AC_PINCAP_IN))
+ return 0;
+
+ if (node->wid_caps & AC_WCAP_DIGITAL)
+ return 0; /* skip SPDIF */
+
+ if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
+ snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
+ return -EINVAL;
+ }
+
+ pinctl = AC_PINCTL_IN_EN;
+ /* create a proper capture source label */
+ type = get_input_type(node, &pinctl);
+ if (! type) {
+ /* input as default? */
+ if (! (node->pin_ctl & AC_PINCTL_IN_EN))
+ return 0;
+ type = "Input";
+ }
+ label = spec->cap_labels[spec->input_mux.num_items];
+ strcpy(label, type);
+ spec->input_mux.items[spec->input_mux.num_items].label = label;
+
+ /* unmute the PIN external input */
+ unmute_input(codec, node, 0); /* index = 0? */
+ /* set PIN-In enable */
+ snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+
+ return 1; /* found */
+}
+
+/*
+ * parse input
+ */
+static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
+{
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *node;
+ int i, err;
+
+ snd_printdd("AUD_IN = %x\n", adc_node->nid);
+ clear_check_flags(spec);
+
+ // awk added - fixed no recording due to muted widget
+ unmute_input(codec, adc_node, 0);
+
+ /*
+ * check each connection of the ADC
+ * if it reaches to a proper input PIN, add the path as the
+ * input path.
+ */
+ for (i = 0; i < adc_node->nconns; i++) {
+ node = hda_get_node(spec, adc_node->conn_list[i]);
+ if (! node)
+ continue;
+ err = parse_adc_sub_nodes(codec, spec, node);
+ if (err < 0)
+ return err;
+ else if (err > 0) {
+ struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items];
+ char *buf = spec->cap_labels[spec->input_mux.num_items];
+ int ocap;
+ for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) {
+ if (! strcmp(buf, spec->cap_labels[ocap])) {
+ /* same label already exists,
+ * put the index number to be unique
+ */
+ sprintf(buf, "%s %d", spec->cap_labels[ocap],
+ spec->input_mux.num_items);
+ }
+ }
+ csrc->index = i;
+ spec->input_mux.num_items++;
+ }
+ }
+
+ if (! spec->input_mux.num_items)
+ return 0; /* no input path found... */
+
+ snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
+ for (i = 0; i < spec->input_mux.num_items; i++)
+ snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
+ spec->input_mux.items[i].index);
+
+ spec->adc_node = adc_node;
+ return 1;
+}
+
+/*
+ * parse input
+ */
+static int parse_input(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ struct list_head *p;
+ struct hda_gnode *node;
+ int err;
+
+ /*
+ * At first we look for an audio input widget.
+ * If it reaches to certain input PINs, we take it as the
+ * input path.
+ */
+ list_for_each(p, &spec->nid_list) {
+ node = list_entry(p, struct hda_gnode, list);
+ if (node->wid_caps & AC_WCAP_DIGITAL)
+ continue; /* skip SPDIF */
+ if (node->type == AC_WID_AUD_IN) {
+ err = parse_input_path(codec, node);
+ if (err < 0)
+ return err;
+ else if (err > 0)
+ return 0;
+ }
+ }
+ snd_printd("hda_generic: no proper input path found\n");
+ return 0;
+}
+
+/*
+ * create mixer controls if possible
+ */
+#define DIR_OUT 0x1
+#define DIR_IN 0x2
+
+static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
+ unsigned int index, const char *type, const char *dir_sfx)
+{
+ char name[32];
+ int err;
+ int created = 0;
+ snd_kcontrol_new_t knew;
+
+ if (type)
+ sprintf(name, "%s %s Switch", type, dir_sfx);
+ else
+ sprintf(name, "%s Switch", dir_sfx);
+ if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+ (node->amp_in_caps & AC_AMPCAP_MUTE)) {
+ knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
+ snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ return err;
+ created = 1;
+ } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+ (node->amp_out_caps & AC_AMPCAP_MUTE)) {
+ knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
+ snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ return err;
+ created = 1;
+ }
+
+ if (type)
+ sprintf(name, "%s %s Volume", type, dir_sfx);
+ else
+ sprintf(name, "%s Volume", dir_sfx);
+ if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+ (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
+ knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
+ snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ return err;
+ created = 1;
+ } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+ (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
+ knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
+ snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ return err;
+ created = 1;
+ }
+
+ return created;
+}
+
+/*
+ * check whether the controls with the given name and direction suffix already exist
+ */
+static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
+{
+ snd_ctl_elem_id_t id;
+ memset(&id, 0, sizeof(id));
+ sprintf(id.name, "%s %s Volume", type, dir);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ if (snd_ctl_find_id(codec->bus->card, &id))
+ return 1;
+ sprintf(id.name, "%s %s Switch", type, dir);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ if (snd_ctl_find_id(codec->bus->card, &id))
+ return 1;
+ return 0;
+}
+
+/*
+ * build output mixer controls
+ */
+static int build_output_controls(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ int err;
+
+ err = create_mixer(codec, spec->pcm_vol_node, spec->pcm_vol_index,
+ "PCM", "Playback");
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* create capture volume/switch */
+static int build_input_controls(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *adc_node = spec->adc_node;
+ int err;
+
+ if (! adc_node)
+ return 0; /* not found */
+
+ /* create capture volume and switch controls if the ADC has an amp */
+ err = create_mixer(codec, adc_node, 0, NULL, "Capture");
+
+ /* create input MUX if multiple sources are available */
+ if (spec->input_mux.num_items > 1) {
+ static snd_kcontrol_new_t cap_sel = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = capture_source_info,
+ .get = capture_source_get,
+ .put = capture_source_put,
+ };
+ if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0)
+ return err;
+ spec->cur_cap_src = 0;
+ select_input_connection(codec, adc_node, spec->input_mux.items[0].index);
+ }
+ return 0;
+}
+
+
+/*
+ * parse the nodes recursively until reach to the output PIN.
+ *
+ * returns 0 - if not found,
+ * 1 - if found, but no mixer is created
+ * 2 - if found and mixer was already created, (just skip)
+ * a negative error code
+ */
+static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
+ struct hda_gnode *node, struct hda_gnode *dest_node,
+ const char *type)
+{
+ int i, err;
+
+ if (node->checked)
+ return 0;
+
+ node->checked = 1;
+ if (node == dest_node) {
+ /* loopback connection found */
+ return 1;
+ }
+
+ for (i = 0; i < node->nconns; i++) {
+ struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
+ if (! child)
+ continue;
+ err = parse_loopback_path(codec, spec, child, dest_node, type);
+ if (err < 0)
+ return err;
+ else if (err >= 1) {
+ if (err == 1) {
+ err = create_mixer(codec, node, i, type, "Playback");
+ if (err < 0)
+ return err;
+ if (err > 0)
+ return 2; /* ok, created */
+ /* not created, maybe in the lower path */
+ err = 1;
+ }
+ /* connect and unmute */
+ if (node->nconns > 1)
+ select_input_connection(codec, node, i);
+ unmute_input(codec, node, i);
+ unmute_output(codec, node);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * parse the tree and build the loopback controls
+ */
+static int build_loopback_controls(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ struct list_head *p;
+ struct hda_gnode *node;
+ int err;
+ const char *type;
+
+ if (! spec->out_pin_node)
+ return 0;
+
+ list_for_each(p, &spec->nid_list) {
+ node = list_entry(p, struct hda_gnode, list);
+ if (node->type != AC_WID_PIN)
+ continue;
+ /* input capable? */
+ if (! (node->pin_caps & AC_PINCAP_IN))
+ return 0;
+ type = get_input_type(node, NULL);
+ if (type) {
+ if (check_existing_control(codec, type, "Playback"))
+ continue;
+ clear_check_flags(spec);
+ err = parse_loopback_path(codec, spec, spec->out_pin_node,
+ node, type);
+ if (err < 0)
+ return err;
+ if (! err)
+ continue;
+ }
+ }
+ return 0;
+}
+
+/*
+ * build mixer controls
+ */
+static int build_generic_controls(struct hda_codec *codec)
+{
+ int err;
+
+ if ((err = build_input_controls(codec)) < 0 ||
+ (err = build_output_controls(codec)) < 0 ||
+ (err = build_loopback_controls(codec)) < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * PCM
+ */
+static struct hda_pcm_stream generic_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int build_generic_pcms(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ struct hda_pcm *info = &spec->pcm_rec;
+
+ if (! spec->dac_node && ! spec->adc_node) {
+ snd_printd("hda_generic: no PCM found\n");
+ return 0;
+ }
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "HDA Generic";
+ if (spec->dac_node) {
+ info->stream[0] = generic_pcm_playback;
+ info->stream[0].nid = spec->dac_node->nid;
+ }
+ if (spec->adc_node) {
+ info->stream[1] = generic_pcm_playback;
+ info->stream[1].nid = spec->adc_node->nid;
+ }
+
+ return 0;
+}
+
+
+/*
+ */
+static struct hda_codec_ops generic_patch_ops = {
+ .build_controls = build_generic_controls,
+ .build_pcms = build_generic_pcms,
+ .free = snd_hda_generic_free,
+};
+
+/*
+ * the generic parser
+ */
+int snd_hda_parse_generic_codec(struct hda_codec *codec)
+{
+ struct hda_gspec *spec;
+ int err;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL) {
+ printk(KERN_ERR "hda_generic: can't allocate spec\n");
+ return -ENOMEM;
+ }
+ codec->spec = spec;
+ INIT_LIST_HEAD(&spec->nid_list);
+
+ if ((err = build_afg_tree(codec)) < 0)
+ goto error;
+
+ if ((err = parse_input(codec)) < 0 ||
+ (err = parse_output(codec)) < 0)
+ goto error;
+
+ codec->patch_ops = generic_patch_ops;
+
+ return 0;
+
+ error:
+ snd_hda_generic_free(codec);
+ return err;
+}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
new file mode 100644
index 00000000000..d89647a3d44
--- /dev/null
+++ b/sound/pci/hda/hda_intel.c
@@ -0,0 +1,1449 @@
+/*
+ *
+ * hda_intel.c - Implementation of primary alsa driver code base for Intel HD Audio.
+ *
+ * Copyright(c) 2004 Intel Corporation. All rights reserved.
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ * PeiSen Hou <pshou@realtek.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * CONTACTS:
+ *
+ * Matt Jared matt.jared@intel.com
+ * Andy Kopp andy.kopp@intel.com
+ * Dan Kogan dan.d.kogan@intel.com
+ *
+ * CHANGES:
+ *
+ * 2004.12.01 Major rewrite by tiwai, merged the work of pshou
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "hda_codec.h"
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static char *model[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
+ "{Intel, ICH6M},"
+ "{Intel, ICH7}}");
+MODULE_DESCRIPTION("Intel HDA driver");
+
+#define SFX "hda-intel: "
+
+/*
+ * registers
+ */
+#define ICH6_REG_GCAP 0x00
+#define ICH6_REG_VMIN 0x02
+#define ICH6_REG_VMAJ 0x03
+#define ICH6_REG_OUTPAY 0x04
+#define ICH6_REG_INPAY 0x06
+#define ICH6_REG_GCTL 0x08
+#define ICH6_REG_WAKEEN 0x0c
+#define ICH6_REG_STATESTS 0x0e
+#define ICH6_REG_GSTS 0x10
+#define ICH6_REG_INTCTL 0x20
+#define ICH6_REG_INTSTS 0x24
+#define ICH6_REG_WALCLK 0x30
+#define ICH6_REG_SYNC 0x34
+#define ICH6_REG_CORBLBASE 0x40
+#define ICH6_REG_CORBUBASE 0x44
+#define ICH6_REG_CORBWP 0x48
+#define ICH6_REG_CORBRP 0x4A
+#define ICH6_REG_CORBCTL 0x4c
+#define ICH6_REG_CORBSTS 0x4d
+#define ICH6_REG_CORBSIZE 0x4e
+
+#define ICH6_REG_RIRBLBASE 0x50
+#define ICH6_REG_RIRBUBASE 0x54
+#define ICH6_REG_RIRBWP 0x58
+#define ICH6_REG_RINTCNT 0x5a
+#define ICH6_REG_RIRBCTL 0x5c
+#define ICH6_REG_RIRBSTS 0x5d
+#define ICH6_REG_RIRBSIZE 0x5e
+
+#define ICH6_REG_IC 0x60
+#define ICH6_REG_IR 0x64
+#define ICH6_REG_IRS 0x68
+#define ICH6_IRS_VALID (1<<1)
+#define ICH6_IRS_BUSY (1<<0)
+
+#define ICH6_REG_DPLBASE 0x70
+#define ICH6_REG_DPUBASE 0x74
+#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */
+
+/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
+
+/* stream register offsets from stream base */
+#define ICH6_REG_SD_CTL 0x00
+#define ICH6_REG_SD_STS 0x03
+#define ICH6_REG_SD_LPIB 0x04
+#define ICH6_REG_SD_CBL 0x08
+#define ICH6_REG_SD_LVI 0x0c
+#define ICH6_REG_SD_FIFOW 0x0e
+#define ICH6_REG_SD_FIFOSIZE 0x10
+#define ICH6_REG_SD_FORMAT 0x12
+#define ICH6_REG_SD_BDLPL 0x18
+#define ICH6_REG_SD_BDLPU 0x1c
+
+/* PCI space */
+#define ICH6_PCIREG_TCSEL 0x44
+
+/*
+ * other constants
+ */
+
+/* max number of SDs */
+#define MAX_ICH6_DEV 8
+/* max number of fragments - we may use more if allocating more pages for BDL */
+#define AZX_MAX_FRAG (PAGE_SIZE / (MAX_ICH6_DEV * 16))
+/* max buffer size - no h/w limit, you can increase as you like */
+#define AZX_MAX_BUF_SIZE (1024*1024*1024)
+/* max number of PCM devics per card */
+#define AZX_MAX_PCMS 8
+
+/* RIRB int mask: overrun[2], response[0] */
+#define RIRB_INT_RESPONSE 0x01
+#define RIRB_INT_OVERRUN 0x04
+#define RIRB_INT_MASK 0x05
+
+/* STATESTS int mask: SD2,SD1,SD0 */
+#define STATESTS_INT_MASK 0x07
+#define AZX_MAX_CODECS 3
+
+/* SD_CTL bits */
+#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
+#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
+#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
+#define SD_CTL_STREAM_TAG_SHIFT 20
+
+/* SD_CTL and SD_STS */
+#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
+#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
+#define SD_INT_COMPLETE 0x04 /* completion interrupt */
+#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|SD_INT_COMPLETE)
+
+/* SD_STS */
+#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
+
+/* INTCTL and INTSTS */
+#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
+#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
+#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
+
+/* GCTL reset bit */
+#define ICH6_GCTL_RESET (1<<0)
+
+/* CORB/RIRB control, read/write pointer */
+#define ICH6_RBCTL_DMA_EN 0x02 /* enable DMA */
+#define ICH6_RBCTL_IRQ_EN 0x01 /* enable IRQ */
+#define ICH6_RBRWP_CLR 0x8000 /* read/write pointer clear */
+/* below are so far hardcoded - should read registers in future */
+#define ICH6_MAX_CORB_ENTRIES 256
+#define ICH6_MAX_RIRB_ENTRIES 256
+
+
+/*
+ * Use CORB/RIRB for communication from/to codecs.
+ * This is the way recommended by Intel (see below).
+ */
+#define USE_CORB_RIRB
+
+/*
+ * Define this if use the position buffer instead of reading SD_LPIB
+ * It's not used as default since SD_LPIB seems to give more accurate position
+ */
+/* #define USE_POSBUF */
+
+/*
+ */
+
+typedef struct snd_azx azx_t;
+typedef struct snd_azx_rb azx_rb_t;
+typedef struct snd_azx_dev azx_dev_t;
+
+struct snd_azx_dev {
+ u32 *bdl; /* virtual address of the BDL */
+ dma_addr_t bdl_addr; /* physical address of the BDL */
+ volatile u32 *posbuf; /* position buffer pointer */
+
+ unsigned int bufsize; /* size of the play buffer in bytes */
+ unsigned int fragsize; /* size of each period in bytes */
+ unsigned int frags; /* number for period in the play buffer */
+ unsigned int fifo_size; /* FIFO size */
+
+ void __iomem *sd_addr; /* stream descriptor pointer */
+
+ u32 sd_int_sta_mask; /* stream int status mask */
+
+ /* pcm support */
+ snd_pcm_substream_t *substream; /* assigned substream, set in PCM open */
+ unsigned int format_val; /* format value to be set in the controller and the codec */
+ unsigned char stream_tag; /* assigned stream */
+ unsigned char index; /* stream index */
+
+ unsigned int opened: 1;
+ unsigned int running: 1;
+};
+
+/* CORB/RIRB */
+struct snd_azx_rb {
+ u32 *buf; /* CORB/RIRB buffer
+ * Each CORB entry is 4byte, RIRB is 8byte
+ */
+ dma_addr_t addr; /* physical address of CORB/RIRB buffer */
+ /* for RIRB */
+ unsigned short rp, wp; /* read/write pointers */
+ int cmds; /* number of pending requests */
+ u32 res; /* last read value */
+};
+
+struct snd_azx {
+ snd_card_t *card;
+ struct pci_dev *pci;
+
+ /* pci resources */
+ unsigned long addr;
+ void __iomem *remap_addr;
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+ struct semaphore open_mutex;
+
+ /* streams */
+ azx_dev_t azx_dev[MAX_ICH6_DEV];
+
+ /* PCM */
+ unsigned int pcm_devs;
+ snd_pcm_t *pcm[AZX_MAX_PCMS];
+
+ /* HD codec */
+ unsigned short codec_mask;
+ struct hda_bus *bus;
+
+ /* CORB/RIRB */
+ azx_rb_t corb;
+ azx_rb_t rirb;
+
+ /* BDL, CORB/RIRB and position buffers */
+ struct snd_dma_buffer bdl;
+ struct snd_dma_buffer rb;
+ struct snd_dma_buffer posbuf;
+};
+
+/*
+ * macros for easy use
+ */
+#define azx_writel(chip,reg,value) \
+ writel(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readl(chip,reg) \
+ readl((chip)->remap_addr + ICH6_REG_##reg)
+#define azx_writew(chip,reg,value) \
+ writew(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readw(chip,reg) \
+ readw((chip)->remap_addr + ICH6_REG_##reg)
+#define azx_writeb(chip,reg,value) \
+ writeb(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readb(chip,reg) \
+ readb((chip)->remap_addr + ICH6_REG_##reg)
+
+#define azx_sd_writel(dev,reg,value) \
+ writel(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readl(dev,reg) \
+ readl((dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_writew(dev,reg,value) \
+ writew(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readw(dev,reg) \
+ readw((dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_writeb(dev,reg,value) \
+ writeb(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readb(dev,reg) \
+ readb((dev)->sd_addr + ICH6_REG_##reg)
+
+/* for pcm support */
+#define get_azx_dev(substream) (azx_dev_t*)(substream->runtime->private_data)
+
+/* Get the upper 32bit of the given dma_addr_t
+ * Compiler should optimize and eliminate the code if dma_addr_t is 32bit
+ */
+#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0)
+
+
+/*
+ * Interface for HD codec
+ */
+
+#ifdef USE_CORB_RIRB
+/*
+ * CORB / RIRB interface
+ */
+static int azx_alloc_cmd_io(azx_t *chip)
+{
+ int err;
+
+ /* single page (at least 4096 bytes) must suffice for both ringbuffes */
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->rb);
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
+ return err;
+ }
+ return 0;
+}
+
+static void azx_init_cmd_io(azx_t *chip)
+{
+ /* CORB set up */
+ chip->corb.addr = chip->rb.addr;
+ chip->corb.buf = (u32 *)chip->rb.area;
+ azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
+ azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr));
+
+ /* set the corb write pointer to 0 */
+ azx_writew(chip, CORBWP, 0);
+ /* reset the corb hw read pointer */
+ azx_writew(chip, CORBRP, ICH6_RBRWP_CLR);
+ /* enable corb dma */
+ azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);
+
+ /* RIRB set up */
+ chip->rirb.addr = chip->rb.addr + 2048;
+ chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+ azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
+ azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr));
+
+ /* reset the rirb hw write pointer */
+ azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);
+ /* set N=1, get RIRB response interrupt for new entry */
+ azx_writew(chip, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+#ifdef USE_CORB_RIRB
+ azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
+#else
+ azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN);
+#endif
+ chip->rirb.rp = chip->rirb.cmds = 0;
+}
+
+static void azx_free_cmd_io(azx_t *chip)
+{
+ /* disable ringbuffer DMAs */
+ azx_writeb(chip, RIRBCTL, 0);
+ azx_writeb(chip, CORBCTL, 0);
+}
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int para)
+{
+ azx_t *chip = codec->bus->private_data;
+ unsigned int wp;
+ u32 val;
+
+ val = (u32)(codec->addr & 0x0f) << 28;
+ val |= (u32)direct << 27;
+ val |= (u32)nid << 20;
+ val |= verb << 8;
+ val |= para;
+
+ /* add command to corb */
+ wp = azx_readb(chip, CORBWP);
+ wp++;
+ wp %= ICH6_MAX_CORB_ENTRIES;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->rirb.cmds++;
+ chip->corb.buf[wp] = cpu_to_le32(val);
+ azx_writel(chip, CORBWP, wp);
+ spin_unlock_irq(&chip->reg_lock);
+
+ return 0;
+}
+
+#define ICH6_RIRB_EX_UNSOL_EV (1<<4)
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void azx_update_rirb(azx_t *chip)
+{
+ unsigned int rp, wp;
+ u32 res, res_ex;
+
+ wp = azx_readb(chip, RIRBWP);
+ if (wp == chip->rirb.wp)
+ return;
+ chip->rirb.wp = wp;
+
+ while (chip->rirb.rp != wp) {
+ chip->rirb.rp++;
+ chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
+
+ rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+ res = le32_to_cpu(chip->rirb.buf[rp]);
+ if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
+ snd_hda_queue_unsol_event(chip->bus, res, res_ex);
+ else if (chip->rirb.cmds) {
+ chip->rirb.cmds--;
+ chip->rirb.res = res;
+ }
+ }
+}
+
+/* receive a response */
+static unsigned int azx_get_response(struct hda_codec *codec)
+{
+ azx_t *chip = codec->bus->private_data;
+ int timeout = 50;
+
+ while (chip->rirb.cmds) {
+ if (! --timeout) {
+ snd_printk(KERN_ERR "azx_get_response timeout\n");
+ chip->rirb.rp = azx_readb(chip, RIRBWP);
+ chip->rirb.cmds = 0;
+ return -1;
+ }
+ msleep(1);
+ }
+ return chip->rirb.res; /* the last value */
+}
+
+#else
+/*
+ * Use the single immediate command instead of CORB/RIRB for simplicity
+ *
+ * Note: according to Intel, this is not preferred use. The command was
+ * intended for the BIOS only, and may get confused with unsolicited
+ * responses. So, we shouldn't use it for normal operation from the
+ * driver.
+ * I left the codes, however, for debugging/testing purposes.
+ */
+
+#define azx_alloc_cmd_io(chip) 0
+#define azx_init_cmd_io(chip)
+#define azx_free_cmd_io(chip)
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int para)
+{
+ azx_t *chip = codec->bus->private_data;
+ u32 val;
+ int timeout = 50;
+
+ val = (u32)(codec->addr & 0x0f) << 28;
+ val |= (u32)direct << 27;
+ val |= (u32)nid << 20;
+ val |= verb << 8;
+ val |= para;
+
+ while (timeout--) {
+ /* check ICB busy bit */
+ if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
+ /* Clear IRV valid bit */
+ azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_VALID);
+ azx_writel(chip, IC, val);
+ azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY);
+ return 0;
+ }
+ udelay(1);
+ }
+ snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val);
+ return -EIO;
+}
+
+/* receive a response */
+static unsigned int azx_get_response(struct hda_codec *codec)
+{
+ azx_t *chip = codec->bus->private_data;
+ int timeout = 50;
+
+ while (timeout--) {
+ /* check IRV busy bit */
+ if (azx_readw(chip, IRS) & ICH6_IRS_VALID)
+ return azx_readl(chip, IR);
+ udelay(1);
+ }
+ snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS));
+ return (unsigned int)-1;
+}
+
+#define azx_update_rirb(chip)
+
+#endif /* USE_CORB_RIRB */
+
+/* reset codec link */
+static int azx_reset(azx_t *chip)
+{
+ int count;
+
+ /* reset controller */
+ azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
+
+ count = 50;
+ while (azx_readb(chip, GCTL) && --count)
+ msleep(1);
+
+ /* delay for >= 100us for codec PLL to settle per spec
+ * Rev 0.9 section 5.5.1
+ */
+ msleep(1);
+
+ /* Bring controller out of reset */
+ azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
+
+ count = 50;
+ while (! azx_readb(chip, GCTL) && --count)
+ msleep(1);
+
+ /* Brent Chartrand said to wait >= 540us for codecs to intialize */
+ msleep(1);
+
+ /* check to see if controller is ready */
+ if (! azx_readb(chip, GCTL)) {
+ snd_printd("azx_reset: controller not ready!\n");
+ return -EBUSY;
+ }
+
+ /* detect codecs */
+ if (! chip->codec_mask) {
+ chip->codec_mask = azx_readw(chip, STATESTS);
+ snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Lowlevel interface
+ */
+
+/* enable interrupts */
+static void azx_int_enable(azx_t *chip)
+{
+ /* enable controller CIE and GIE */
+ azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
+ ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(azx_t *chip)
+{
+ int i;
+
+ /* disable interrupts in stream descriptor */
+ for (i = 0; i < MAX_ICH6_DEV; i++) {
+ azx_dev_t *azx_dev = &chip->azx_dev[i];
+ azx_sd_writeb(azx_dev, SD_CTL,
+ azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);
+ }
+
+ /* disable SIE for all streams */
+ azx_writeb(chip, INTCTL, 0);
+
+ /* disable controller CIE and GIE */
+ azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
+ ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));
+}
+
+/* clear interrupts */
+static void azx_int_clear(azx_t *chip)
+{
+ int i;
+
+ /* clear stream status */
+ for (i = 0; i < MAX_ICH6_DEV; i++) {
+ azx_dev_t *azx_dev = &chip->azx_dev[i];
+ azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
+ }
+
+ /* clear STATESTS */
+ azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+
+ /* clear rirb status */
+ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+
+ /* clear int status */
+ azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
+}
+
+/* start a stream */
+static void azx_stream_start(azx_t *chip, azx_dev_t *azx_dev)
+{
+ /* enable SIE */
+ azx_writeb(chip, INTCTL,
+ azx_readb(chip, INTCTL) | (1 << azx_dev->index));
+ /* set DMA start and interrupt mask */
+ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
+ SD_CTL_DMA_START | SD_INT_MASK);
+}
+
+/* stop a stream */
+static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev)
+{
+ /* stop DMA */
+ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
+ ~(SD_CTL_DMA_START | SD_INT_MASK));
+ azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+ /* disable SIE */
+ azx_writeb(chip, INTCTL,
+ azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
+}
+
+
+/*
+ * initialize the chip
+ */
+static void azx_init_chip(azx_t *chip)
+{
+ unsigned char tcsel_reg;
+
+ /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+ * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+ * Ensuring these bits are 0 clears playback static on some HD Audio codecs
+ */
+ pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, &tcsel_reg);
+ pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, tcsel_reg & 0xf8);
+
+ /* reset controller */
+ azx_reset(chip);
+
+ /* initialize interrupts */
+ azx_int_clear(chip);
+ azx_int_enable(chip);
+
+ /* initialize the codec command I/O */
+ azx_init_cmd_io(chip);
+
+#ifdef USE_POSBUF
+ /* program the position buffer */
+ azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
+ azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+#endif
+}
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs)
+{
+ azx_t *chip = dev_id;
+ azx_dev_t *azx_dev;
+ u32 status;
+ int i;
+
+ spin_lock(&chip->reg_lock);
+
+ status = azx_readl(chip, INTSTS);
+ if (status == 0) {
+ spin_unlock(&chip->reg_lock);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < MAX_ICH6_DEV; i++) {
+ azx_dev = &chip->azx_dev[i];
+ if (status & azx_dev->sd_int_sta_mask) {
+ azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
+ if (azx_dev->substream && azx_dev->running) {
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(azx_dev->substream);
+ spin_lock(&chip->reg_lock);
+ }
+ }
+ }
+
+ /* clear rirb int */
+ status = azx_readb(chip, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ if (status & RIRB_INT_RESPONSE)
+ azx_update_rirb(chip);
+ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+ }
+
+#if 0
+ /* clear state status int */
+ if (azx_readb(chip, STATESTS) & 0x04)
+ azx_writeb(chip, STATESTS, 0x04);
+#endif
+ spin_unlock(&chip->reg_lock);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * set up BDL entries
+ */
+static void azx_setup_periods(azx_dev_t *azx_dev)
+{
+ u32 *bdl = azx_dev->bdl;
+ dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;
+ int idx;
+
+ /* reset BDL address */
+ azx_sd_writel(azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(azx_dev, SD_BDLPU, 0);
+
+ /* program the initial BDL entries */
+ for (idx = 0; idx < azx_dev->frags; idx++) {
+ unsigned int off = idx << 2; /* 4 dword step */
+ dma_addr_t addr = dma_addr + idx * azx_dev->fragsize;
+ /* program the address field of the BDL entry */
+ bdl[off] = cpu_to_le32((u32)addr);
+ bdl[off+1] = cpu_to_le32(upper_32bit(addr));
+
+ /* program the size field of the BDL entry */
+ bdl[off+2] = cpu_to_le32(azx_dev->fragsize);
+
+ /* program the IOC to enable interrupt when buffer completes */
+ bdl[off+3] = cpu_to_le32(0x01);
+ }
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev)
+{
+ unsigned char val;
+ int timeout;
+
+ /* make sure the run bit is zero for SD */
+ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START);
+ /* reset stream */
+ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET);
+ udelay(3);
+ timeout = 300;
+ while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
+ --timeout)
+ ;
+ val &= ~SD_CTL_STREAM_RESET;
+ azx_sd_writeb(azx_dev, SD_CTL, val);
+ udelay(3);
+
+ timeout = 300;
+ /* waiting for hardware to report that the stream is out of reset */
+ while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
+ --timeout)
+ ;
+
+ /* program the stream_tag */
+ azx_sd_writel(azx_dev, SD_CTL,
+ (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) |
+ (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));
+
+ /* program the length of samples in cyclic buffer */
+ azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+
+ /* program the stream format */
+ /* this value needs to be the same as the one programmed */
+ azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+
+ /* program the stream LVI (last valid index) of the BDL */
+ azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+
+ /* program the BDL address */
+ /* lower BDL address */
+ azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr);
+ /* upper BDL address */
+ azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
+
+#ifdef USE_POSBUF
+ /* enable the position buffer */
+ if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+ azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+#endif
+ /* set the interrupt enable bits in the descriptor control register */
+ azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
+
+ return 0;
+}
+
+
+/*
+ * Codec initialization
+ */
+
+static int __devinit azx_codec_create(azx_t *chip, const char *model)
+{
+ struct hda_bus_template bus_temp;
+ int c, codecs, err;
+
+ memset(&bus_temp, 0, sizeof(bus_temp));
+ bus_temp.private_data = chip;
+ bus_temp.modelname = model;
+ bus_temp.pci = chip->pci;
+ bus_temp.ops.command = azx_send_cmd;
+ bus_temp.ops.get_response = azx_get_response;
+
+ if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0)
+ return err;
+
+ codecs = 0;
+ for (c = 0; c < AZX_MAX_CODECS; c++) {
+ if (chip->codec_mask & (1 << c)) {
+ err = snd_hda_codec_new(chip->bus, c, NULL);
+ if (err < 0)
+ continue;
+ codecs++;
+ }
+ }
+ if (! codecs) {
+ snd_printk(KERN_ERR SFX "no codecs initialized\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+
+/*
+ * PCM support
+ */
+
+/* assign a stream for the PCM */
+static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream)
+{
+ int dev, i;
+ dev = stream == SNDRV_PCM_STREAM_PLAYBACK ? 4 : 0;
+ for (i = 0; i < 4; i++, dev++)
+ if (! chip->azx_dev[dev].opened) {
+ chip->azx_dev[dev].opened = 1;
+ return &chip->azx_dev[dev];
+ }
+ return NULL;
+}
+
+/* release the assigned stream */
+static inline void azx_release_device(azx_dev_t *azx_dev)
+{
+ azx_dev->opened = 0;
+}
+
+static snd_pcm_hardware_t azx_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
+struct azx_pcm {
+ azx_t *chip;
+ struct hda_codec *codec;
+ struct hda_pcm_stream *hinfo[2];
+};
+
+static int azx_pcm_open(snd_pcm_substream_t *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ azx_t *chip = apcm->chip;
+ azx_dev_t *azx_dev;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned long flags;
+ int err;
+
+ down(&chip->open_mutex);
+ azx_dev = azx_assign_device(chip, substream->stream);
+ if (azx_dev == NULL) {
+ up(&chip->open_mutex);
+ return -EBUSY;
+ }
+ runtime->hw = azx_pcm_hw;
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+ snd_pcm_limit_hw_rates(runtime);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) {
+ azx_release_device(azx_dev);
+ up(&chip->open_mutex);
+ return err;
+ }
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ azx_dev->substream = substream;
+ azx_dev->running = 0;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ runtime->private_data = azx_dev;
+ up(&chip->open_mutex);
+ return 0;
+}
+
+static int azx_pcm_close(snd_pcm_substream_t *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ azx_t *chip = apcm->chip;
+ azx_dev_t *azx_dev = get_azx_dev(substream);
+ unsigned long flags;
+
+ down(&chip->open_mutex);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ azx_dev->substream = NULL;
+ azx_dev->running = 0;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ azx_release_device(azx_dev);
+ hinfo->ops.close(hinfo, apcm->codec, substream);
+ up(&chip->open_mutex);
+ return 0;
+}
+
+static int azx_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int azx_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ azx_dev_t *azx_dev = get_azx_dev(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+
+ /* reset BDL address */
+ azx_sd_writel(azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(azx_dev, SD_BDLPU, 0);
+ azx_sd_writel(azx_dev, SD_CTL, 0);
+
+ hinfo->ops.cleanup(hinfo, apcm->codec, substream);
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int azx_pcm_prepare(snd_pcm_substream_t *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ azx_t *chip = apcm->chip;
+ azx_dev_t *azx_dev = get_azx_dev(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
+ azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);
+ azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize;
+ azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
+ runtime->channels,
+ runtime->format,
+ hinfo->maxbps);
+ if (! azx_dev->format_val) {
+ snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n",
+ runtime->rate, runtime->channels, runtime->format);
+ return -EINVAL;
+ }
+
+ snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n",
+ azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);
+ azx_setup_periods(azx_dev);
+ azx_setup_controller(chip, azx_dev);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
+ else
+ azx_dev->fifo_size = 0;
+
+ return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag,
+ azx_dev->format_val, substream);
+}
+
+static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ azx_dev_t *azx_dev = get_azx_dev(substream);
+ azx_t *chip = apcm->chip;
+ int err = 0;
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ azx_stream_start(chip, azx_dev);
+ azx_dev->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ azx_stream_stop(chip, azx_dev);
+ azx_dev->running = 0;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ spin_unlock(&chip->reg_lock);
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH ||
+ cmd == SNDRV_PCM_TRIGGER_STOP) {
+ int timeout = 5000;
+ while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout)
+ ;
+ }
+ return err;
+}
+
+static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ azx_dev_t *azx_dev = get_azx_dev(substream);
+ unsigned int pos;
+
+#ifdef USE_POSBUF
+ /* use the position buffer */
+ pos = *azx_dev->posbuf;
+#else
+ /* read LPIB */
+ pos = azx_sd_readl(azx_dev, SD_LPIB) + azx_dev->fifo_size;
+#endif
+ if (pos >= azx_dev->bufsize)
+ pos = 0;
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static snd_pcm_ops_t azx_pcm_ops = {
+ .open = azx_pcm_open,
+ .close = azx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = azx_pcm_hw_params,
+ .hw_free = azx_pcm_hw_free,
+ .prepare = azx_pcm_prepare,
+ .trigger = azx_pcm_trigger,
+ .pointer = azx_pcm_pointer,
+};
+
+static void azx_pcm_free(snd_pcm_t *pcm)
+{
+ kfree(pcm->private_data);
+}
+
+static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec,
+ struct hda_pcm *cpcm, int pcm_dev)
+{
+ int err;
+ snd_pcm_t *pcm;
+ struct azx_pcm *apcm;
+
+ snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL);
+ snd_assert(cpcm->name, return -EINVAL);
+
+ err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+ cpcm->stream[0].substreams, cpcm->stream[1].substreams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strcpy(pcm->name, cpcm->name);
+ apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+ if (apcm == NULL)
+ return -ENOMEM;
+ apcm->chip = chip;
+ apcm->codec = codec;
+ apcm->hinfo[0] = &cpcm->stream[0];
+ apcm->hinfo[1] = &cpcm->stream[1];
+ pcm->private_data = apcm;
+ pcm->private_free = azx_pcm_free;
+ if (cpcm->stream[0].substreams)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
+ if (cpcm->stream[1].substreams)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 1024 * 64, 1024 * 128);
+ chip->pcm[pcm_dev] = pcm;
+
+ return 0;
+}
+
+static int __devinit azx_pcm_create(azx_t *chip)
+{
+ struct list_head *p;
+ struct hda_codec *codec;
+ int c, err;
+ int pcm_dev;
+
+ if ((err = snd_hda_build_pcms(chip->bus)) < 0)
+ return err;
+
+ pcm_dev = 0;
+ list_for_each(p, &chip->bus->codec_list) {
+ codec = list_entry(p, struct hda_codec, list);
+ for (c = 0; c < codec->num_pcms; c++) {
+ if (pcm_dev >= AZX_MAX_PCMS) {
+ snd_printk(KERN_ERR SFX "Too many PCMs\n");
+ return -EINVAL;
+ }
+ err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev);
+ if (err < 0)
+ return err;
+ pcm_dev++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * mixer creation - all stuff is implemented in hda module
+ */
+static int __devinit azx_mixer_create(azx_t *chip)
+{
+ return snd_hda_build_controls(chip->bus);
+}
+
+
+/*
+ * initialize SD streams
+ */
+static int __devinit azx_init_stream(azx_t *chip)
+{
+ int i;
+
+ /* initialize each stream (aka device)
+ * assign the starting bdl address to each stream (device) and initialize
+ */
+ for (i = 0; i < MAX_ICH6_DEV; i++) {
+ unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
+ azx_dev_t *azx_dev = &chip->azx_dev[i];
+ azx_dev->bdl = (u32 *)(chip->bdl.area + off);
+ azx_dev->bdl_addr = chip->bdl.addr + off;
+#ifdef USE_POSBUF
+ azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
+#endif
+ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+ azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
+ /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+ azx_dev->sd_int_sta_mask = 1 << i;
+ /* stream tag: must be non-zero and unique */
+ azx_dev->index = i;
+ azx_dev->stream_tag = i + 1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int azx_suspend(snd_card_t *card, pm_message_t state)
+{
+ azx_t *chip = card->pm_private_data;
+ int i;
+
+ for (i = 0; i < chip->pcm_devs; i++)
+ if (chip->pcm[i])
+ snd_pcm_suspend_all(chip->pcm[i]);
+ snd_hda_suspend(chip->bus, state);
+ azx_free_cmd_io(chip);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int azx_resume(snd_card_t *card)
+{
+ azx_t *chip = card->pm_private_data;
+
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+ azx_init_chip(chip);
+ snd_hda_resume(chip->bus);
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ * destructor
+ */
+static int azx_free(azx_t *chip)
+{
+ if (chip->remap_addr) {
+ int i;
+
+ for (i = 0; i < MAX_ICH6_DEV; i++)
+ azx_stream_stop(chip, &chip->azx_dev[i]);
+
+ /* disable interrupts */
+ azx_int_disable(chip);
+ azx_int_clear(chip);
+
+ /* disable CORB/RIRB */
+ azx_free_cmd_io(chip);
+
+ /* disable position buffer */
+ azx_writel(chip, DPLBASE, 0);
+ azx_writel(chip, DPUBASE, 0);
+
+ /* wait a little for interrupts to finish */
+ msleep(1);
+
+ iounmap(chip->remap_addr);
+ }
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void*)chip);
+
+ if (chip->bdl.area)
+ snd_dma_free_pages(&chip->bdl);
+ if (chip->rb.area)
+ snd_dma_free_pages(&chip->rb);
+#ifdef USE_POSBUF
+ if (chip->posbuf.area)
+ snd_dma_free_pages(&chip->posbuf);
+#endif
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+
+ return 0;
+}
+
+static int azx_dev_free(snd_device_t *device)
+{
+ return azx_free(device->device_data);
+}
+
+/*
+ * constructor
+ */
+static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **rchip)
+{
+ azx_t *chip;
+ int err = 0;
+ static snd_device_ops_t ops = {
+ .dev_free = azx_dev_free,
+ };
+
+ *rchip = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+
+ if (NULL == chip) {
+ snd_printk(KERN_ERR SFX "cannot allocate chip\n");
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ init_MUTEX(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ chip->addr = pci_resource_start(pci,0);
+ chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0));
+ if (chip->remap_addr == NULL) {
+ snd_printk(KERN_ERR SFX "ioremap error\n");
+ err = -ENXIO;
+ goto errout;
+ }
+
+ if (request_irq(pci->irq, azx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ "HDA Intel", (void*)chip)) {
+ snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
+ err = -EBUSY;
+ goto errout;
+ }
+ chip->irq = pci->irq;
+
+ pci_set_master(pci);
+ synchronize_irq(chip->irq);
+
+ /* allocate memory for the BDL for each stream */
+ if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->bdl)) < 0) {
+ snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
+ goto errout;
+ }
+#ifdef USE_POSBUF
+ /* allocate memory for the position buffer */
+ if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) {
+ snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
+ goto errout;
+ }
+#endif
+ /* allocate CORB/RIRB */
+ if ((err = azx_alloc_cmd_io(chip)) < 0)
+ goto errout;
+
+ /* initialize streams */
+ azx_init_stream(chip);
+
+ /* initialize chip */
+ azx_init_chip(chip);
+
+ /* codec detection */
+ if (! chip->codec_mask) {
+ snd_printk(KERN_ERR SFX "no codecs found!\n");
+ err = -ENODEV;
+ goto errout;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) <0) {
+ snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
+ goto errout;
+ }
+
+ *rchip = chip;
+ return 0;
+
+ errout:
+ azx_free(chip);
+ return err;
+}
+
+static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ azx_t *chip;
+ int err = 0;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (! enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (NULL == card) {
+ snd_printk(KERN_ERR SFX "Error creating card!\n");
+ return -ENOMEM;
+ }
+
+ if ((err = azx_create(card, pci, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "HDA-Intel");
+ strcpy(card->shortname, "HDA Intel");
+ sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq);
+
+ /* create codec instances */
+ if ((err = azx_codec_create(chip, model[dev])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* create PCM streams */
+ if ((err = azx_pcm_create(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* create mixer controls */
+ if ((err = azx_mixer_create(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_card_set_pm_callback(card, azx_suspend, azx_resume, chip);
+ snd_card_set_dev(card, &pci->dev);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+
+ return err;
+}
+
+static void __devexit azx_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static struct pci_device_id azx_ids[] = {
+ { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH6 */
+ { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH7 */
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, azx_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+ .name = "HDA Intel",
+ .id_table = azx_ids,
+ .probe = azx_probe,
+ .remove = __devexit_p(azx_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_azx_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_azx_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_azx_init)
+module_exit(alsa_card_azx_exit)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
new file mode 100644
index 00000000000..7c7b849875a
--- /dev/null
+++ b/sound/pci/hda/hda_local.h
@@ -0,0 +1,161 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Local helper functions
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SOUND_HDA_LOCAL_H
+#define __SOUND_HDA_LOCAL_H
+
+/*
+ * for mixer controls
+ */
+#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19))
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .info = snd_hda_mixer_amp_volume_info, \
+ .get = snd_hda_mixer_amp_volume_get, \
+ .put = snd_hda_mixer_amp_volume_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
+ HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
+ HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
+ HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = snd_hda_mixer_amp_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \
+ HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \
+ HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
+ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
+
+int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
+int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
+int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
+
+/*
+ * input MUX helper
+ */
+#define HDA_MAX_NUM_INPUTS 8
+struct hda_input_mux_item {
+ const char *label;
+ unsigned int index;
+};
+struct hda_input_mux {
+ unsigned int num_items;
+ struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS];
+};
+
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo);
+int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
+ snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
+ unsigned int *cur_val);
+
+/*
+ * Multi-channel / digital-out PCM helper
+ */
+
+enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
+enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
+
+struct hda_multi_out {
+ int num_dacs; /* # of DACs, must be more than 1 */
+ hda_nid_t *dac_nids; /* DAC list */
+ hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
+ hda_nid_t dig_out_nid; /* digital out audio widget */
+ int max_channels; /* currently supported analog channels */
+ int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
+};
+
+int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout);
+int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout);
+int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
+ snd_pcm_substream_t *substream);
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream);
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout);
+
+/*
+ * generic codec parser
+ */
+int snd_hda_parse_generic_codec(struct hda_codec *codec);
+
+/*
+ * generic proc interface
+ */
+#ifdef CONFIG_PROC_FS
+int snd_hda_codec_proc_new(struct hda_codec *codec);
+#else
+static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
+#endif
+
+/*
+ * Misc
+ */
+struct hda_board_config {
+ const char *modelname;
+ int config;
+ unsigned short pci_vendor;
+ unsigned short pci_device;
+};
+
+int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl);
+int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew);
+
+/*
+ * power management
+ */
+#ifdef CONFIG_PM
+int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew);
+int snd_hda_resume_spdif_out(struct hda_codec *codec);
+int snd_hda_resume_spdif_in(struct hda_codec *codec);
+#endif
+
+/*
+ * unsolicited event handler
+ */
+
+#define HDA_UNSOL_QUEUE_SIZE 64
+
+struct hda_bus_unsolicited {
+ /* ring buffer */
+ u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
+ unsigned int rp, wp;
+
+ /* workqueue */
+ struct workqueue_struct *workq;
+ struct work_struct work;
+};
+
+#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
new file mode 100644
index 00000000000..cf6abce42bc
--- /dev/null
+++ b/sound/pci/hda/hda_patch.h
@@ -0,0 +1,17 @@
+/*
+ * HDA Patches - included by hda_codec.c
+ */
+
+/* Realtek codecs */
+extern struct hda_codec_preset snd_hda_preset_realtek[];
+/* C-Media codecs */
+extern struct hda_codec_preset snd_hda_preset_cmedia[];
+/* Analog Devices codecs */
+extern struct hda_codec_preset snd_hda_preset_analog[];
+
+static const struct hda_codec_preset *hda_preset_tables[] = {
+ snd_hda_preset_realtek,
+ snd_hda_preset_cmedia,
+ snd_hda_preset_analog,
+ NULL
+};
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
new file mode 100644
index 00000000000..4d5db7faad8
--- /dev/null
+++ b/sound/pci/hda/hda_proc.c
@@ -0,0 +1,298 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Generic proc interface
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+
+static const char *get_wid_type_name(unsigned int wid_value)
+{
+ static char *names[16] = {
+ [AC_WID_AUD_OUT] = "Audio Output",
+ [AC_WID_AUD_IN] = "Audio Input",
+ [AC_WID_AUD_MIX] = "Audio Mixer",
+ [AC_WID_AUD_SEL] = "Audio Selector",
+ [AC_WID_PIN] = "Pin Complex",
+ [AC_WID_POWER] = "Power Widget",
+ [AC_WID_VOL_KNB] = "Volume Knob Widget",
+ [AC_WID_BEEP] = "Beep Generator Widget",
+ [AC_WID_VENDOR] = "Vendor Defined Widget",
+ };
+ wid_value &= 0xf;
+ if (names[wid_value])
+ return names[wid_value];
+ else
+ return "UNKOWN Widget";
+}
+
+static void print_amp_caps(snd_info_buffer_t *buffer,
+ struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int caps;
+ if (dir == HDA_OUTPUT)
+ caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP);
+ else
+ caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_IN_CAP);
+ if (caps == -1 || caps == 0) {
+ snd_iprintf(buffer, "N/A\n");
+ return;
+ }
+ snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n",
+ caps & AC_AMPCAP_OFFSET,
+ (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
+ (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
+ (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
+}
+
+static void print_amp_vals(snd_info_buffer_t *buffer,
+ struct hda_codec *codec, hda_nid_t nid,
+ int dir, int stereo)
+{
+ unsigned int val;
+ if (stereo) {
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_LEFT |
+ (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT :
+ AC_AMP_GET_INPUT));
+ snd_iprintf(buffer, "0x%02x ", val);
+ }
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_RIGHT |
+ (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT :
+ AC_AMP_GET_INPUT));
+ snd_iprintf(buffer, "0x%02x\n", val);
+}
+
+static void print_pcm_caps(snd_info_buffer_t *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+ unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+ if (pcm == -1 || stream == -1) {
+ snd_iprintf(buffer, "N/A\n");
+ return;
+ }
+ snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n",
+ pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf);
+}
+
+static const char *get_jack_location(u32 cfg)
+{
+ static char *bases[7] = {
+ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+ };
+ static unsigned char specials_idx[] = {
+ 0x07, 0x08,
+ 0x17, 0x18, 0x19,
+ 0x37, 0x38
+ };
+ static char *specials[] = {
+ "Rear Panel", "Drive Bar",
+ "Riser", "HDMI", "ATAPI",
+ "Mobile-In", "Mobile-Out"
+ };
+ int i;
+ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+ if ((cfg & 0x0f) < 7)
+ return bases[cfg & 0x0f];
+ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+ if (cfg == specials_idx[i])
+ return specials[i];
+ }
+ return "UNKNOWN";
+}
+
+static const char *get_jack_connection(u32 cfg)
+{
+ static char *names[16] = {
+ "Unknown", "1/8", "1/4", "ATAPI",
+ "RCA", "Optical","Digital", "Analog",
+ "DIN", "XLR", "RJ11", "Comb",
+ NULL, NULL, NULL, "Other"
+ };
+ cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
+ if (names[cfg])
+ return names[cfg];
+ else
+ return "UNKNOWN";
+}
+
+static const char *get_jack_color(u32 cfg)
+{
+ static char *names[16] = {
+ "Unknown", "Black", "Grey", "Blue",
+ "Green", "Red", "Orange", "Yellow",
+ "Purple", "Pink", NULL, NULL,
+ NULL, NULL, "White", "Other",
+ };
+ cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
+ if (names[cfg])
+ return names[cfg];
+ else
+ return "UNKNOWN";
+}
+
+static void print_pin_caps(snd_info_buffer_t *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ static char *jack_types[16] = {
+ "Line Out", "Speaker", "HP Out", "CD",
+ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+ "Line In", "Aux", "Mic", "Telephony",
+ "SPDIF In", "Digitial In", "Reserved", "Other"
+ };
+ static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+ unsigned int caps;
+
+ caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ snd_iprintf(buffer, " Pincap 0x08%x:", caps);
+ if (caps & AC_PINCAP_IN)
+ snd_iprintf(buffer, " IN");
+ if (caps & AC_PINCAP_OUT)
+ snd_iprintf(buffer, " OUT");
+ if (caps & AC_PINCAP_HP_DRV)
+ snd_iprintf(buffer, " HP");
+ snd_iprintf(buffer, "\n");
+ caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ snd_iprintf(buffer, " Pin Default 0x%08x: %s at %s %s\n", caps,
+ jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
+ jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
+ get_jack_location(caps));
+ snd_iprintf(buffer, " Conn = %s, Color = %s\n",
+ get_jack_connection(caps),
+ get_jack_color(caps));
+}
+
+
+static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ struct hda_codec *codec = entry->private_data;
+ char buf[32];
+ hda_nid_t nid;
+ int i, nodes;
+
+ snd_hda_get_codec_name(codec, buf, sizeof(buf));
+ snd_iprintf(buffer, "Codec: %s\n", buf);
+ snd_iprintf(buffer, "Address: %d\n", codec->addr);
+ snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
+ snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
+ snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
+ snd_iprintf(buffer, "Default PCM: ");
+ print_pcm_caps(buffer, codec, codec->afg);
+ snd_iprintf(buffer, "Default Amp-In caps: ");
+ print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
+ snd_iprintf(buffer, "Default Amp-Out caps: ");
+ print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
+
+ nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+ if (! nid || nodes < 0) {
+ snd_iprintf(buffer, "Invalid AFG subtree\n");
+ return;
+ }
+ for (i = 0; i < nodes; i++, nid++) {
+ unsigned int wid_caps = snd_hda_param_read(codec, nid,
+ AC_PAR_AUDIO_WIDGET_CAP);
+ unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
+ get_wid_type_name(wid_type), wid_caps);
+ if (wid_caps & AC_WCAP_STEREO)
+ snd_iprintf(buffer, " Stereo");
+ else
+ snd_iprintf(buffer, " Mono");
+ if (wid_caps & AC_WCAP_DIGITAL)
+ snd_iprintf(buffer, " Digital");
+ if (wid_caps & AC_WCAP_IN_AMP)
+ snd_iprintf(buffer, " Amp-In");
+ if (wid_caps & AC_WCAP_OUT_AMP)
+ snd_iprintf(buffer, " Amp-Out");
+ snd_iprintf(buffer, "\n");
+
+ if (wid_caps & AC_WCAP_IN_AMP) {
+ snd_iprintf(buffer, " Amp-In caps: ");
+ print_amp_caps(buffer, codec, nid, HDA_INPUT);
+ snd_iprintf(buffer, " Amp-In vals: ");
+ print_amp_vals(buffer, codec, nid, HDA_INPUT,
+ wid_caps & AC_WCAP_STEREO);
+ }
+ if (wid_caps & AC_WCAP_OUT_AMP) {
+ snd_iprintf(buffer, " Amp-Out caps: ");
+ print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
+ snd_iprintf(buffer, " Amp-Out vals: ");
+ print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+ wid_caps & AC_WCAP_STEREO);
+ }
+
+ if (wid_type == AC_WID_PIN) {
+ unsigned int pinctls;
+ print_pin_caps(buffer, codec, nid);
+ pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
+ if (pinctls & AC_PINCTL_IN_EN)
+ snd_iprintf(buffer, " IN");
+ if (pinctls & AC_PINCTL_OUT_EN)
+ snd_iprintf(buffer, " OUT");
+ if (pinctls & AC_PINCTL_HP_EN)
+ snd_iprintf(buffer, " HP");
+ snd_iprintf(buffer, "\n");
+ }
+
+ if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
+ (wid_caps & AC_WCAP_FORMAT_OVRD)) {
+ snd_iprintf(buffer, " PCM: ");
+ print_pcm_caps(buffer, codec, nid);
+ }
+
+ if (wid_caps & AC_WCAP_CONN_LIST) {
+ hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ int c, conn_len;
+ conn_len = snd_hda_get_connections(codec, nid, conn,
+ HDA_MAX_CONNECTIONS);
+ snd_iprintf(buffer, " Connection: %d\n", conn_len);
+ snd_iprintf(buffer, " ");
+ for (c = 0; c < conn_len; c++)
+ snd_iprintf(buffer, " 0x%02x", conn[c]);
+ snd_iprintf(buffer, "\n");
+ }
+ }
+}
+
+/*
+ * create a proc read
+ */
+int snd_hda_codec_proc_new(struct hda_codec *codec)
+{
+ char name[32];
+ snd_info_entry_t *entry;
+ int err;
+
+ snprintf(name, sizeof(name), "codec#%d", codec->addr);
+ err = snd_card_proc_new(codec->bus->card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info);
+ return 0;
+}
+
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
new file mode 100644
index 00000000000..75d23849f71
--- /dev/null
+++ b/sound/pci/hda/patch_analog.c
@@ -0,0 +1,445 @@
+/*
+ * HD audio interface patch for AD1986A
+ *
+ * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+struct ad1986a_spec {
+ struct semaphore amp_mutex; /* PCM volume/mute control mutex */
+ struct hda_multi_out multiout; /* playback */
+ unsigned int cur_mux; /* capture source */
+ struct hda_pcm pcm_rec[2]; /* PCM information */
+};
+
+#define AD1986A_SPDIF_OUT 0x02
+#define AD1986A_FRONT_DAC 0x03
+#define AD1986A_SURR_DAC 0x04
+#define AD1986A_CLFE_DAC 0x05
+#define AD1986A_ADC 0x06
+
+static hda_nid_t ad1986a_dac_nids[3] = {
+ AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
+};
+
+static struct hda_input_mux ad1986a_capture_source = {
+ .num_items = 7,
+ .items = {
+ { "Mic", 0x0 },
+ { "CD", 0x1 },
+ { "Aux", 0x3 },
+ { "Line", 0x4 },
+ { "Mix", 0x5 },
+ { "Mono", 0x6 },
+ { "Phone", 0x7 },
+ },
+};
+
+/*
+ * PCM control
+ *
+ * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
+ */
+
+#define ad1986a_pcm_amp_vol_info snd_hda_mixer_amp_volume_info
+
+static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad1986a_spec *ad = codec->spec;
+
+ down(&ad->amp_mutex);
+ snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
+ up(&ad->amp_mutex);
+ return 0;
+}
+
+static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad1986a_spec *ad = codec->spec;
+ int i, change = 0;
+
+ down(&ad->amp_mutex);
+ for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
+ change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ }
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
+ up(&ad->amp_mutex);
+ return change;
+}
+
+#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_volume_info
+
+static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad1986a_spec *ad = codec->spec;
+
+ down(&ad->amp_mutex);
+ snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+ up(&ad->amp_mutex);
+ return 0;
+}
+
+static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad1986a_spec *ad = codec->spec;
+ int i, change = 0;
+
+ down(&ad->amp_mutex);
+ for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
+ change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ }
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
+ up(&ad->amp_mutex);
+ return change;
+}
+
+/*
+ * input MUX handling
+ */
+static int ad1986a_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ return snd_hda_input_mux_info(&ad1986a_capture_source, uinfo);
+}
+
+static int ad1986a_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad1986a_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux;
+ return 0;
+}
+
+static int ad1986a_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad1986a_spec *spec = codec->spec;
+
+ return snd_hda_input_mux_put(codec, &ad1986a_capture_source, ucontrol,
+ AD1986A_ADC, &spec->cur_mux);
+}
+
+/*
+ * mixers
+ */
+static snd_kcontrol_new_t ad1986a_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .info = ad1986a_pcm_amp_vol_info,
+ .get = ad1986a_pcm_amp_vol_get,
+ .put = ad1986a_pcm_amp_vol_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .info = ad1986a_pcm_amp_sw_info,
+ .get = ad1986a_pcm_amp_sw_get,
+ .put = ad1986a_pcm_amp_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
+ },
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = ad1986a_mux_enum_info,
+ .get = ad1986a_mux_enum_get,
+ .put = ad1986a_mux_enum_put,
+ },
+ HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1986a_init_verbs[] = {
+ /* Front, Surround, CLFE DAC; mute as default */
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ /* Downmix - off */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ /* HP, Line-Out, Surround, CLFE selectors */
+ {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Mono selector */
+ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Mic selector: Mic 1/2 pin */
+ {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Line-in selector: Line-in */
+ {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Mic 1/2 swap */
+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Record selector: mic */
+ {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ /* PC beep */
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ { } /* end */
+};
+
+
+static int ad1986a_init(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, ad1986a_init_verbs);
+ return 0;
+}
+
+static int ad1986a_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_add_new_ctls(codec, ad1986a_mixers);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_out_ctls(codec, AD1986A_SPDIF_OUT);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int ad1986a_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct ad1986a_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int ad1986a_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ struct ad1986a_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+}
+
+static int ad1986a_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct ad1986a_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ad1986a_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct ad1986a_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ad1986a_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct ad1986a_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ad1986a_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ snd_hda_codec_setup_stream(codec, AD1986A_ADC, stream_tag, 0, format);
+ return 0;
+}
+
+static int ad1986a_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ snd_hda_codec_setup_stream(codec, AD1986A_ADC, 0, 0, 0);
+ return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream ad1986a_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 6,
+ .nid = AD1986A_FRONT_DAC, /* NID to query formats and rates */
+ .ops = {
+ .open = ad1986a_playback_pcm_open,
+ .prepare = ad1986a_playback_pcm_prepare,
+ .cleanup = ad1986a_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream ad1986a_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = AD1986A_ADC, /* NID to query formats and rates */
+ .ops = {
+ .prepare = ad1986a_capture_pcm_prepare,
+ .cleanup = ad1986a_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream ad1986a_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = AD1986A_SPDIF_OUT,
+ .ops = {
+ .open = ad1986a_dig_playback_pcm_open,
+ .close = ad1986a_dig_playback_pcm_close
+ },
+};
+
+static int ad1986a_build_pcms(struct hda_codec *codec)
+{
+ struct ad1986a_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->num_pcms = 2;
+ codec->pcm_info = info;
+
+ info->name = "AD1986A Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1986a_pcm_analog_capture;
+ info++;
+
+ info->name = "AD1986A Digital";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_digital_playback;
+
+ return 0;
+}
+
+static void ad1986a_free(struct hda_codec *codec)
+{
+ kfree(codec->spec);
+}
+
+#ifdef CONFIG_PM
+static int ad1986a_resume(struct hda_codec *codec)
+{
+ ad1986a_init(codec);
+ snd_hda_resume_ctls(codec, ad1986a_mixers);
+ snd_hda_resume_spdif_out(codec);
+ return 0;
+}
+#endif
+
+static struct hda_codec_ops ad1986a_patch_ops = {
+ .build_controls = ad1986a_build_controls,
+ .build_pcms = ad1986a_build_pcms,
+ .init = ad1986a_init,
+ .free = ad1986a_free,
+#ifdef CONFIG_PM
+ .resume = ad1986a_resume,
+#endif
+};
+
+static int patch_ad1986a(struct hda_codec *codec)
+{
+ struct ad1986a_spec *spec;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ init_MUTEX(&spec->amp_mutex);
+ codec->spec = spec;
+
+ spec->multiout.max_channels = 6;
+ spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
+ spec->multiout.dac_nids = ad1986a_dac_nids;
+ spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
+
+ codec->patch_ops = ad1986a_patch_ops;
+
+ return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_analog[] = {
+ { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
+ {} /* terminator */
+};
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
new file mode 100644
index 00000000000..b7cc8e4bffb
--- /dev/null
+++ b/sound/pci/hda/patch_cmedia.c
@@ -0,0 +1,621 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for C-Media CMI9880
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+
+/* board config type */
+enum {
+ CMI_MINIMAL, /* back 3-jack */
+ CMI_MIN_FP, /* back 3-jack + front-panel 2-jack */
+ CMI_FULL, /* back 6-jack + front-panel 2-jack */
+ CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */
+ CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */
+};
+
+struct cmi_spec {
+ int board_config;
+ unsigned int surr_switch: 1; /* switchable line,mic */
+ unsigned int no_line_in: 1; /* no line-in (5-jack) */
+ unsigned int front_panel: 1; /* has front-panel 2-jack */
+
+ /* playback */
+ struct hda_multi_out multiout;
+
+ /* capture */
+ hda_nid_t *adc_nids;
+ hda_nid_t dig_in_nid;
+
+ /* capture source */
+ const struct hda_input_mux *input_mux;
+ unsigned int cur_mux[2];
+
+ /* channel mode */
+ unsigned int num_ch_modes;
+ unsigned int cur_ch_mode;
+ const struct cmi_channel_mode *channel_modes;
+
+ struct hda_pcm pcm_rec[2]; /* PCM information */
+};
+
+/*
+ * input MUX
+ */
+static int cmi_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cmi_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int cmi_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cmi_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ return 0;
+}
+
+static int cmi_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cmi_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
+}
+
+/*
+ * shared line-in, mic for surrounds
+ */
+
+/* 3-stack / 2 channel */
+static struct hda_verb cmi9880_ch2_init[] = {
+ /* set line-in PIN for input */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* set mic PIN for input, also enable vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* route front PCM (DAC1) to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ {}
+};
+
+/* 3-stack / 6 channel */
+static struct hda_verb cmi9880_ch6_init[] = {
+ /* set line-in PIN for output */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* set mic PIN for output */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* route front PCM (DAC1) to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ {}
+};
+
+/* 3-stack+front / 8 channel */
+static struct hda_verb cmi9880_ch8_init[] = {
+ /* set line-in PIN for output */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* set mic PIN for output */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* route rear-surround PCM (DAC4) to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 },
+ {}
+};
+
+struct cmi_channel_mode {
+ unsigned int channels;
+ const struct hda_verb *sequence;
+};
+
+static struct cmi_channel_mode cmi9880_channel_modes[3] = {
+ { 2, cmi9880_ch2_init },
+ { 6, cmi9880_ch6_init },
+ { 8, cmi9880_ch8_init },
+};
+
+static int cmi_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cmi_spec *spec = codec->spec;
+
+ snd_assert(spec->channel_modes, return -EINVAL);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->num_ch_modes;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ sprintf(uinfo->value.enumerated.name, "%dch",
+ spec->channel_modes[uinfo->value.enumerated.item].channels);
+ return 0;
+}
+
+static int cmi_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cmi_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_ch_mode;
+ return 0;
+}
+
+static int cmi_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cmi_spec *spec = codec->spec;
+
+ snd_assert(spec->channel_modes, return -EINVAL);
+ if (ucontrol->value.enumerated.item[0] >= spec->num_ch_modes)
+ ucontrol->value.enumerated.item[0] = spec->num_ch_modes;
+ if (ucontrol->value.enumerated.item[0] == spec->cur_ch_mode &&
+ ! codec->in_resume)
+ return 0;
+
+ spec->cur_ch_mode = ucontrol->value.enumerated.item[0];
+ snd_hda_sequence_write(codec, spec->channel_modes[spec->cur_ch_mode].sequence);
+ spec->multiout.max_channels = spec->channel_modes[spec->cur_ch_mode].channels;
+ return 1;
+}
+
+/*
+ */
+static snd_kcontrol_new_t cmi9880_basic_mixer[] = {
+ /* CMI9880 has no playback volumes! */
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Side Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = cmi_mux_enum_info,
+ .get = cmi_mux_enum_get,
+ .put = cmi_mux_enum_put,
+ },
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT),
+ { } /* end */
+};
+
+/*
+ * shared I/O pins
+ */
+static snd_kcontrol_new_t cmi9880_ch_mode_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = cmi_ch_mode_info,
+ .get = cmi_ch_mode_get,
+ .put = cmi_ch_mode_put,
+ },
+ { } /* end */
+};
+
+/* AUD-in selections:
+ * 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20
+ */
+static struct hda_input_mux cmi9880_basic_mux = {
+ .num_items = 4,
+ .items = {
+ { "Front Mic", 0x5 },
+ { "Rear Mic", 0x2 },
+ { "Line", 0x1 },
+ { "CD", 0x7 },
+ }
+};
+
+static struct hda_input_mux cmi9880_no_line_mux = {
+ .num_items = 3,
+ .items = {
+ { "Front Mic", 0x5 },
+ { "Rear Mic", 0x2 },
+ { "CD", 0x7 },
+ }
+};
+
+/* front, rear, clfe, rear_surr */
+static hda_nid_t cmi9880_dac_nids[4] = {
+ 0x03, 0x04, 0x05, 0x06
+};
+/* ADC0, ADC1 */
+static hda_nid_t cmi9880_adc_nids[2] = {
+ 0x08, 0x09
+};
+
+#define CMI_DIG_OUT_NID 0x07
+#define CMI_DIG_IN_NID 0x0a
+
+/*
+ */
+static struct hda_verb cmi9880_basic_init[] = {
+ /* port-D for line out (rear panel) */
+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-E for HP out (front panel) */
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* route front PCM to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-A for surround (rear panel) */
+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-G for CLFE (rear panel) */
+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-H for side (rear panel) */
+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-C for line-in (rear panel) */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* port-B for mic-in (rear panel) with vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-F for mic-in (front panel) with vref */
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* CD-in */
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* route front mic to ADC1/2 */
+ { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
+ { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
+ {} /* terminator */
+};
+
+static struct hda_verb cmi9880_allout_init[] = {
+ /* port-D for line out (rear panel) */
+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-E for HP out (front panel) */
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* route front PCM to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-A for side (rear panel) */
+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-G for CLFE (rear panel) */
+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-C for surround (rear panel) */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* port-B for mic-in (rear panel) with vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-F for mic-in (front panel) with vref */
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* CD-in */
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* route front mic to ADC1/2 */
+ { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
+ { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
+ {} /* terminator */
+};
+
+/*
+ */
+static int cmi9880_build_controls(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer);
+ if (err < 0)
+ return err;
+ if (spec->surr_switch) {
+ err = snd_hda_add_new_ctls(codec, cmi9880_ch_mode_mixer);
+ if (err < 0)
+ return err;
+ }
+ if (spec->multiout.dig_out_nid) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int cmi9880_init(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+ if (spec->board_config == CMI_ALLOUT)
+ snd_hda_sequence_write(codec, cmi9880_allout_init);
+ else
+ snd_hda_sequence_write(codec, cmi9880_basic_init);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int cmi9880_resume(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ cmi9880_init(codec);
+ snd_hda_resume_ctls(codec, cmi9880_basic_mixer);
+ if (spec->surr_switch)
+ snd_hda_resume_ctls(codec, cmi9880_ch_mode_mixer);
+ if (spec->multiout.dig_out_nid)
+ snd_hda_resume_spdif_out(codec);
+ if (spec->dig_in_nid)
+ snd_hda_resume_spdif_in(codec);
+
+ return 0;
+}
+#endif
+
+/*
+ * Analog playback callbacks
+ */
+static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct cmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ struct cmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+}
+
+static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct cmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct cmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct cmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ stream_tag, 0, format);
+ return 0;
+}
+
+static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+ return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = 0x03, /* NID to query formats and rates */
+ .ops = {
+ .open = cmi9880_playback_pcm_open,
+ .prepare = cmi9880_playback_pcm_prepare,
+ .cleanup = cmi9880_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x08, /* NID to query formats and rates */
+ .ops = {
+ .prepare = cmi9880_capture_pcm_prepare,
+ .cleanup = cmi9880_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in cmi9880_build_pcms */
+ .ops = {
+ .open = cmi9880_dig_playback_pcm_open,
+ .close = cmi9880_dig_playback_pcm_close
+ },
+};
+
+static struct hda_pcm_stream cmi9880_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in cmi9880_build_pcms */
+};
+
+static int cmi9880_build_pcms(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "CMI9880";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_analog_capture;
+
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ codec->num_pcms++;
+ info++;
+ info->name = "CMI9880 Digital";
+ if (spec->multiout.dig_out_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in_nid) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+ }
+ }
+
+ return 0;
+}
+
+static void cmi9880_free(struct hda_codec *codec)
+{
+ kfree(codec->spec);
+}
+
+/*
+ */
+
+static struct hda_board_config cmi9880_cfg_tbl[] = {
+ { .modelname = "minimal", .config = CMI_MINIMAL },
+ { .modelname = "min_fp", .config = CMI_MIN_FP },
+ { .modelname = "full", .config = CMI_FULL },
+ { .modelname = "full_dig", .config = CMI_FULL_DIG },
+ { .modelname = "allout", .config = CMI_ALLOUT },
+ {} /* terminator */
+};
+
+static struct hda_codec_ops cmi9880_patch_ops = {
+ .build_controls = cmi9880_build_controls,
+ .build_pcms = cmi9880_build_pcms,
+ .init = cmi9880_init,
+ .free = cmi9880_free,
+#ifdef CONFIG_PM
+ .resume = cmi9880_resume,
+#endif
+};
+
+static int patch_cmi9880(struct hda_codec *codec)
+{
+ struct cmi_spec *spec;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl);
+ if (spec->board_config < 0) {
+ snd_printd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
+ spec->board_config = CMI_FULL_DIG; /* try everything */
+ }
+
+ switch (spec->board_config) {
+ case CMI_MINIMAL:
+ case CMI_MIN_FP:
+ spec->surr_switch = 1;
+ if (spec->board_config == CMI_MINIMAL)
+ spec->num_ch_modes = 2;
+ else {
+ spec->front_panel = 1;
+ spec->num_ch_modes = 3;
+ }
+ spec->channel_modes = cmi9880_channel_modes;
+ spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
+ spec->input_mux = &cmi9880_basic_mux;
+ break;
+ case CMI_FULL:
+ case CMI_FULL_DIG:
+ spec->front_panel = 1;
+ spec->multiout.max_channels = 8;
+ spec->input_mux = &cmi9880_basic_mux;
+ if (spec->board_config == CMI_FULL_DIG) {
+ spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+ spec->dig_in_nid = CMI_DIG_IN_NID;
+ }
+ break;
+ case CMI_ALLOUT:
+ spec->front_panel = 1;
+ spec->multiout.max_channels = 8;
+ spec->no_line_in = 1;
+ spec->input_mux = &cmi9880_no_line_mux;
+ spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+ break;
+ }
+
+ spec->multiout.num_dacs = 4;
+ spec->multiout.dac_nids = cmi9880_dac_nids;
+
+ spec->adc_nids = cmi9880_adc_nids;
+
+ codec->patch_ops = cmi9880_patch_ops;
+
+ return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_cmedia[] = {
+ { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
+ { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+ {} /* terminator */
+};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
new file mode 100644
index 00000000000..17c5062423a
--- /dev/null
+++ b/sound/pci/hda/patch_realtek.c
@@ -0,0 +1,1503 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for ALC 260/880/882 codecs
+ *
+ * Copyright (c) 2004 PeiSen Hou <pshou@realtek.com.tw>
+ * Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+
+/* ALC880 board config type */
+enum {
+ ALC880_MINIMAL,
+ ALC880_3ST,
+ ALC880_3ST_DIG,
+ ALC880_5ST,
+ ALC880_5ST_DIG,
+ ALC880_W810,
+};
+
+struct alc_spec {
+ /* codec parameterization */
+ unsigned int front_panel: 1;
+
+ snd_kcontrol_new_t* mixers[2];
+ unsigned int num_mixers;
+
+ struct hda_verb *init_verbs;
+
+ char* stream_name_analog;
+ struct hda_pcm_stream *stream_analog_playback;
+ struct hda_pcm_stream *stream_analog_capture;
+
+ char* stream_name_digital;
+ struct hda_pcm_stream *stream_digital_playback;
+ struct hda_pcm_stream *stream_digital_capture;
+
+ /* playback */
+ struct hda_multi_out multiout;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t *adc_nids;
+ hda_nid_t dig_in_nid;
+
+ /* capture source */
+ const struct hda_input_mux *input_mux;
+ unsigned int cur_mux[3];
+
+ /* channel model */
+ const struct alc_channel_mode *channel_mode;
+ int num_channel_mode;
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[2];
+};
+
+/* DAC/ADC assignment */
+
+static hda_nid_t alc880_dac_nids[4] = {
+ /* front, rear, clfe, rear_surr */
+ 0x02, 0x05, 0x04, 0x03
+};
+
+static hda_nid_t alc880_w810_dac_nids[3] = {
+ /* front, rear/surround, clfe */
+ 0x02, 0x03, 0x04
+};
+
+static hda_nid_t alc880_adc_nids[3] = {
+ /* ADC0-2 */
+ 0x07, 0x08, 0x09,
+};
+
+#define ALC880_DIGOUT_NID 0x06
+#define ALC880_DIGIN_NID 0x0a
+
+static hda_nid_t alc260_dac_nids[1] = {
+ /* front */
+ 0x02,
+};
+
+static hda_nid_t alc260_adc_nids[2] = {
+ /* ADC0-1 */
+ 0x04, 0x05,
+};
+
+#define ALC260_DIGOUT_NID 0x03
+#define ALC260_DIGIN_NID 0x06
+
+static struct hda_input_mux alc880_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x3 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc260_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+/*
+ * input MUX handling
+ */
+static int alc_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int alc_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ return 0;
+}
+
+static int alc_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
+}
+
+/*
+ * channel mode setting
+ */
+struct alc_channel_mode {
+ int channels;
+ const struct hda_verb *sequence;
+};
+
+
+/*
+ * channel source setting (2/6 channel selection for 3-stack)
+ */
+
+/*
+ * set the path ways for 2 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc880_threestack_ch2_init[] = {
+ /* set pin widget 1Ah (line in) for input */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* set pin widget 18h (mic1) for input, for mic also enable the vref */
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* mute the output for Line In PW */
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+ /* mute for Mic1 PW */
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ * need to set the codec line out and mic 1 pin widgets to outputs
+ */
+static struct hda_verb alc880_threestack_ch6_init[] = {
+ /* set pin widget 1Ah (line in) for output */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* set pin widget 18h (mic1) for output */
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* unmute the output for Line In PW */
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+ /* unmute for Mic1 PW */
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+ /* for rear channel output using Line In 1
+ * set select widget connection (nid = 0x12) - to summer node
+ * for rear NID = 0x0f...offset 3 in connection list
+ */
+ { 0x12, AC_VERB_SET_CONNECT_SEL, 0x3 },
+ /* for Mic1 - retask for center/lfe */
+ /* set select widget connection (nid = 0x10) - to summer node for
+ * front CLFE NID = 0x0e...offset 2 in connection list
+ */
+ { 0x10, AC_VERB_SET_CONNECT_SEL, 0x2 },
+ { } /* end */
+};
+
+static struct alc_channel_mode alc880_threestack_modes[2] = {
+ { 2, alc880_threestack_ch2_init },
+ { 6, alc880_threestack_ch6_init },
+};
+
+
+/*
+ * channel source setting (6/8 channel selection for 5-stack)
+ */
+
+/* set the path ways for 6 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc880_fivestack_ch6_init[] = {
+ /* set pin widget 1Ah (line in) for input */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* mute the output for Line In PW */
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+ { } /* end */
+};
+
+/* need to set the codec line out and mic 1 pin widgets to outputs */
+static struct hda_verb alc880_fivestack_ch8_init[] = {
+ /* set pin widget 1Ah (line in) for output */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* unmute the output for Line In PW */
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+ /* output for surround channel output using Line In 1 */
+ /* set select widget connection (nid = 0x12) - to summer node
+ * for surr_rear NID = 0x0d...offset 1 in connection list
+ */
+ { 0x12, AC_VERB_SET_CONNECT_SEL, 0x1 },
+ { } /* end */
+};
+
+static struct alc_channel_mode alc880_fivestack_modes[2] = {
+ { 6, alc880_fivestack_ch6_init },
+ { 8, alc880_fivestack_ch8_init },
+};
+
+/*
+ * channel source setting for W810 system
+ *
+ * W810 has rear IO for:
+ * Front (DAC 02)
+ * Surround (DAC 03)
+ * Center/LFE (DAC 04)
+ * Digital out (06)
+ *
+ * The system also has a pair of internal speakers, and a headphone jack.
+ * These are both connected to Line2 on the codec, hence to DAC 02.
+ *
+ * There is a variable resistor to control the speaker or headphone
+ * volume. This is a hardware-only device without a software API.
+ *
+ * Plugging headphones in will disable the internal speakers. This is
+ * implemented in hardware, not via the driver using jack sense. In
+ * a similar fashion, plugging into the rear socket marked "front" will
+ * disable both the speakers and headphones.
+ *
+ * For input, there's a microphone jack, and an "audio in" jack.
+ * These may not do anything useful with this driver yet, because I
+ * haven't setup any initialization verbs for these yet...
+ */
+
+static struct alc_channel_mode alc880_w810_modes[1] = {
+ { 6, NULL }
+};
+
+/*
+ */
+static int alc880_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+
+ snd_assert(spec->channel_mode, return -ENXIO);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= 2)
+ uinfo->value.enumerated.item = 1;
+ sprintf(uinfo->value.enumerated.name, "%dch",
+ spec->channel_mode[uinfo->value.enumerated.item].channels);
+ return 0;
+}
+
+static int alc880_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+
+ snd_assert(spec->channel_mode, return -ENXIO);
+ ucontrol->value.enumerated.item[0] =
+ (spec->multiout.max_channels == spec->channel_mode[0].channels) ? 0 : 1;
+ return 0;
+}
+
+static int alc880_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int mode;
+
+ snd_assert(spec->channel_mode, return -ENXIO);
+ mode = ucontrol->value.enumerated.item[0] ? 1 : 0;
+ if (spec->multiout.max_channels == spec->channel_mode[mode].channels &&
+ ! codec->in_resume)
+ return 0;
+
+ /* change the current channel setting */
+ spec->multiout.max_channels = spec->channel_mode[mode].channels;
+ if (spec->channel_mode[mode].sequence)
+ snd_hda_sequence_write(codec, spec->channel_mode[mode].sequence);
+
+ return 1;
+}
+
+
+/*
+ */
+
+/* 3-stack mode
+ * Pin assignment: Front=0x14, Line-In/Rear=0x1a, Mic/CLFE=0x18, F-Mic=0x1b
+ * HP=0x19
+ */
+static snd_kcontrol_new_t alc880_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x18, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x18, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc880_ch_mode_info,
+ .get = alc880_ch_mode_get,
+ .put = alc880_ch_mode_put,
+ },
+ { } /* end */
+};
+
+/* 5-stack mode
+ * Pin assignment: Front=0x14, Rear=0x17, CLFE=0x16
+ * Line-In/Side=0x1a, Mic=0x18, F-Mic=0x1b, HP=0x19
+ */
+static snd_kcontrol_new_t alc880_five_stack_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Side Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc880_ch_mode_info,
+ .get = alc880_ch_mode_get,
+ .put = alc880_ch_mode_put,
+ },
+ { } /* end */
+};
+
+static snd_kcontrol_new_t alc880_w810_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 3,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/*
+ */
+static int alc_build_controls(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->multiout.dig_out_nid) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * initialize the codec volumes, etc
+ */
+
+static struct hda_verb alc880_init_verbs_three_stack[] = {
+ /* Line In pin widget for input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* CD pin widget for input */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* Mic1 (rear panel) pin widget for input and vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Mic2 (front panel) pin widget for input and vref at 80% */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* unmute amp left and right */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+ /* set connection select to line in (default select for this ADC) */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* unmute front mixer amp left (volume = 0) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* unmute rear mixer amp left and right (volume = 0) */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* unmute rear mixer amp left and right (volume = 0) */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+
+ /* using rear surround as the path for headphone output */
+ /* unmute rear surround mixer amp left and right (volume = 0) */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* PASD 3 stack boards use the Mic 2 as the headphone output */
+ /* need to program the selector associated with the Mic 2 pin widget to
+ * surround path (index 0x01) for headphone output */
+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* need to retask the Mic 2 pin widget to output */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer widget(nid=0x0B)
+ * to support the input path of analog loopback
+ * Note: PASD motherboards uses the Line In 2 as the input for front panel
+ * mic (mic 2)
+ */
+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+ /* unmute CD */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* unmute Line In */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ /* unmute Mic 1 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* unmute Line In 2 (for PASD boards Mic 2) */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+
+ /* Unmute input amps for the line out paths to support the output path of
+ * analog loopback
+ * the mixers on the output path has 2 inputs, one from the DAC and one
+ * from the mixer
+ */
+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+ /* Unmute Front out path */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Surround (used as HP) out path */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute C/LFE out path */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */
+ /* Unmute rear Surround out path */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+ { }
+};
+
+static struct hda_verb alc880_init_verbs_five_stack[] = {
+ /* Line In pin widget for input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* CD pin widget for input */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* Mic1 (rear panel) pin widget for input and vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Mic2 (front panel) pin widget for input and vref at 80% */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* unmute amp left and right */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+ /* set connection select to line in (default select for this ADC) */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* unmute front mixer amp left and right (volume = 0) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* five rear and clfe */
+ /* unmute rear mixer amp left and right (volume = 0) */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* unmute clfe mixer amp left and right (volume = 0) */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+
+ /* using rear surround as the path for headphone output */
+ /* unmute rear surround mixer amp left and right (volume = 0) */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* PASD 3 stack boards use the Mic 2 as the headphone output */
+ /* need to program the selector associated with the Mic 2 pin widget to
+ * surround path (index 0x01) for headphone output
+ */
+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* need to retask the Mic 2 pin widget to output */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer
+ * widget(nid=0x0B) to support the input path of analog loopback
+ */
+ /* Note: PASD motherboards uses the Line In 2 as the input for front panel mic (mic 2) */
+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03*/
+ /* unmute CD */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* unmute Line In */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ /* unmute Mic 1 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* unmute Line In 2 (for PASD boards Mic 2) */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+
+ /* Unmute input amps for the line out paths to support the output path of
+ * analog loopback
+ * the mixers on the output path has 2 inputs, one from the DAC and
+ * one from the mixer
+ */
+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+ /* Unmute Front out path */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Surround (used as HP) out path */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute C/LFE out path */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */
+ /* Unmute rear Surround out path */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+ { }
+};
+
+static struct hda_verb alc880_w810_init_verbs[] = {
+ /* front channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+ /* front channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+
+ /* front channel selector/amp: output 0: unmuted, max volume */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+
+ /* front out pin: muted, (no volume selection) */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+ /* front out pin: NOT headphone enable, out enable, vref disabled */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+ /* surround channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+ /* surround channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+
+ /* surround channel selector/amp: output 0: unmuted, max volume */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+
+ /* surround out pin: muted, (no volume selection) */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+ /* surround out pin: NOT headphone enable, out enable, vref disabled */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+ /* c/lfe channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+ /* c/lfe channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+
+ /* c/lfe channel selector/amp: output 0: unmuted, max volume */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+
+ /* c/lfe out pin: muted, (no volume selection) */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+ /* c/lfe out pin: NOT headphone enable, out enable, vref disabled */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+ /* hphone/speaker input selector: front DAC */
+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+ /* hphone/speaker out pin: muted, (no volume selection) */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+ /* hphone/speaker out pin: NOT headphone enable, out enable, vref disabled */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+ { }
+};
+
+static int alc_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ snd_hda_sequence_write(codec, spec->init_verbs);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int alc_resume(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ alc_init(codec);
+ for (i = 0; i < spec->num_mixers; i++) {
+ snd_hda_resume_ctls(codec, spec->mixers[i]);
+ }
+ if (spec->multiout.dig_out_nid)
+ snd_hda_resume_spdif_out(codec);
+ if (spec->dig_in_nid)
+ snd_hda_resume_spdif_in(codec);
+
+ return 0;
+}
+#endif
+
+/*
+ * Analog playback callbacks
+ */
+static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+}
+
+static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ stream_tag, 0, format);
+ return 0;
+}
+
+static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+ return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream alc880_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = 0x02, /* NID to query formats and rates */
+ .ops = {
+ .open = alc880_playback_pcm_open,
+ .prepare = alc880_playback_pcm_prepare,
+ .cleanup = alc880_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x07, /* NID to query formats and rates */
+ .ops = {
+ .prepare = alc880_capture_pcm_prepare,
+ .cleanup = alc880_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+ .ops = {
+ .open = alc880_dig_playback_pcm_open,
+ .close = alc880_dig_playback_pcm_close
+ },
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static int alc_build_pcms(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+ int i;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = spec->stream_name_analog;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
+ for (i = 0; i < spec->num_channel_mode; i++) {
+ if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+ }
+ }
+
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ codec->num_pcms++;
+ info++;
+ info->name = spec->stream_name_digital;
+ if (spec->multiout.dig_out_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in_nid) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+ }
+ }
+
+ return 0;
+}
+
+static void alc_free(struct hda_codec *codec)
+{
+ kfree(codec->spec);
+}
+
+/*
+ */
+static struct hda_codec_ops alc_patch_ops = {
+ .build_controls = alc_build_controls,
+ .build_pcms = alc_build_pcms,
+ .init = alc_init,
+ .free = alc_free,
+#ifdef CONFIG_PM
+ .resume = alc_resume,
+#endif
+};
+
+/*
+ */
+
+static struct hda_board_config alc880_cfg_tbl[] = {
+ /* Back 3 jack, front 2 jack */
+ { .modelname = "3stack", .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe200, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe201, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe202, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe203, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe204, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe205, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe206, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe207, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe208, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe209, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe20a, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe20b, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe20c, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe20d, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe20e, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe20f, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe210, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe211, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe214, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe302, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe303, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe304, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe306, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe307, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xe404, .config = ALC880_3ST },
+ { .pci_vendor = 0x8086, .pci_device = 0xa101, .config = ALC880_3ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x3031, .config = ALC880_3ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x4036, .config = ALC880_3ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x4037, .config = ALC880_3ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x4038, .config = ALC880_3ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x4040, .config = ALC880_3ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x4041, .config = ALC880_3ST },
+
+ /* Back 3 jack, front 2 jack (Internal add Aux-In) */
+ { .pci_vendor = 0x1025, .pci_device = 0xe310, .config = ALC880_3ST },
+
+ /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
+ { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xe308, .config = ALC880_3ST_DIG },
+
+ /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
+ { .pci_vendor = 0x8086, .pci_device = 0xe305, .config = ALC880_3ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xd402, .config = ALC880_3ST_DIG },
+ { .pci_vendor = 0x1025, .pci_device = 0xe309, .config = ALC880_3ST_DIG },
+
+ /* Back 5 jack, front 2 jack */
+ { .modelname = "5stack", .config = ALC880_5ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x3033, .config = ALC880_5ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x4039, .config = ALC880_5ST },
+ { .pci_vendor = 0x107b, .pci_device = 0x3032, .config = ALC880_5ST },
+ { .pci_vendor = 0x103c, .pci_device = 0x2a09, .config = ALC880_5ST },
+
+ /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
+ { .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xe224, .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xe400, .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xe401, .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xe402, .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xd400, .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xd401, .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x8086, .pci_device = 0xa100, .config = ALC880_5ST_DIG },
+ { .pci_vendor = 0x1565, .pci_device = 0x8202, .config = ALC880_5ST_DIG },
+
+ { .modelname = "w810", .config = ALC880_W810 },
+ { .pci_vendor = 0x161f, .pci_device = 0x203d, .config = ALC880_W810 },
+
+ {}
+};
+
+static int patch_alc880(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
+ if (board_config < 0) {
+ snd_printd(KERN_INFO "hda_codec: Unknown model for ALC880\n");
+ board_config = ALC880_MINIMAL;
+ }
+
+ switch (board_config) {
+ case ALC880_W810:
+ spec->mixers[spec->num_mixers] = alc880_w810_base_mixer;
+ spec->num_mixers++;
+ break;
+ case ALC880_5ST:
+ case ALC880_5ST_DIG:
+ spec->mixers[spec->num_mixers] = alc880_five_stack_mixer;
+ spec->num_mixers++;
+ break;
+ default:
+ spec->mixers[spec->num_mixers] = alc880_base_mixer;
+ spec->num_mixers++;
+ break;
+ }
+
+ switch (board_config) {
+ case ALC880_3ST_DIG:
+ case ALC880_5ST_DIG:
+ case ALC880_W810:
+ spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+ break;
+ default:
+ break;
+ }
+
+ switch (board_config) {
+ case ALC880_3ST:
+ case ALC880_3ST_DIG:
+ case ALC880_5ST:
+ case ALC880_5ST_DIG:
+ case ALC880_W810:
+ spec->front_panel = 1;
+ break;
+ default:
+ break;
+ }
+
+ switch (board_config) {
+ case ALC880_5ST:
+ case ALC880_5ST_DIG:
+ spec->init_verbs = alc880_init_verbs_five_stack;
+ spec->channel_mode = alc880_fivestack_modes;
+ spec->num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes);
+ break;
+ case ALC880_W810:
+ spec->init_verbs = alc880_w810_init_verbs;
+ spec->channel_mode = alc880_w810_modes;
+ spec->num_channel_mode = ARRAY_SIZE(alc880_w810_modes);
+ break;
+ default:
+ spec->init_verbs = alc880_init_verbs_three_stack;
+ spec->channel_mode = alc880_threestack_modes;
+ spec->num_channel_mode = ARRAY_SIZE(alc880_threestack_modes);
+ break;
+ }
+
+ spec->stream_name_analog = "ALC880 Analog";
+ spec->stream_analog_playback = &alc880_pcm_analog_playback;
+ spec->stream_analog_capture = &alc880_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC880 Digital";
+ spec->stream_digital_playback = &alc880_pcm_digital_playback;
+ spec->stream_digital_capture = &alc880_pcm_digital_capture;
+
+ spec->multiout.max_channels = spec->channel_mode[0].channels;
+
+ switch (board_config) {
+ case ALC880_W810:
+ spec->multiout.num_dacs = ARRAY_SIZE(alc880_w810_dac_nids);
+ spec->multiout.dac_nids = alc880_w810_dac_nids;
+ // No dedicated headphone socket - it's shared with built-in speakers.
+ break;
+ default:
+ spec->multiout.num_dacs = ARRAY_SIZE(alc880_dac_nids);
+ spec->multiout.dac_nids = alc880_dac_nids;
+ spec->multiout.hp_nid = 0x03; /* rear-surround NID */
+ break;
+ }
+
+ spec->input_mux = &alc880_capture_source;
+ spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
+ spec->adc_nids = alc880_adc_nids;
+
+ codec->patch_ops = alc_patch_ops;
+
+ return 0;
+}
+
+/*
+ * ALC260 support
+ */
+
+/*
+ * This is just place-holder, so there's something for alc_build_pcms to look
+ * at when it calculates the maximum number of channels. ALC260 has no mixer
+ * element which allows changing the channel mode, so the verb list is
+ * never used.
+ */
+static struct alc_channel_mode alc260_modes[1] = {
+ { 2, NULL },
+};
+
+snd_kcontrol_new_t alc260_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+ /* use LINE2 for the output */
+ /* HDA_CODEC_MUTE("Front Playback Switch", 0x0f, 0x0, HDA_OUTPUT), */
+ HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_verb alc260_init_verbs[] = {
+ /* Line In pin widget for input */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* CD pin widget for input */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* Mic1 (rear panel) pin widget for input and vref at 80% */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Mic2 (front panel) pin widget for input and vref at 80% */
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* LINE-2 is used for line-out in rear */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* select line-out */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* LINE-OUT pin */
+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* enable HP */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* enable Mono */
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* unmute amp left and right */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+ /* set connection select to line in (default select for this ADC) */
+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* unmute Line-Out mixer amp left and right (volume = 0) */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* unmute HP mixer amp left and right (volume = 0) */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ /* unmute Mono mixer amp left and right (volume = 0) */
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ /* mute LINE-2 out */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+ /* unmute CD */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* unmute Line In */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ /* unmute Mic */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+ /* Unmute Front out path */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Headphone out path */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Mono out path */
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ { }
+};
+
+static struct hda_pcm_stream alc260_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x2,
+};
+
+static struct hda_pcm_stream alc260_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x4,
+};
+
+static int patch_alc260(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->mixers[spec->num_mixers] = alc260_base_mixer;
+ spec->num_mixers++;
+
+ spec->init_verbs = alc260_init_verbs;
+ spec->channel_mode = alc260_modes;
+ spec->num_channel_mode = ARRAY_SIZE(alc260_modes);
+
+ spec->stream_name_analog = "ALC260 Analog";
+ spec->stream_analog_playback = &alc260_pcm_analog_playback;
+ spec->stream_analog_capture = &alc260_pcm_analog_capture;
+
+ spec->multiout.max_channels = spec->channel_mode[0].channels;
+ spec->multiout.num_dacs = ARRAY_SIZE(alc260_dac_nids);
+ spec->multiout.dac_nids = alc260_dac_nids;
+
+ spec->input_mux = &alc260_capture_source;
+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+ spec->adc_nids = alc260_adc_nids;
+
+ codec->patch_ops = alc_patch_ops;
+
+ return 0;
+}
+
+/*
+ * ALC882 support
+ *
+ * ALC882 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration. Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs. This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
+ */
+
+static struct alc_channel_mode alc882_ch_modes[1] = {
+ { 8, NULL }
+};
+
+static hda_nid_t alc882_dac_nids[4] = {
+ /* front, rear, clfe, rear_surr */
+ 0x02, 0x03, 0x04, 0x05
+};
+
+static hda_nid_t alc882_adc_nids[3] = {
+ /* ADC0-2 */
+ 0x07, 0x08, 0x09,
+};
+
+/* input MUX */
+/* FIXME: should be a matrix-type input source selection */
+
+static struct hda_input_mux alc882_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+#define alc882_mux_enum_info alc_mux_enum_info
+#define alc882_mux_enum_get alc_mux_enum_get
+
+static int alc882_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = spec->input_mux;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
+ hda_nid_t nid = capture_mixers[adc_idx];
+ unsigned int *cur_val = &spec->cur_mux[adc_idx];
+ unsigned int i, idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (*cur_val == idx && ! codec->in_resume)
+ return 0;
+ for (i = 0; i < imux->num_items; i++) {
+ unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ v | (imux->items[i].index << 8));
+ }
+ *cur_val = idx;
+ return 1;
+}
+
+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
+ * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ */
+static snd_kcontrol_new_t alc882_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Side Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 3,
+ .info = alc882_mux_enum_info,
+ .get = alc882_mux_enum_get,
+ .put = alc882_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_verb alc882_init_verbs[] = {
+ /* Front mixer: unmute input/output amp left and right (volume = 0) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* Rear mixer */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* CLFE mixer */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* Side mixer */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+
+ /* Front Pin: to output mode */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Front Pin: mute amp left and right (no volume) */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* select Front mixer (0x0c, index 0) */
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Rear Pin */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Rear Pin: mute amp left and right (no volume) */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* select Rear mixer (0x0d, index 1) */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* CLFE Pin */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* CLFE Pin: mute amp left and right (no volume) */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* select CLFE mixer (0x0e, index 2) */
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* Side Pin */
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Side Pin: mute amp left and right (no volume) */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* select Side mixer (0x0f, index 3) */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+ /* Headphone Pin */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Headphone Pin: mute amp left and right (no volume) */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* select Front mixer (0x0c, index 0) */
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Mic (rear) pin widget for input and vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Front Mic pin widget for input and vref at 80% */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Line In pin widget for input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* CD pin widget for input */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* ADC1: unmute amp left and right */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+ /* ADC2: unmute amp left and right */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+ /* ADC3: unmute amp left and right */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+ /* Unmute front loopback */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute rear loopback */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Mute CLFE loopback */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+ /* Unmute side loopback */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+ { }
+};
+
+static int patch_alc882(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->mixers[spec->num_mixers] = alc882_base_mixer;
+ spec->num_mixers++;
+
+ spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+ spec->dig_in_nid = ALC880_DIGIN_NID;
+ spec->front_panel = 1;
+ spec->init_verbs = alc882_init_verbs;
+ spec->channel_mode = alc882_ch_modes;
+ spec->num_channel_mode = ARRAY_SIZE(alc882_ch_modes);
+
+ spec->stream_name_analog = "ALC882 Analog";
+ spec->stream_analog_playback = &alc880_pcm_analog_playback;
+ spec->stream_analog_capture = &alc880_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC882 Digital";
+ spec->stream_digital_playback = &alc880_pcm_digital_playback;
+ spec->stream_digital_capture = &alc880_pcm_digital_capture;
+
+ spec->multiout.max_channels = spec->channel_mode[0].channels;
+ spec->multiout.num_dacs = ARRAY_SIZE(alc882_dac_nids);
+ spec->multiout.dac_nids = alc882_dac_nids;
+
+ spec->input_mux = &alc882_capture_source;
+ spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
+ spec->adc_nids = alc882_adc_nids;
+
+ codec->patch_ops = alc_patch_ops;
+
+ return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_realtek[] = {
+ { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
+ { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
+ { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
+ {} /* terminator */
+};
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
new file mode 100644
index 00000000000..7837cef8855
--- /dev/null
+++ b/sound/pci/ice1712/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-ice17xx-ak4xxx-objs := ak4xxx.o
+snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
+obj-$(CONFIG_SND_ICE1724) += snd-ice1724.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
new file mode 100644
index 00000000000..ae9dc029ba0
--- /dev/null
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -0,0 +1,194 @@
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "ice1712.h"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ICEnsemble ICE17xx <-> AK4xxx AD/DA chip interface");
+MODULE_LICENSE("GPL");
+
+static void snd_ice1712_akm4xxx_lock(akm4xxx_t *ak, int chip)
+{
+ ice1712_t *ice = ak->private_data[0];
+
+ snd_ice1712_save_gpio_status(ice);
+}
+
+static void snd_ice1712_akm4xxx_unlock(akm4xxx_t *ak, int chip)
+{
+ ice1712_t *ice = ak->private_data[0];
+
+ snd_ice1712_restore_gpio_status(ice);
+}
+
+/*
+ * write AK4xxx register
+ */
+static void snd_ice1712_akm4xxx_write(akm4xxx_t *ak, int chip,
+ unsigned char addr, unsigned char data)
+{
+ unsigned int tmp;
+ int idx;
+ unsigned int addrdata;
+ struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+ ice1712_t *ice = ak->private_data[0];
+
+ snd_assert(chip >= 0 && chip < 4, return);
+
+ tmp = snd_ice1712_gpio_read(ice);
+ tmp |= priv->add_flags;
+ tmp &= ~priv->mask_flags;
+ if (priv->cs_mask == priv->cs_addr) {
+ if (priv->cif) {
+ tmp |= priv->cs_mask; /* start without chip select */
+ } else {
+ tmp &= ~priv->cs_mask; /* chip select low */
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ }
+ } else {
+ /* doesn't handle cf=1 yet */
+ tmp &= ~priv->cs_mask;
+ tmp |= priv->cs_addr;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ }
+
+ /* build I2C address + data byte */
+ addrdata = (priv->caddr << 6) | 0x20 | (addr & 0x1f);
+ addrdata = (addrdata << 8) | data;
+ for (idx = 15; idx >= 0; idx--) {
+ /* drop clock */
+ tmp &= ~priv->clk_mask;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ /* set data */
+ if (addrdata & (1 << idx))
+ tmp |= priv->data_mask;
+ else
+ tmp &= ~priv->data_mask;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ /* raise clock */
+ tmp |= priv->clk_mask;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ }
+
+ if (priv->cs_mask == priv->cs_addr) {
+ if (priv->cif) {
+ /* assert a cs pulse to trigger */
+ tmp &= ~priv->cs_mask;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ }
+ tmp |= priv->cs_mask; /* chip select high to trigger */
+ } else {
+ tmp &= ~priv->cs_mask;
+ tmp |= priv->cs_none; /* deselect address */
+ }
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+}
+
+/*
+ * initialize the akm4xxx_t record with the template
+ */
+int snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *temp,
+ const struct snd_ak4xxx_private *_priv, ice1712_t *ice)
+{
+ struct snd_ak4xxx_private *priv;
+
+ if (_priv != NULL) {
+ priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+ *priv = *_priv;
+ } else {
+ priv = NULL;
+ }
+ *ak = *temp;
+ ak->card = ice->card;
+ ak->private_value[0] = (unsigned long)priv;
+ ak->private_data[0] = ice;
+ if (ak->ops.lock == NULL)
+ ak->ops.lock = snd_ice1712_akm4xxx_lock;
+ if (ak->ops.unlock == NULL)
+ ak->ops.unlock = snd_ice1712_akm4xxx_unlock;
+ if (ak->ops.write == NULL)
+ ak->ops.write = snd_ice1712_akm4xxx_write;
+ snd_akm4xxx_init(ak);
+ return 0;
+}
+
+void snd_ice1712_akm4xxx_free(ice1712_t *ice)
+{
+ unsigned int akidx;
+ if (ice->akm == NULL)
+ return;
+ for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
+ akm4xxx_t *ak = &ice->akm[akidx];
+ kfree((void*)ak->private_value[0]);
+ }
+ kfree(ice->akm);
+}
+
+/*
+ * build AK4xxx controls
+ */
+int snd_ice1712_akm4xxx_build_controls(ice1712_t *ice)
+{
+ unsigned int akidx;
+ int err;
+
+ for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
+ akm4xxx_t *ak = &ice->akm[akidx];
+ err = snd_akm4xxx_build_controls(ak);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int __init alsa_ice1712_akm4xxx_module_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_ice1712_akm4xxx_module_exit(void)
+{
+}
+
+module_init(alsa_ice1712_akm4xxx_module_init)
+module_exit(alsa_ice1712_akm4xxx_module_exit)
+
+EXPORT_SYMBOL(snd_ice1712_akm4xxx_init);
+EXPORT_SYMBOL(snd_ice1712_akm4xxx_free);
+EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls);
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
new file mode 100644
index 00000000000..779951725e1
--- /dev/null
+++ b/sound/pci/ice1712/amp.c
@@ -0,0 +1,65 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "amp.h"
+
+
+static int __devinit snd_vt1724_amp_init(ice1712_t *ice)
+{
+ /* only use basic functionality for now */
+
+ ice->num_total_dacs = 2; /* only PSDOUT0 is connected */
+ ice->num_total_adcs = 2;
+
+ return 0;
+}
+
+static int __devinit snd_vt1724_amp_add_controls(ice1712_t *ice)
+{
+ /* we use pins 39 and 41 of the VT1616 for left and right read outputs */
+ snd_ac97_write_cache(ice->ac97, 0x5a, snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
+ return 0;
+}
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_AUDIO2000,
+ .name = "AMP Ltd AUDIO2000",
+ .model = "amp2000",
+ .chip_init = snd_vt1724_amp_init,
+ .build_controls = snd_vt1724_amp_add_controls,
+ },
+ { } /* terminator */
+};
+
diff --git a/sound/pci/ice1712/amp.h b/sound/pci/ice1712/amp.h
new file mode 100644
index 00000000000..d58d43383e8
--- /dev/null
+++ b/sound/pci/ice1712/amp.h
@@ -0,0 +1,34 @@
+#ifndef __SOUND_AMP_H
+#define __SOUND_AMP_H
+
+/*
+ * ALSA driver for VIA VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define AMP_AUDIO2000_DEVICE_DESC "{AMP Ltd,AUDIO2000},"
+
+#define VT1724_SUBDEVICE_AUDIO2000 0x12142417 /* Advanced Micro Peripherals Ltd AUDIO2000 */
+
+extern struct snd_ice1712_card_info snd_vt1724_amp_cards[];
+
+
+#endif /* __SOUND_AMP_H */
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
new file mode 100644
index 00000000000..4405d96cbed
--- /dev/null
+++ b/sound/pci/ice1712/aureon.c
@@ -0,0 +1,1948 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Terratec Aureon cards
+ *
+ * Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * NOTES:
+ *
+ * - we reuse the akm4xxx_t record for storing the wm8770 codec data.
+ * both wm and akm codecs are pretty similar, so we can integrate
+ * both controls in the future, once if wm codecs are reused in
+ * many boards.
+ *
+ * - DAC digital volumes are not implemented in the mixer.
+ * if they show better response than DAC analog volumes, we can use them
+ * instead.
+ *
+ * Lowlevel functions for AudioTrak Prodigy 7.1 (and possibly 192) cards
+ * Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
+ *
+ * version 0.82: Stable / not all features work yet (no communication with AC97 secondary)
+ * added 64x/128x oversampling switch (should be 64x only for 96khz)
+ * fixed some recording labels (still need to check the rest)
+ * recording is working probably thanks to correct wm8770 initialization
+ *
+ * version 0.5: Initial release:
+ * working: analog output, mixer, headphone amplifier switch
+ * not working: prety much everything else, at least i could verify that
+ * we have no digital output, no capture, pretty bad clicks and poops
+ * on mixer switch and other coll stuff.
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "aureon.h"
+
+/* WM8770 registers */
+#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
+#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
+#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */
+#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */
+#define WM_PHASE_SWAP 0x12 /* DAC phase */
+#define WM_DAC_CTRL1 0x13 /* DAC control bits */
+#define WM_MUTE 0x14 /* mute controls */
+#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */
+#define WM_INT_CTRL 0x16 /* interface control */
+#define WM_MASTER 0x17 /* master clock and mode */
+#define WM_POWERDOWN 0x18 /* power-down controls */
+#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */
+#define WM_ADC_MUX 0x1b /* input MUX */
+#define WM_OUT_MUX1 0x1c /* output MUX */
+#define WM_OUT_MUX2 0x1e /* output MUX */
+#define WM_RESET 0x1f /* software reset */
+
+/* CS8415A registers */
+#define CS8415_CTRL1 0x01
+#define CS8415_CTRL2 0x02
+#define CS8415_QSUB 0x14
+#define CS8415_RATIO 0x1E
+#define CS8415_C_BUFFER 0x20
+#define CS8415_ID 0x7F
+
+static void aureon_ac97_write(ice1712_t *ice, unsigned short reg, unsigned short val) {
+ unsigned int tmp;
+
+ /* Send address to XILINX chip */
+ tmp = (snd_ice1712_gpio_read(ice) & ~0xFF) | (reg & 0x7F);
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+ tmp |= AUREON_AC97_ADDR;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+ tmp &= ~AUREON_AC97_ADDR;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+
+ /* Send low-order byte to XILINX chip */
+ tmp &= ~AUREON_AC97_DATA_MASK;
+ tmp |= val & AUREON_AC97_DATA_MASK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+ tmp |= AUREON_AC97_DATA_LOW;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+ tmp &= ~AUREON_AC97_DATA_LOW;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+
+ /* Send high-order byte to XILINX chip */
+ tmp &= ~AUREON_AC97_DATA_MASK;
+ tmp |= (val >> 8) & AUREON_AC97_DATA_MASK;
+
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+ tmp |= AUREON_AC97_DATA_HIGH;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+ tmp &= ~AUREON_AC97_DATA_HIGH;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+
+ /* Instruct XILINX chip to parse the data to the STAC9744 chip */
+ tmp |= AUREON_AC97_COMMIT;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+ tmp &= ~AUREON_AC97_COMMIT;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(10);
+
+ /* Store the data in out private buffer */
+ ice->spec.aureon.stac9744[(reg & 0x7F) >> 1] = val;
+}
+
+static unsigned short aureon_ac97_read(ice1712_t *ice, unsigned short reg)
+{
+ return ice->spec.aureon.stac9744[(reg & 0x7F) >> 1];
+}
+
+/*
+ * Initialize STAC9744 chip
+ */
+static int aureon_ac97_init (ice1712_t *ice) {
+ int i;
+ static unsigned short ac97_defaults[] = {
+ 0x00, 0x9640,
+ 0x02, 0x8000,
+ 0x04, 0x8000,
+ 0x06, 0x8000,
+ 0x0C, 0x8008,
+ 0x0E, 0x8008,
+ 0x10, 0x8808,
+ 0x12, 0x8808,
+ 0x14, 0x8808,
+ 0x16, 0x8808,
+ 0x18, 0x8808,
+ 0x1C, 0x8000,
+ 0x26, 0x000F,
+ 0x28, 0x0201,
+ 0x2C, 0xBB80,
+ 0x32, 0xBB80,
+ 0x7C, 0x8384,
+ 0x7E, 0x7644,
+ (unsigned short)-1
+ };
+ unsigned int tmp;
+
+ /* Cold reset */
+ tmp = (snd_ice1712_gpio_read(ice) | AUREON_AC97_RESET) & ~AUREON_AC97_DATA_MASK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(3);
+
+ tmp &= ~AUREON_AC97_RESET;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(3);
+
+ tmp |= AUREON_AC97_RESET;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(3);
+
+ memset(&ice->spec.aureon.stac9744, 0, sizeof(ice->spec.aureon.stac9744));
+ for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2)
+ ice->spec.aureon.stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
+
+ aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770
+
+ return 0;
+}
+
+#define AUREON_AC97_STEREO 0x80
+
+/*
+ * AC'97 volume controls
+ */
+static int aureon_ac97_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = kcontrol->private_value & AUREON_AC97_STEREO ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 31;
+ return 0;
+}
+
+static int aureon_ac97_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short vol;
+
+ down(&ice->gpio_mutex);
+
+ vol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
+ ucontrol->value.integer.value[0] = 0x1F - (vol & 0x1F);
+ if (kcontrol->private_value & AUREON_AC97_STEREO)
+ ucontrol->value.integer.value[1] = 0x1F - ((vol >> 8) & 0x1F);
+
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int aureon_ac97_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short ovol, nvol;
+ int change;
+
+ snd_ice1712_save_gpio_status(ice);
+
+ ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
+ nvol = (0x1F - ucontrol->value.integer.value[0]) & 0x001F;
+ if (kcontrol->private_value & AUREON_AC97_STEREO)
+ nvol |= ((0x1F - ucontrol->value.integer.value[1]) << 8) & 0x1F00;
+ nvol |= ovol & ~0x1F1F;
+
+ if ((change = (ovol != nvol)))
+ aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
+
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/*
+ * AC'97 mute controls
+ */
+#define aureon_ac97_mute_info aureon_mono_bool_info
+
+static int aureon_ac97_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ down(&ice->gpio_mutex);
+
+ ucontrol->value.integer.value[0] = aureon_ac97_read(ice, kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
+
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int aureon_ac97_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short ovol, nvol;
+ int change;
+
+ snd_ice1712_save_gpio_status(ice);
+
+ ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
+ nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~ 0x8000);
+
+ if ((change = (ovol != nvol)))
+ aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
+
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/*
+ * AC'97 mute controls
+ */
+#define aureon_ac97_micboost_info aureon_mono_bool_info
+
+static int aureon_ac97_micboost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ down(&ice->gpio_mutex);
+
+ ucontrol->value.integer.value[0] = aureon_ac97_read(ice, AC97_MIC) & 0x0020 ? 0 : 1;
+
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int aureon_ac97_micboost_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short ovol, nvol;
+ int change;
+
+ snd_ice1712_save_gpio_status(ice);
+
+ ovol = aureon_ac97_read(ice, AC97_MIC);
+ nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x0020) | (ovol & ~0x0020);
+
+ if ((change = (ovol != nvol)))
+ aureon_ac97_write(ice, AC97_MIC, nvol);
+
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/*
+ * write data in the SPI mode
+ */
+static void aureon_spi_write(ice1712_t *ice, unsigned int cs, unsigned int data, int bits)
+{
+ unsigned int tmp;
+ int i;
+
+ tmp = snd_ice1712_gpio_read(ice);
+
+ snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RW|AUREON_SPI_MOSI|AUREON_SPI_CLK|
+ AUREON_WM_CS|AUREON_CS8415_CS));
+ tmp |= AUREON_WM_RW;
+ tmp &= ~cs;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+
+ for (i = bits - 1; i >= 0; i--) {
+ tmp &= ~AUREON_SPI_CLK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ if (data & (1 << i))
+ tmp |= AUREON_SPI_MOSI;
+ else
+ tmp &= ~AUREON_SPI_MOSI;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ tmp |= AUREON_SPI_CLK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ }
+
+ tmp &= ~AUREON_SPI_CLK;
+ tmp |= cs;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ tmp |= AUREON_SPI_CLK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+}
+
+/*
+ * Read data in SPI mode
+ */
+static void aureon_spi_read(ice1712_t *ice, unsigned int cs, unsigned int data, int bits, unsigned char *buffer, int size) {
+ int i, j;
+ unsigned int tmp;
+
+ tmp = (snd_ice1712_gpio_read(ice) & ~AUREON_SPI_CLK) | AUREON_CS8415_CS|AUREON_WM_CS;
+ snd_ice1712_gpio_write(ice, tmp);
+ tmp &= ~cs;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+
+ for (i=bits-1; i>=0; i--) {
+ if (data & (1 << i))
+ tmp |= AUREON_SPI_MOSI;
+ else
+ tmp &= ~AUREON_SPI_MOSI;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+
+ tmp |= AUREON_SPI_CLK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+
+ tmp &= ~AUREON_SPI_CLK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ }
+
+ for (j=0; j<size; j++) {
+ unsigned char outdata = 0;
+ for (i=7; i>=0; i--) {
+ tmp = snd_ice1712_gpio_read(ice);
+ outdata <<= 1;
+ outdata |= (tmp & AUREON_SPI_MISO) ? 1 : 0;
+ udelay(1);
+
+ tmp |= AUREON_SPI_CLK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+
+ tmp &= ~AUREON_SPI_CLK;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ }
+ buffer[j] = outdata;
+ }
+
+ tmp |= cs;
+ snd_ice1712_gpio_write(ice, tmp);
+}
+
+static unsigned char aureon_cs8415_get(ice1712_t *ice, int reg) {
+ unsigned char val;
+ aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
+ aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, &val, 1);
+ return val;
+}
+
+static void aureon_cs8415_read(ice1712_t *ice, int reg, unsigned char *buffer, int size) {
+ aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
+ aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, buffer, size);
+}
+
+static void aureon_cs8415_put(ice1712_t *ice, int reg, unsigned char val) {
+ aureon_spi_write(ice, AUREON_CS8415_CS, 0x200000 | (reg << 8) | val, 24);
+}
+
+/*
+ * get the current register value of WM codec
+ */
+static unsigned short wm_get(ice1712_t *ice, int reg)
+{
+ reg <<= 1;
+ return ((unsigned short)ice->akm[0].images[reg] << 8) |
+ ice->akm[0].images[reg + 1];
+}
+
+/*
+ * set the register value of WM codec
+ */
+static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val)
+{
+ aureon_spi_write(ice, AUREON_WM_CS, (reg << 9) | (val & 0x1ff), 16);
+}
+
+/*
+ * set the register value of WM codec and remember it
+ */
+static void wm_put(ice1712_t *ice, int reg, unsigned short val)
+{
+ wm_put_nocache(ice, reg, val);
+ reg <<= 1;
+ ice->akm[0].images[reg] = val >> 8;
+ ice->akm[0].images[reg + 1] = val;
+}
+
+/*
+ */
+static int aureon_mono_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+/*
+ * AC'97 master playback mute controls (Mute on WM8770 chip)
+ */
+#define aureon_ac97_mmute_info aureon_mono_bool_info
+
+static int aureon_ac97_mmute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ down(&ice->gpio_mutex);
+
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX1) >> 1) & 0x01;
+
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int aureon_ac97_mmute_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short ovol, nvol;
+ int change;
+
+ snd_ice1712_save_gpio_status(ice);
+
+ ovol = wm_get(ice, WM_OUT_MUX1);
+ nvol = (ovol & ~0x02) | (ucontrol->value.integer.value[0] ? 0x02 : 0x00);
+ if ((change = (ovol != nvol)))
+ wm_put(ice, WM_OUT_MUX1, nvol);
+
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/*
+ * Logarithmic volume values for WM8770
+ * Computed as 20 * Log10(255 / x)
+ */
+static unsigned char wm_vol[256] = {
+ 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
+ 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
+ 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
+ 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+
+#define WM_VOL_MAX (sizeof(wm_vol) - 1)
+#define WM_VOL_MUTE 0x8000
+
+static void wm_set_vol(ice1712_t *ice, unsigned int index, unsigned short vol, unsigned short master)
+{
+ unsigned char nvol;
+
+ if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
+ nvol = 0;
+ else
+ nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
+
+ wm_put(ice, index, nvol);
+ wm_put_nocache(ice, index, 0x180 | nvol);
+}
+
+/*
+ * DAC mute control
+ */
+#define wm_pcm_mute_info aureon_mono_bool_info
+
+static int wm_pcm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ down(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_pcm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short nval, oval;
+ int change;
+
+ snd_ice1712_save_gpio_status(ice);
+ oval = wm_get(ice, WM_MUTE);
+ nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
+ if ((change = (nval != oval)))
+ wm_put(ice, WM_MUTE, nval);
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/*
+ * Master volume attenuation mixer control
+ */
+static int wm_master_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = WM_VOL_MAX;
+ return 0;
+}
+
+static int wm_master_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int i;
+ for (i=0; i<2; i++)
+ ucontrol->value.integer.value[i] = ice->spec.aureon.master[i] & ~WM_VOL_MUTE;
+ return 0;
+}
+
+static int wm_master_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int ch, change = 0;
+
+ snd_ice1712_save_gpio_status(ice);
+ for (ch = 0; ch < 2; ch++) {
+ if (ucontrol->value.integer.value[ch] != ice->spec.aureon.master[ch]) {
+ int dac;
+ ice->spec.aureon.master[ch] &= WM_VOL_MUTE;
+ ice->spec.aureon.master[ch] |= ucontrol->value.integer.value[ch];
+ for (dac = 0; dac < ice->num_total_dacs; dac += 2)
+ wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
+ ice->spec.aureon.vol[dac + ch],
+ ice->spec.aureon.master[ch]);
+ change = 1;
+ }
+ }
+ snd_ice1712_restore_gpio_status(ice);
+ return change;
+}
+
+/*
+ * DAC volume attenuation mixer control
+ */
+static int wm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ int voices = kcontrol->private_value >> 8;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = voices;
+ uinfo->value.integer.min = 0; /* mute (-101dB) */
+ uinfo->value.integer.max = 0x7F; /* 0dB */
+ return 0;
+}
+
+static int wm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int i, ofs, voices;
+
+ voices = kcontrol->private_value >> 8;
+ ofs = kcontrol->private_value & 0xff;
+ for (i = 0; i < voices; i++)
+ ucontrol->value.integer.value[i] = ice->spec.aureon.vol[ofs+i] & ~WM_VOL_MUTE;
+ return 0;
+}
+
+static int wm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int i, idx, ofs, voices;
+ int change = 0;
+
+ voices = kcontrol->private_value >> 8;
+ ofs = kcontrol->private_value & 0xff;
+ snd_ice1712_save_gpio_status(ice);
+ for (i = 0; i < voices; i++) {
+ idx = WM_DAC_ATTEN + ofs + i;
+ if (ucontrol->value.integer.value[i] != ice->spec.aureon.vol[ofs+i]) {
+ ice->spec.aureon.vol[ofs+i] &= WM_VOL_MUTE;
+ ice->spec.aureon.vol[ofs+i] |= ucontrol->value.integer.value[i];
+ wm_set_vol(ice, idx, ice->spec.aureon.vol[ofs+i],
+ ice->spec.aureon.master[i]);
+ change = 1;
+ }
+ }
+ snd_ice1712_restore_gpio_status(ice);
+ return change;
+}
+
+/*
+ * WM8770 mute control
+ */
+static int wm_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = kcontrol->private_value >> 8;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int voices, ofs, i;
+
+ voices = kcontrol->private_value >> 8;
+ ofs = kcontrol->private_value & 0xFF;
+
+ for (i = 0; i < voices; i++)
+ ucontrol->value.integer.value[i] = (ice->spec.aureon.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+ return 0;
+}
+
+static int wm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change = 0, voices, ofs, i;
+
+ voices = kcontrol->private_value >> 8;
+ ofs = kcontrol->private_value & 0xFF;
+
+ snd_ice1712_save_gpio_status(ice);
+ for (i = 0; i < voices; i++) {
+ int val = (ice->spec.aureon.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+ if (ucontrol->value.integer.value[i] != val) {
+ ice->spec.aureon.vol[ofs + i] &= ~WM_VOL_MUTE;
+ ice->spec.aureon.vol[ofs + i] |=
+ ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+ wm_set_vol(ice, ofs + i, ice->spec.aureon.vol[ofs + i],
+ ice->spec.aureon.master[i]);
+ change = 1;
+ }
+ }
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/*
+ * WM8770 master mute control
+ */
+static int wm_master_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm_master_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (ice->spec.aureon.master[0] & WM_VOL_MUTE) ? 0 : 1;
+ ucontrol->value.integer.value[1] = (ice->spec.aureon.master[1] & WM_VOL_MUTE) ? 0 : 1;
+ return 0;
+}
+
+static int wm_master_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change = 0, i;
+
+ snd_ice1712_save_gpio_status(ice);
+ for (i = 0; i < 2; i++) {
+ int val = (ice->spec.aureon.master[i] & WM_VOL_MUTE) ? 0 : 1;
+ if (ucontrol->value.integer.value[i] != val) {
+ int dac;
+ ice->spec.aureon.master[i] &= ~WM_VOL_MUTE;
+ ice->spec.aureon.master[i] |=
+ ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+ for (dac = 0; dac < ice->num_total_dacs; dac += 2)
+ wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
+ ice->spec.aureon.vol[dac + i],
+ ice->spec.aureon.master[i]);
+ change = 1;
+ }
+ }
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/* digital master volume */
+#define PCM_0dB 0xff
+#define PCM_RES 128 /* -64dB */
+#define PCM_MIN (PCM_0dB - PCM_RES)
+static int wm_pcm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0; /* mute (-64dB) */
+ uinfo->value.integer.max = PCM_RES; /* 0dB */
+ return 0;
+}
+
+static int wm_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ down(&ice->gpio_mutex);
+ val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+ val = val > PCM_MIN ? (val - PCM_MIN) : 0;
+ ucontrol->value.integer.value[0] = val;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short ovol, nvol;
+ int change = 0;
+
+ snd_ice1712_save_gpio_status(ice);
+ nvol = ucontrol->value.integer.value[0];
+ nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
+ ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+ if (ovol != nvol) {
+ wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
+ wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
+ change = 1;
+ }
+ snd_ice1712_restore_gpio_status(ice);
+ return change;
+}
+
+/*
+ * ADC mute control
+ */
+static int wm_adc_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm_adc_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+ int i;
+
+ down(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ val = wm_get(ice, WM_ADC_GAIN + i);
+ ucontrol->value.integer.value[i] = ~val>>5 & 0x1;
+ }
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short new, old;
+ int i, change = 0;
+
+ snd_ice1712_save_gpio_status(ice);
+ for (i = 0; i < 2; i++) {
+ old = wm_get(ice, WM_ADC_GAIN + i);
+ new = (~ucontrol->value.integer.value[i]<<5&0x20) | (old&~0x20);
+ if (new != old) {
+ wm_put(ice, WM_ADC_GAIN + i, new);
+ change = 1;
+ }
+ }
+ snd_ice1712_restore_gpio_status(ice);
+
+ return change;
+}
+
+/*
+ * ADC gain mixer control
+ */
+static int wm_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* -12dB */
+ uinfo->value.integer.max = 0x1f; /* 19dB */
+ return 0;
+}
+
+static int wm_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int i, idx;
+ unsigned short vol;
+
+ down(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ idx = WM_ADC_GAIN + i;
+ vol = wm_get(ice, idx) & 0x1f;
+ ucontrol->value.integer.value[i] = vol;
+ }
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int i, idx;
+ unsigned short ovol, nvol;
+ int change = 0;
+
+ snd_ice1712_save_gpio_status(ice);
+ for (i = 0; i < 2; i++) {
+ idx = WM_ADC_GAIN + i;
+ nvol = ucontrol->value.integer.value[i];
+ ovol = wm_get(ice, idx);
+ if ((ovol & 0x1f) != nvol) {
+ wm_put(ice, idx, nvol | (ovol & ~0x1f));
+ change = 1;
+ }
+ }
+ snd_ice1712_restore_gpio_status(ice);
+ return change;
+}
+
+/*
+ * ADC input mux mixer control
+ */
+static int wm_adc_mux_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[] = {
+ "CD", //AIN1
+ "Aux", //AIN2
+ "Line", //AIN3
+ "Mic", //AIN4
+ "AC97" //AIN5
+ };
+ static char *universe_texts[] = {
+ "Aux1", //AIN1
+ "CD", //AIN2
+ "Phono", //AIN3
+ "Line", //AIN4
+ "Aux2", //AIN5
+ "Mic", //AIN6
+ "Aux3", //AIN7
+ "AC97" //AIN8
+ };
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 2;
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
+ uinfo->value.enumerated.items = 8;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]);
+ }
+ else {
+ uinfo->value.enumerated.items = 5;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ }
+ return 0;
+}
+
+static int wm_adc_mux_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+
+ down(&ice->gpio_mutex);
+ val = wm_get(ice, WM_ADC_MUX);
+ ucontrol->value.integer.value[0] = val & 7;
+ ucontrol->value.integer.value[1] = (val >> 4) & 7;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_mux_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short oval, nval;
+ int change;
+
+ snd_ice1712_save_gpio_status(ice);
+ oval = wm_get(ice, WM_ADC_MUX);
+ nval = oval & ~0x77;
+ nval |= ucontrol->value.integer.value[0] & 7;
+ nval |= (ucontrol->value.integer.value[1] & 7) << 4;
+ change = (oval != nval);
+ if (change)
+ wm_put(ice, WM_ADC_MUX, nval);
+ snd_ice1712_restore_gpio_status(ice);
+ return 0;
+}
+
+/*
+ * CS8415 Input mux
+ */
+static int aureon_cs8415_mux_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ static char *aureon_texts[] = {
+ "CD", //RXP0
+ "Optical" //RXP1
+ };
+ static char *prodigy_texts[] = {
+ "CD",
+ "Coax"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
+ strcpy(uinfo->value.enumerated.name, prodigy_texts[uinfo->value.enumerated.item]);
+ else
+ strcpy(uinfo->value.enumerated.name, aureon_texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int aureon_cs8415_mux_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ //snd_ice1712_save_gpio_status(ice);
+ //val = aureon_cs8415_get(ice, CS8415_CTRL2);
+ ucontrol->value.integer.value[0] = ice->spec.aureon.cs8415_mux;
+ //snd_ice1712_restore_gpio_status(ice);
+ return 0;
+}
+
+static int aureon_cs8415_mux_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short oval, nval;
+ int change;
+
+ snd_ice1712_save_gpio_status(ice);
+ oval = aureon_cs8415_get(ice, CS8415_CTRL2);
+ nval = oval & ~0x07;
+ nval |= ucontrol->value.integer.value[0] & 7;
+ change = (oval != nval);
+ if (change)
+ aureon_cs8415_put(ice, CS8415_CTRL2, nval);
+ snd_ice1712_restore_gpio_status(ice);
+ ice->spec.aureon.cs8415_mux = ucontrol->value.integer.value[0];
+ return change;
+}
+
+static int aureon_cs8415_rate_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 192000;
+ return 0;
+}
+
+static int aureon_cs8415_rate_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char ratio;
+ ratio = aureon_cs8415_get(ice, CS8415_RATIO);
+ ucontrol->value.integer.value[0] = (int)((unsigned int)ratio * 750);
+ return 0;
+}
+
+/*
+ * CS8415A Mute
+ */
+static int aureon_cs8415_mute_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int aureon_cs8415_mute_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ snd_ice1712_save_gpio_status(ice);
+ ucontrol->value.integer.value[0] = (aureon_cs8415_get(ice, CS8415_CTRL1) & 0x20) ? 0 : 1;
+ snd_ice1712_restore_gpio_status(ice);
+ return 0;
+}
+
+static int aureon_cs8415_mute_put (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char oval, nval;
+ int change;
+ snd_ice1712_save_gpio_status(ice);
+ oval = aureon_cs8415_get(ice, CS8415_CTRL1);
+ if (ucontrol->value.integer.value[0])
+ nval = oval & ~0x20;
+ else
+ nval = oval | 0x20;
+ if ((change = (oval != nval)))
+ aureon_cs8415_put(ice, CS8415_CTRL1, nval);
+ snd_ice1712_restore_gpio_status(ice);
+ return change;
+}
+
+/*
+ * CS8415A Q-Sub info
+ */
+static int aureon_cs8415_qsub_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 10;
+ return 0;
+}
+
+static int aureon_cs8415_qsub_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ snd_ice1712_save_gpio_status(ice);
+ aureon_cs8415_read(ice, CS8415_QSUB, ucontrol->value.bytes.data, 10);
+ snd_ice1712_restore_gpio_status(ice);
+
+ return 0;
+}
+
+static int aureon_cs8415_spdif_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int aureon_cs8415_mask_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+ memset(ucontrol->value.iec958.status, 0xFF, 24);
+ return 0;
+}
+
+static int aureon_cs8415_spdif_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ snd_ice1712_save_gpio_status(ice);
+ aureon_cs8415_read(ice, CS8415_C_BUFFER, ucontrol->value.iec958.status, 24);
+ snd_ice1712_restore_gpio_status(ice);
+ return 0;
+}
+
+/*
+ * Headphone Amplifier
+ */
+static int aureon_set_headphone_amp(ice1712_t *ice, int enable)
+{
+ unsigned int tmp, tmp2;
+
+ tmp2 = tmp = snd_ice1712_gpio_read(ice);
+ if (enable)
+ tmp |= AUREON_HP_SEL;
+ else
+ tmp &= ~ AUREON_HP_SEL;
+ if (tmp != tmp2) {
+ snd_ice1712_gpio_write(ice, tmp);
+ return 1;
+ }
+ return 0;
+}
+
+static int aureon_get_headphone_amp(ice1712_t *ice)
+{
+ unsigned int tmp = snd_ice1712_gpio_read(ice);
+
+ return ( tmp & AUREON_HP_SEL )!= 0;
+}
+
+#define aureon_hpamp_info aureon_mono_bool_info
+
+static int aureon_hpamp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
+ return 0;
+}
+
+
+static int aureon_hpamp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
+}
+
+/*
+ * Deemphasis
+ */
+
+#define aureon_deemp_info aureon_mono_bool_info
+
+static int aureon_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
+ return 0;
+}
+
+static int aureon_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int temp, temp2;
+ temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
+ if (ucontrol->value.integer.value[0])
+ temp |= 0xf;
+ else
+ temp &= ~0xf;
+ if (temp != temp2) {
+ wm_put(ice, WM_DAC_CTRL2, temp);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * ADC Oversampling
+ */
+static int aureon_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[2] = { "128x", "64x" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int aureon_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
+ return 0;
+}
+
+static int aureon_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ int temp, temp2;
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ temp2 = temp = wm_get(ice, WM_MASTER);
+
+ if (ucontrol->value.enumerated.item[0])
+ temp |= 0x8;
+ else
+ temp &= ~0x8;
+
+ if (temp != temp2) {
+ wm_put(ice, WM_MASTER, temp);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * mixers
+ */
+
+static snd_kcontrol_new_t aureon_dac_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = wm_master_mute_info,
+ .get = wm_master_mute_get,
+ .put = wm_master_mute_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .info = wm_master_vol_info,
+ .get = wm_master_vol_get,
+ .put = wm_master_vol_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Playback Switch",
+ .info = wm_mute_info,
+ .get = wm_mute_get,
+ .put = wm_mute_put,
+ .private_value = (2 << 8) | 0
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Playback Volume",
+ .info = wm_vol_info,
+ .get = wm_vol_get,
+ .put = wm_vol_put,
+ .private_value = (2 << 8) | 0
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Rear Playback Switch",
+ .info = wm_mute_info,
+ .get = wm_mute_get,
+ .put = wm_mute_put,
+ .private_value = (2 << 8) | 2
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Rear Playback Volume",
+ .info = wm_vol_info,
+ .get = wm_vol_get,
+ .put = wm_vol_put,
+ .private_value = (2 << 8) | 2
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Center Playback Switch",
+ .info = wm_mute_info,
+ .get = wm_mute_get,
+ .put = wm_mute_put,
+ .private_value = (1 << 8) | 4
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Center Playback Volume",
+ .info = wm_vol_info,
+ .get = wm_vol_get,
+ .put = wm_vol_put,
+ .private_value = (1 << 8) | 4
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "LFE Playback Switch",
+ .info = wm_mute_info,
+ .get = wm_mute_get,
+ .put = wm_mute_put,
+ .private_value = (1 << 8) | 5
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "LFE Playback Volume",
+ .info = wm_vol_info,
+ .get = wm_vol_get,
+ .put = wm_vol_put,
+ .private_value = (1 << 8) | 5
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Side Playback Switch",
+ .info = wm_mute_info,
+ .get = wm_mute_get,
+ .put = wm_mute_put,
+ .private_value = (2 << 8) | 6
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Side Playback Volume",
+ .info = wm_vol_info,
+ .get = wm_vol_get,
+ .put = wm_vol_put,
+ .private_value = (2 << 8) | 6
+ }
+};
+
+static snd_kcontrol_new_t wm_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .info = wm_pcm_mute_info,
+ .get = wm_pcm_mute_get,
+ .put = wm_pcm_mute_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .info = wm_pcm_vol_info,
+ .get = wm_pcm_vol_get,
+ .put = wm_pcm_vol_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Switch",
+ .info = wm_adc_mute_info,
+ .get = wm_adc_mute_get,
+ .put = wm_adc_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .info = wm_adc_vol_info,
+ .get = wm_adc_vol_get,
+ .put = wm_adc_vol_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = wm_adc_mux_info,
+ .get = wm_adc_mux_get,
+ .put = wm_adc_mux_put,
+ .private_value = 5
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "External Amplifier",
+ .info = aureon_hpamp_info,
+ .get = aureon_hpamp_get,
+ .put = aureon_hpamp_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Deemphasis Switch",
+ .info = aureon_deemp_info,
+ .get = aureon_deemp_get,
+ .put = aureon_deemp_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Oversampling",
+ .info = aureon_oversampling_info,
+ .get = aureon_oversampling_get,
+ .put = aureon_oversampling_put
+ }
+};
+
+static snd_kcontrol_new_t ac97_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "AC97 Playback Switch",
+ .info = aureon_ac97_mmute_info,
+ .get = aureon_ac97_mmute_get,
+ .put = aureon_ac97_mmute_put,
+ .private_value = AC97_MASTER
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "AC97 Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MASTER|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_CD
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_CD|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Aux Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_AUX,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Aux Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_AUX|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_LINE
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_LINE|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_MIC
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MIC
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Boost (+20dB)",
+ .info = aureon_ac97_micboost_info,
+ .get = aureon_ac97_micboost_get,
+ .put = aureon_ac97_micboost_put
+ }
+};
+
+static snd_kcontrol_new_t universe_ac97_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "AC97 Playback Switch",
+ .info = aureon_ac97_mmute_info,
+ .get = aureon_ac97_mmute_get,
+ .put = aureon_ac97_mmute_put,
+ .private_value = AC97_MASTER
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "AC97 Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MASTER|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_AUX
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_AUX|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Phono Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_CD,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Phono Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_CD|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_LINE
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_LINE|AUREON_AC97_STEREO
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_MIC
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MIC
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Boost (+20dB)",
+ .info = aureon_ac97_micboost_info,
+ .get = aureon_ac97_micboost_get,
+ .put = aureon_ac97_micboost_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Aux Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_VIDEO,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Aux Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_VIDEO|AUREON_AC97_STEREO
+ }
+};
+
+
+static snd_kcontrol_new_t cs8415_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+ .info = aureon_cs8415_mute_info,
+ .get = aureon_cs8415_mute_get,
+ .put = aureon_cs8415_mute_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Source",
+ .info = aureon_cs8415_mux_info,
+ .get = aureon_cs8415_mux_get,
+ .put = aureon_cs8415_mux_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("Q-subcode ",CAPTURE,DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = aureon_cs8415_qsub_info,
+ .get = aureon_cs8415_qsub_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = aureon_cs8415_spdif_info,
+ .get = aureon_cs8415_mask_get
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = aureon_cs8415_spdif_info,
+ .get = aureon_cs8415_spdif_get
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Rate",
+ .access =SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = aureon_cs8415_rate_info,
+ .get = aureon_cs8415_rate_get
+ }
+};
+
+
+static int __devinit aureon_add_controls(ice1712_t *ice)
+{
+ unsigned int i, counts;
+ int err;
+
+ counts = ARRAY_SIZE(aureon_dac_controls);
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY)
+ counts -= 2; /* no side */
+ for (i = 0; i < counts; i++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&aureon_dac_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
+ for (i = 0; i < ARRAY_SIZE(universe_ac97_controls); i++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&universe_ac97_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+ }
+ else {
+ for (i = 0; i < ARRAY_SIZE(ac97_controls); i++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&ac97_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ {
+ unsigned char id;
+ snd_ice1712_save_gpio_status(ice);
+ id = aureon_cs8415_get(ice, CS8415_ID);
+ if (id != 0x41)
+ snd_printk("No CS8415 chip. Skipping CS8415 controls.\n");
+ else if ((id & 0x0F) != 0x01)
+ snd_printk("Detected unsupported CS8415 rev. (%c)\n", (char)((id & 0x0F) + 'A' - 1));
+ else {
+ for (i = 0; i< ARRAY_SIZE(cs8415_controls); i++) {
+ snd_kcontrol_t *kctl;
+ err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice)));
+ if (err < 0)
+ return err;
+ if (i > 1)
+ kctl->id.device = ice->pcm->device;
+ }
+ }
+ snd_ice1712_restore_gpio_status(ice);
+ }
+
+ return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit aureon_init(ice1712_t *ice)
+{
+ static unsigned short wm_inits_aureon[] = {
+ /* These come first to reduce init pop noise */
+ 0x1b, 0x044, /* ADC Mux (AC'97 source) */
+ 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
+ 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
+
+ 0x18, 0x000, /* All power-up */
+
+ 0x16, 0x122, /* I2S, normal polarity, 24bit */
+ 0x17, 0x022, /* 256fs, slave mode */
+ 0x00, 0, /* DAC1 analog mute */
+ 0x01, 0, /* DAC2 analog mute */
+ 0x02, 0, /* DAC3 analog mute */
+ 0x03, 0, /* DAC4 analog mute */
+ 0x04, 0, /* DAC5 analog mute */
+ 0x05, 0, /* DAC6 analog mute */
+ 0x06, 0, /* DAC7 analog mute */
+ 0x07, 0, /* DAC8 analog mute */
+ 0x08, 0x100, /* master analog mute */
+ 0x09, 0xff, /* DAC1 digital full */
+ 0x0a, 0xff, /* DAC2 digital full */
+ 0x0b, 0xff, /* DAC3 digital full */
+ 0x0c, 0xff, /* DAC4 digital full */
+ 0x0d, 0xff, /* DAC5 digital full */
+ 0x0e, 0xff, /* DAC6 digital full */
+ 0x0f, 0xff, /* DAC7 digital full */
+ 0x10, 0xff, /* DAC8 digital full */
+ 0x11, 0x1ff, /* master digital full */
+ 0x12, 0x000, /* phase normal */
+ 0x13, 0x090, /* unmute DAC L/R */
+ 0x14, 0x000, /* all unmute */
+ 0x15, 0x000, /* no deemphasis, no ZFLG */
+ 0x19, 0x000, /* -12dB ADC/L */
+ 0x1a, 0x000, /* -12dB ADC/R */
+ (unsigned short)-1
+ };
+ static unsigned short wm_inits_prodigy[] = {
+
+ /* These come first to reduce init pop noise */
+ 0x1b, 0x000, /* ADC Mux */
+ 0x1c, 0x009, /* Out Mux1 */
+ 0x1d, 0x009, /* Out Mux2 */
+
+ 0x18, 0x000, /* All power-up */
+
+ 0x16, 0x022, /* I2S, normal polarity, 24bit, high-pass on */
+ 0x17, 0x006, /* 128fs, slave mode */
+
+ 0x00, 0, /* DAC1 analog mute */
+ 0x01, 0, /* DAC2 analog mute */
+ 0x02, 0, /* DAC3 analog mute */
+ 0x03, 0, /* DAC4 analog mute */
+ 0x04, 0, /* DAC5 analog mute */
+ 0x05, 0, /* DAC6 analog mute */
+ 0x06, 0, /* DAC7 analog mute */
+ 0x07, 0, /* DAC8 analog mute */
+ 0x08, 0x100, /* master analog mute */
+
+ 0x09, 0x7f, /* DAC1 digital full */
+ 0x0a, 0x7f, /* DAC2 digital full */
+ 0x0b, 0x7f, /* DAC3 digital full */
+ 0x0c, 0x7f, /* DAC4 digital full */
+ 0x0d, 0x7f, /* DAC5 digital full */
+ 0x0e, 0x7f, /* DAC6 digital full */
+ 0x0f, 0x7f, /* DAC7 digital full */
+ 0x10, 0x7f, /* DAC8 digital full */
+ 0x11, 0x1FF, /* master digital full */
+
+ 0x12, 0x000, /* phase normal */
+ 0x13, 0x090, /* unmute DAC L/R */
+ 0x14, 0x000, /* all unmute */
+ 0x15, 0x000, /* no deemphasis, no ZFLG */
+
+ 0x19, 0x000, /* -12dB ADC/L */
+ 0x1a, 0x000, /* -12dB ADC/R */
+ (unsigned short)-1
+
+ };
+ static unsigned short cs_inits[] = {
+ 0x0441, /* RUN */
+ 0x0180, /* no mute, OMCK output on RMCK pin */
+ 0x0201, /* S/PDIF source on RXP1 */
+ 0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */
+ (unsigned short)-1
+ };
+ unsigned int tmp;
+ unsigned short *p;
+ int err, i;
+
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
+ ice->num_total_dacs = 6;
+ ice->num_total_adcs = 2;
+ } else {
+ /* aureon 7.1 and prodigy 7.1 */
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 2;
+ }
+
+ /* to remeber the register values of CS8415 */
+ ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ice->akm)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+
+ if ((err = aureon_ac97_init(ice)) != 0)
+ return err;
+
+ snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
+
+ /* reset the wm codec as the SPI mode */
+ snd_ice1712_save_gpio_status(ice);
+ snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RESET|AUREON_WM_CS|AUREON_CS8415_CS|AUREON_HP_SEL));
+
+ tmp = snd_ice1712_gpio_read(ice);
+ tmp &= ~AUREON_WM_RESET;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ tmp |= AUREON_WM_CS | AUREON_CS8415_CS;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+ tmp |= AUREON_WM_RESET;
+ snd_ice1712_gpio_write(ice, tmp);
+ udelay(1);
+
+ /* initialize WM8770 codec */
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
+ p = wm_inits_prodigy;
+ else
+ p = wm_inits_aureon;
+ for (; *p != (unsigned short)-1; p += 2)
+ wm_put(ice, p[0], p[1]);
+
+ /* initialize CS8415A codec */
+ for (p = cs_inits; *p != (unsigned short)-1; p++)
+ aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24);
+ ice->spec.aureon.cs8415_mux = 1;
+
+ aureon_set_headphone_amp(ice, 1);
+
+ snd_ice1712_restore_gpio_status(ice);
+
+ ice->spec.aureon.master[0] = WM_VOL_MUTE;
+ ice->spec.aureon.master[1] = WM_VOL_MUTE;
+ for (i = 0; i < ice->num_total_dacs; i++) {
+ ice->spec.aureon.vol[i] = WM_VOL_MUTE;
+ wm_set_vol(ice, i, ice->spec.aureon.vol[i], ice->spec.aureon.master[i % 2]);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Aureon boards don't provide the EEPROM data except for the vendor IDs.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char aureon51_eeprom[] __devinitdata = {
+ 0x0a, /* SYSCONF: clock 512, spdif-in/ADC, 3DACs */
+ 0x80, /* ACLINK: I2S */
+ 0xfc, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x5f, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+static unsigned char aureon71_eeprom[] __devinitdata = {
+ 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+ 0x80, /* ACLINK: I2S */
+ 0xfc, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x5f, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+static unsigned char prodigy71_eeprom[] __devinitdata = {
+ 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+ 0x80, /* ACLINK: I2S */
+ 0xfc, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x5f, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_AUREON51_SKY,
+ .name = "Terratec Aureon 5.1-Sky",
+ .model = "aureon51",
+ .chip_init = aureon_init,
+ .build_controls = aureon_add_controls,
+ .eeprom_size = sizeof(aureon51_eeprom),
+ .eeprom_data = aureon51_eeprom,
+ .driver = "Aureon51",
+ },
+ {
+ .subvendor = VT1724_SUBDEVICE_AUREON71_SPACE,
+ .name = "Terratec Aureon 7.1-Space",
+ .model = "aureon71",
+ .chip_init = aureon_init,
+ .build_controls = aureon_add_controls,
+ .eeprom_size = sizeof(aureon71_eeprom),
+ .eeprom_data = aureon71_eeprom,
+ .driver = "Aureon71",
+ },
+ {
+ .subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE,
+ .name = "Terratec Aureon 7.1-Universe",
+ .model = "universe",
+ .chip_init = aureon_init,
+ .build_controls = aureon_add_controls,
+ .eeprom_size = sizeof(aureon71_eeprom),
+ .eeprom_data = aureon71_eeprom,
+ .driver = "Aureon71Universe",
+ },
+ {
+ .subvendor = VT1724_SUBDEVICE_PRODIGY71,
+ .name = "Audiotrak Prodigy 7.1",
+ .model = "prodigy71",
+ .chip_init = aureon_init,
+ .build_controls = aureon_add_controls,
+ .eeprom_size = sizeof(prodigy71_eeprom),
+ .eeprom_data = prodigy71_eeprom,
+ .driver = "Prodigy71", /* should be identical with Aureon71 */
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h
new file mode 100644
index 00000000000..95d515f36f2
--- /dev/null
+++ b/sound/pci/ice1712/aureon.h
@@ -0,0 +1,56 @@
+#ifndef __SOUND_AUREON_H
+#define __SOUND_AUREON_H
+
+/*
+ * ALSA driver for VIA VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Terratec Aureon cards
+ *
+ * Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define AUREON_DEVICE_DESC "{Terratec,Aureon 5.1 Sky},"\
+ "{Terratec,Aureon 7.1 Space},"\
+ "{Terratec,Aureon 7.1 Universe}," \
+ "{AudioTrak,Prodigy 7.1},"
+
+#define VT1724_SUBDEVICE_AUREON51_SKY 0x3b154711 /* Aureon 5.1 Sky */
+#define VT1724_SUBDEVICE_AUREON71_SPACE 0x3b154511 /* Aureon 7.1 Space */
+#define VT1724_SUBDEVICE_AUREON71_UNIVERSE 0x3b155311 /* Aureon 7.1 Universe */
+#define VT1724_SUBDEVICE_PRODIGY71 0x33495345 /* PRODIGY 7.1 */
+
+extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[];
+
+/* GPIO bits */
+#define AUREON_CS8415_CS (1 << 22)
+#define AUREON_SPI_MISO (1 << 21)
+#define AUREON_WM_RESET (1 << 20)
+#define AUREON_SPI_CLK (1 << 19)
+#define AUREON_SPI_MOSI (1 << 18)
+#define AUREON_WM_RW (1 << 17)
+#define AUREON_AC97_RESET (1 << 16)
+#define AUREON_DIGITAL_SEL1 (1 << 15)
+#define AUREON_HP_SEL (1 << 14)
+#define AUREON_WM_CS (1 << 12)
+#define AUREON_AC97_COMMIT (1 << 11)
+#define AUREON_AC97_ADDR (1 << 10)
+#define AUREON_AC97_DATA_LOW (1 << 9)
+#define AUREON_AC97_DATA_HIGH (1 << 8)
+#define AUREON_AC97_DATA_MASK 0xFF
+
+#endif /* __SOUND_AUREON_H */
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
new file mode 100644
index 00000000000..eb20f73be61
--- /dev/null
+++ b/sound/pci/ice1712/delta.c
@@ -0,0 +1,771 @@
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile
+ * Digigram VX442
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/cs8427.h>
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+#include "delta.h"
+
+#define SND_CS8403
+#include <sound/cs8403.h>
+
+
+/*
+ * CS8427 via SPI mode (for Audiophile), emulated I2C
+ */
+
+/* send 8 bits */
+static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp)
+{
+ int idx;
+
+ for (idx = 7; idx >= 0; idx--) {
+ tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK);
+ if (data & (1 << idx))
+ tmp |= ICE1712_DELTA_AP_DOUT;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
+ tmp |= ICE1712_DELTA_AP_CCLK;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
+ }
+}
+
+/* read 8 bits */
+static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp)
+{
+ unsigned char data = 0;
+ int idx;
+
+ for (idx = 7; idx >= 0; idx--) {
+ tmp &= ~ICE1712_DELTA_AP_CCLK;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
+ if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN)
+ data |= 1 << idx;
+ tmp |= ICE1712_DELTA_AP_CCLK;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
+ }
+ return data;
+}
+
+/* assert chip select */
+static unsigned char ap_cs8427_codec_select(ice1712_t *ice)
+{
+ unsigned char tmp;
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ tmp &= ~ICE1712_DELTA_1010LT_CS;
+ tmp |= ICE1712_DELTA_1010LT_CCLK | ICE1712_DELTA_1010LT_CS_CS8427;
+ break;
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ case ICE1712_SUBDEVICE_DELTA410:
+ tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
+ tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
+ break;
+ case ICE1712_SUBDEVICE_VX442:
+ tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
+ tmp &= ~ICE1712_VX442_CS_DIGITAL;
+ break;
+ }
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
+ return tmp;
+}
+
+/* deassert chip select */
+static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp)
+{
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ tmp &= ~ICE1712_DELTA_1010LT_CS;
+ tmp |= ICE1712_DELTA_1010LT_CS_NONE;
+ break;
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ case ICE1712_SUBDEVICE_DELTA410:
+ tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
+ break;
+ case ICE1712_SUBDEVICE_VX442:
+ tmp |= ICE1712_VX442_CS_DIGITAL;
+ break;
+ }
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+}
+
+/* sequential write */
+static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+ ice1712_t *ice = device->bus->private_data;
+ int res = count;
+ unsigned char tmp;
+
+ down(&ice->gpio_mutex);
+ tmp = ap_cs8427_codec_select(ice);
+ ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */
+ while (count-- > 0)
+ ap_cs8427_write_byte(ice, *bytes++, tmp);
+ ap_cs8427_codec_deassert(ice, tmp);
+ up(&ice->gpio_mutex);
+ return res;
+}
+
+/* sequential read */
+static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+ ice1712_t *ice = device->bus->private_data;
+ int res = count;
+ unsigned char tmp;
+
+ down(&ice->gpio_mutex);
+ tmp = ap_cs8427_codec_select(ice);
+ ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */
+ while (count-- > 0)
+ *bytes++ = ap_cs8427_read_byte(ice, tmp);
+ ap_cs8427_codec_deassert(ice, tmp);
+ up(&ice->gpio_mutex);
+ return res;
+}
+
+static int ap_cs8427_probeaddr(snd_i2c_bus_t *bus, unsigned short addr)
+{
+ if (addr == 0x10)
+ return 1;
+ return -ENOENT;
+}
+
+static snd_i2c_ops_t ap_cs8427_i2c_ops = {
+ .sendbytes = ap_cs8427_sendbytes,
+ .readbytes = ap_cs8427_readbytes,
+ .probeaddr = ap_cs8427_probeaddr,
+};
+
+/*
+ */
+
+static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+ unsigned char tmp, mask1, mask2;
+ int idx;
+ /* send byte to transmitter */
+ mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
+ mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
+ down(&ice->gpio_mutex);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ for (idx = 7; idx >= 0; idx--) {
+ tmp &= ~(mask1 | mask2);
+ if (bits & (1 << idx))
+ tmp |= mask2;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(100);
+ tmp |= mask1;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(100);
+ }
+ tmp &= ~mask1;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ up(&ice->gpio_mutex);
+}
+
+
+static void delta_spdif_default_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_bits);
+}
+
+static int delta_spdif_default_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned int val;
+ int change;
+
+ val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
+ spin_lock_irq(&ice->reg_lock);
+ change = ice->spdif.cs8403_bits != val;
+ ice->spdif.cs8403_bits = val;
+ if (change && ice->playback_pro_substream == NULL) {
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ice1712_delta_cs8403_spdif_write(ice, val);
+ } else {
+ spin_unlock_irq(&ice->reg_lock);
+ }
+ return change;
+}
+
+static void delta_spdif_stream_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_stream_bits);
+}
+
+static int delta_spdif_stream_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned int val;
+ int change;
+
+ val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
+ spin_lock_irq(&ice->reg_lock);
+ change = ice->spdif.cs8403_stream_bits != val;
+ ice->spdif.cs8403_stream_bits = val;
+ if (change && ice->playback_pro_substream != NULL) {
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ice1712_delta_cs8403_spdif_write(ice, val);
+ } else {
+ spin_unlock_irq(&ice->reg_lock);
+ }
+ return change;
+}
+
+
+/*
+ * AK4524 on Delta 44 and 66 to choose the chip mask
+ */
+static void delta_ak4524_lock(akm4xxx_t *ak, int chip)
+{
+ struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+ ice1712_t *ice = ak->private_data[0];
+
+ snd_ice1712_save_gpio_status(ice);
+ priv->cs_mask =
+ priv->cs_addr = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A :
+ ICE1712_DELTA_CODEC_CHIP_B;
+}
+
+/*
+ * AK4524 on Delta1010LT to choose the chip address
+ */
+static void delta1010lt_ak4524_lock(akm4xxx_t *ak, int chip)
+{
+ struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+ ice1712_t *ice = ak->private_data[0];
+
+ snd_ice1712_save_gpio_status(ice);
+ priv->cs_mask = ICE1712_DELTA_1010LT_CS;
+ priv->cs_addr = chip << 4;
+}
+
+/*
+ * AK4528 on VX442 to choose the chip mask
+ */
+static void vx442_ak4524_lock(akm4xxx_t *ak, int chip)
+{
+ struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+ ice1712_t *ice = ak->private_data[0];
+
+ snd_ice1712_save_gpio_status(ice);
+ priv->cs_mask =
+ priv->cs_addr = chip == 0 ? ICE1712_VX442_CODEC_CHIP_A :
+ ICE1712_VX442_CODEC_CHIP_B;
+}
+
+/*
+ * change the DFS bit according rate for Delta1010
+ */
+static void delta_1010_set_rate_val(ice1712_t *ice, unsigned int rate)
+{
+ unsigned char tmp, tmp2;
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+
+ down(&ice->gpio_mutex);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ tmp2 = tmp & ~ICE1712_DELTA_DFS;
+ if (rate > 48000)
+ tmp2 |= ICE1712_DELTA_DFS;
+ if (tmp != tmp2)
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp2);
+ up(&ice->gpio_mutex);
+}
+
+/*
+ * change the rate of AK4524 on Delta 44/66, AP, 1010LT
+ */
+static void delta_ak4524_set_rate_val(akm4xxx_t *ak, unsigned int rate)
+{
+ unsigned char tmp, tmp2;
+ ice1712_t *ice = ak->private_data[0];
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+
+ /* check before reset ak4524 to avoid unnecessary clicks */
+ down(&ice->gpio_mutex);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ up(&ice->gpio_mutex);
+ tmp2 = tmp & ~ICE1712_DELTA_DFS;
+ if (rate > 48000)
+ tmp2 |= ICE1712_DELTA_DFS;
+ if (tmp == tmp2)
+ return;
+
+ /* do it again */
+ snd_akm4xxx_reset(ak, 1);
+ down(&ice->gpio_mutex);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS;
+ if (rate > 48000)
+ tmp |= ICE1712_DELTA_DFS;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ up(&ice->gpio_mutex);
+ snd_akm4xxx_reset(ak, 0);
+}
+
+/*
+ * change the rate of AK4524 on VX442
+ */
+static void vx442_ak4524_set_rate_val(akm4xxx_t *ak, unsigned int rate)
+{
+ unsigned char val;
+
+ val = (rate > 48000) ? 0x65 : 0x60;
+ if (snd_akm4xxx_get(ak, 0, 0x02) != val ||
+ snd_akm4xxx_get(ak, 1, 0x02) != val) {
+ snd_akm4xxx_reset(ak, 1);
+ snd_akm4xxx_write(ak, 0, 0x02, val);
+ snd_akm4xxx_write(ak, 1, 0x02, val);
+ snd_akm4xxx_reset(ak, 0);
+ }
+}
+
+
+/*
+ * SPDIF ops for Delta 1010, Dio, 66
+ */
+
+/* open callback */
+static void delta_open_spdif(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+ ice->spdif.cs8403_stream_bits = ice->spdif.cs8403_bits;
+}
+
+/* set up */
+static void delta_setup_spdif(ice1712_t *ice, int rate)
+{
+ unsigned long flags;
+ unsigned int tmp;
+ int change;
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ tmp = ice->spdif.cs8403_stream_bits;
+ if (tmp & 0x01) /* consumer */
+ tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
+ switch (rate) {
+ case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
+ case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
+ case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
+ default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+ }
+ change = ice->spdif.cs8403_stream_bits != tmp;
+ ice->spdif.cs8403_stream_bits = tmp;
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ if (change)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
+ snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
+}
+
+
+/*
+ * initialize the chips on M-Audio cards
+ */
+
+static akm4xxx_t akm_audiophile __devinitdata = {
+ .type = SND_AK4528,
+ .num_adcs = 2,
+ .num_dacs = 2,
+ .ops = {
+ .set_rate_val = delta_ak4524_set_rate_val
+ }
+};
+
+static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 0,
+ .data_mask = ICE1712_DELTA_AP_DOUT,
+ .clk_mask = ICE1712_DELTA_AP_CCLK,
+ .cs_mask = ICE1712_DELTA_AP_CS_CODEC,
+ .cs_addr = ICE1712_DELTA_AP_CS_CODEC,
+ .cs_none = 0,
+ .add_flags = ICE1712_DELTA_AP_CS_DIGITAL,
+ .mask_flags = 0,
+};
+
+static akm4xxx_t akm_delta410 __devinitdata = {
+ .type = SND_AK4529,
+ .num_adcs = 2,
+ .num_dacs = 8,
+ .ops = {
+ .set_rate_val = delta_ak4524_set_rate_val
+ }
+};
+
+static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
+ .caddr = 0,
+ .cif = 0,
+ .data_mask = ICE1712_DELTA_AP_DOUT,
+ .clk_mask = ICE1712_DELTA_AP_CCLK,
+ .cs_mask = ICE1712_DELTA_AP_CS_CODEC,
+ .cs_addr = ICE1712_DELTA_AP_CS_CODEC,
+ .cs_none = 0,
+ .add_flags = ICE1712_DELTA_AP_CS_DIGITAL,
+ .mask_flags = 0,
+};
+
+static akm4xxx_t akm_delta1010lt __devinitdata = {
+ .type = SND_AK4524,
+ .num_adcs = 8,
+ .num_dacs = 8,
+ .ops = {
+ .lock = delta1010lt_ak4524_lock,
+ .set_rate_val = delta_ak4524_set_rate_val
+ }
+};
+
+static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 0, /* the default level of the CIF pin from AK4524 */
+ .data_mask = ICE1712_DELTA_1010LT_DOUT,
+ .clk_mask = ICE1712_DELTA_1010LT_CCLK,
+ .cs_mask = 0,
+ .cs_addr = 0, /* set later */
+ .cs_none = ICE1712_DELTA_1010LT_CS_NONE,
+ .add_flags = 0,
+ .mask_flags = 0,
+};
+
+static akm4xxx_t akm_delta44 __devinitdata = {
+ .type = SND_AK4524,
+ .num_adcs = 4,
+ .num_dacs = 4,
+ .ops = {
+ .lock = delta_ak4524_lock,
+ .set_rate_val = delta_ak4524_set_rate_val
+ }
+};
+
+static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 0, /* the default level of the CIF pin from AK4524 */
+ .data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA,
+ .clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK,
+ .cs_mask = 0,
+ .cs_addr = 0, /* set later */
+ .cs_none = 0,
+ .add_flags = 0,
+ .mask_flags = 0,
+};
+
+static akm4xxx_t akm_vx442 __devinitdata = {
+ .type = SND_AK4524,
+ .num_adcs = 4,
+ .num_dacs = 4,
+ .ops = {
+ .lock = vx442_ak4524_lock,
+ .set_rate_val = vx442_ak4524_set_rate_val
+ }
+};
+
+static struct snd_ak4xxx_private akm_vx442_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 0,
+ .data_mask = ICE1712_VX442_DOUT,
+ .clk_mask = ICE1712_VX442_CCLK,
+ .cs_mask = 0,
+ .cs_addr = 0, /* set later */
+ .cs_none = 0,
+ .add_flags = 0,
+ .mask_flags = 0,
+};
+
+static int __devinit snd_ice1712_delta_init(ice1712_t *ice)
+{
+ int err;
+ akm4xxx_t *ak;
+
+ /* determine I2C, DACs and ADCs */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 2;
+ break;
+ case ICE1712_SUBDEVICE_DELTA410:
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 2;
+ break;
+ case ICE1712_SUBDEVICE_DELTA44:
+ case ICE1712_SUBDEVICE_DELTA66:
+ ice->num_total_dacs = ice->omni ? 8 : 4;
+ ice->num_total_adcs = ice->omni ? 8 : 4;
+ break;
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 8;
+ break;
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ ice->num_total_dacs = 4; /* two AK4324 codecs */
+ break;
+ case ICE1712_SUBDEVICE_VX442:
+ ice->num_total_dacs = 4;
+ ice->num_total_adcs = 4;
+ break;
+ }
+
+ /* initialize spdif */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ case ICE1712_SUBDEVICE_DELTA410:
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ case ICE1712_SUBDEVICE_VX442:
+ if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
+ snd_printk("unable to create I2C bus\n");
+ return err;
+ }
+ ice->i2c->private_data = ice;
+ ice->i2c->ops = &ap_cs8427_i2c_ops;
+ if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+ return err;
+ break;
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ ice->gpio.set_pro_rate = delta_1010_set_rate_val;
+ break;
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ ice->gpio.set_pro_rate = delta_1010_set_rate_val;
+ /* fall thru */
+ case ICE1712_SUBDEVICE_DELTA66:
+ ice->spdif.ops.open = delta_open_spdif;
+ ice->spdif.ops.setup_rate = delta_setup_spdif;
+ ice->spdif.ops.default_get = delta_spdif_default_get;
+ ice->spdif.ops.default_put = delta_spdif_default_put;
+ ice->spdif.ops.stream_get = delta_spdif_stream_get;
+ ice->spdif.ops.stream_put = delta_spdif_stream_put;
+ /* Set spdif defaults */
+ snd_ice1712_delta_cs8403_spdif_write(ice, ice->spdif.cs8403_bits);
+ break;
+ }
+
+ /* no analog? */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ return 0;
+ }
+
+ /* second stage of initialization, analog parts and others */
+ ak = ice->akm = kmalloc(sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ak)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_audiophile, &akm_audiophile_priv, ice);
+ break;
+ case ICE1712_SUBDEVICE_DELTA410:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice);
+ break;
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice);
+ break;
+ case ICE1712_SUBDEVICE_DELTA66:
+ case ICE1712_SUBDEVICE_DELTA44:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
+ break;
+ case ICE1712_SUBDEVICE_VX442:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+
+/*
+ * additional controls for M-Audio cards
+ */
+
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
+static snd_kcontrol_new_t snd_ice1712_delta1010lt_wordclock_select __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 1, 0);
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
+static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+
+
+static int __devinit snd_ice1712_delta_add_controls(ice1712_t *ice)
+{
+ int err;
+
+ /* 1010 and dio specific controls */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice));
+ if (err < 0)
+ return err;
+ break;
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice));
+ if (err < 0)
+ return err;
+ break;
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_select, ice));
+ if (err < 0)
+ return err;
+ break;
+ }
+
+ /* normal spdif controls */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ case ICE1712_SUBDEVICE_DELTA66:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ err = snd_ice1712_spdif_build_controls(ice);
+ if (err < 0)
+ return err;
+ break;
+ }
+
+ /* spdif status in */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ case ICE1712_SUBDEVICE_DELTA66:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice));
+ if (err < 0)
+ return err;
+ break;
+ }
+
+ /* ak4524 controls */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ case ICE1712_SUBDEVICE_DELTA410:
+ case ICE1712_SUBDEVICE_DELTA44:
+ case ICE1712_SUBDEVICE_DELTA66:
+ case ICE1712_SUBDEVICE_VX442:
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+ break;
+ }
+
+ return 0;
+}
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
+ {
+ .subvendor = ICE1712_SUBDEVICE_DELTA1010,
+ .name = "M Audio Delta 1010",
+ .model = "delta1010",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_DELTADIO2496,
+ .name = "M Audio Delta DiO 2496",
+ .model = "dio2496",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ .no_mpu401 = 1,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_DELTA66,
+ .name = "M Audio Delta 66",
+ .model = "delta66",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ .no_mpu401 = 1,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_DELTA44,
+ .name = "M Audio Delta 44",
+ .model = "delta44",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ .no_mpu401 = 1,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_AUDIOPHILE,
+ .name = "M Audio Audiophile 24/96",
+ .model = "audiophile",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_DELTA410,
+ .name = "M Audio Delta 410",
+ .model = "delta410",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_DELTA1010LT,
+ .name = "M Audio Delta 1010LT",
+ .model = "delta1010lt",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_VX442,
+ .name = "Digigram VX442",
+ .model = "vx442",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ .no_mpu401 = 1,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_MEDIASTATION,
+ .name = "Lionstracs Mediastation",
+ .model = "mediastation",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
new file mode 100644
index 00000000000..746ebde9452
--- /dev/null
+++ b/sound/pci/ice1712/delta.h
@@ -0,0 +1,150 @@
+#ifndef __SOUND_DELTA_H
+#define __SOUND_DELTA_H
+
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile
+ * Digigram VX442
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define DELTA_DEVICE_DESC \
+ "{MidiMan M Audio,Delta 1010},"\
+ "{MidiMan M Audio,Delta 1010LT},"\
+ "{MidiMan M Audio,Delta DiO 2496},"\
+ "{MidiMan M Audio,Delta 66},"\
+ "{MidiMan M Audio,Delta 44},"\
+ "{MidiMan M Audio,Audiophile 24/96},"\
+ "{Digigram,VX442},"\
+ "{Lionstracs,Mediastation},"
+
+#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6
+#define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6
+#define ICE1712_SUBDEVICE_DELTA66 0x121432d6
+#define ICE1712_SUBDEVICE_DELTA44 0x121433d6
+#define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6
+#define ICE1712_SUBDEVICE_DELTA410 0x121438d6
+#define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6
+#define ICE1712_SUBDEVICE_VX442 0x12143cd6
+#define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100
+
+/* entry point */
+extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
+
+
+/*
+ * MidiMan M-Audio Delta GPIO definitions
+ */
+
+/* MidiMan M-Audio Delta shared pins */
+#define ICE1712_DELTA_DFS 0x01 /* fast/slow sample rate mode */
+ /* (>48kHz must be 1) */
+#define ICE1712_DELTA_SPDIF_IN_STAT 0x02
+ /* S/PDIF input status */
+ /* 0 = valid signal is present */
+ /* all except Delta44 */
+ /* look to CS8414 datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04
+ /* S/PDIF output status clock */
+ /* (writting on rising edge - 0->1) */
+ /* all except Delta44 */
+ /* look to CS8404A datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08
+ /* S/PDIF output status data */
+ /* all except Delta44 */
+ /* look to CS8404A datasheet */
+/* MidiMan M-Audio DeltaDiO */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10
+ /* coaxial (0), optical (1) */
+ /* S/PDIF input select*/
+
+/* MidiMan M-Audio Delta1010 */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10
+ /* 1 - clock are taken from S/PDIF input */
+ /* 0 - clock are taken from Word Clock input */
+ /* affected SPMCLKIN pin of Envy24 */
+#define ICE1712_DELTA_WORD_CLOCK_STATUS 0x20
+ /* 0 = valid word clock signal is present */
+
+/* MidiMan M-Audio Delta66 */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10
+ /* AKM4524 serial data */
+#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20
+ /* AKM4524 serial clock */
+ /* (writting on rising edge - 0->1 */
+#define ICE1712_DELTA_CODEC_CHIP_A 0x40
+#define ICE1712_DELTA_CODEC_CHIP_B 0x80
+ /* 1 - select chip A or B */
+
+/* MidiMan M-Audio Delta44 */
+/* 0x01 = DFS */
+/* 0x10 = CODEC_SERIAL_DATA */
+/* 0x20 = CODEC_SERIAL_CLOCK */
+/* 0x40 = CODEC_CHIP_A */
+/* 0x80 = CODEC_CHIP_B */
+
+/* MidiMan M-Audio Audiophile/Delta410 definitions */
+/* thanks to Kristof Pelckmans <Kristof.Pelckmans@antwerpen.be> for Delta410 info */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_AP_CCLK 0x02 /* SPI clock */
+ /* (clocking on rising edge - 0->1) */
+#define ICE1712_DELTA_AP_DIN 0x04 /* data input */
+#define ICE1712_DELTA_AP_DOUT 0x08 /* data output */
+#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */
+ /* low signal = select */
+#define ICE1712_DELTA_AP_CS_CODEC 0x20 /* AK4528 (audiophile), AK4529 (Delta410) chip select */
+ /* low signal = select */
+
+/* MidiMan M-Audio Delta1010LT definitions */
+/* thanks to Anders Johansson <ajh@watri.uwa.edu.au> */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_1010LT_CCLK 0x02 /* SPI clock (AK4524 + CS8427) */
+#define ICE1712_DELTA_1010LT_DIN 0x04 /* data input (CS8427) */
+#define ICE1712_DELTA_1010LT_DOUT 0x08 /* data output (AK4524 + CS8427) */
+#define ICE1712_DELTA_1010LT_CS 0x70 /* mask for CS address */
+#define ICE1712_DELTA_1010LT_CS_CHIP_A 0x00 /* AK4524 #0 */
+#define ICE1712_DELTA_1010LT_CS_CHIP_B 0x10 /* AK4524 #1 */
+#define ICE1712_DELTA_1010LT_CS_CHIP_C 0x20 /* AK4524 #2 */
+#define ICE1712_DELTA_1010LT_CS_CHIP_D 0x30 /* AK4524 #3 */
+#define ICE1712_DELTA_1010LT_CS_CS8427 0x40 /* CS8427 */
+#define ICE1712_DELTA_1010LT_CS_NONE 0x50 /* nothing */
+#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
+
+/* Digigram VX442 definitions */
+#define ICE1712_VX442_CCLK 0x02 /* SPI clock */
+#define ICE1712_VX442_DIN 0x04 /* data input */
+#define ICE1712_VX442_DOUT 0x08 /* data output */
+#define ICE1712_VX442_CS_DIGITAL 0x10 /* chip select, low = CS8427 */
+#define ICE1712_VX442_CODEC_CHIP_A 0x20 /* select chip A */
+#define ICE1712_VX442_CODEC_CHIP_B 0x40 /* select chip B */
+
+#endif /* __SOUND_DELTA_H */
diff --git a/sound/pci/ice1712/envy24ht.h b/sound/pci/ice1712/envy24ht.h
new file mode 100644
index 00000000000..f7878020eaa
--- /dev/null
+++ b/sound/pci/ice1712/envy24ht.h
@@ -0,0 +1,215 @@
+#ifndef __SOUND_VT1724_H
+#define __SOUND_VT1724_H
+
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24)
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+#include <sound/rawmidi.h>
+#include <sound/i2c.h>
+#include <sound/pcm.h>
+
+#include "ice1712.h"
+
+enum {
+ ICE_EEP2_SYSCONF = 0, /* 06 */
+ ICE_EEP2_ACLINK, /* 07 */
+ ICE_EEP2_I2S, /* 08 */
+ ICE_EEP2_SPDIF, /* 09 */
+ ICE_EEP2_GPIO_DIR, /* 0a */
+ ICE_EEP2_GPIO_DIR1, /* 0b */
+ ICE_EEP2_GPIO_DIR2, /* 0c */
+ ICE_EEP2_GPIO_MASK, /* 0d */
+ ICE_EEP2_GPIO_MASK1, /* 0e */
+ ICE_EEP2_GPIO_MASK2, /* 0f */
+ ICE_EEP2_GPIO_STATE, /* 10 */
+ ICE_EEP2_GPIO_STATE1, /* 11 */
+ ICE_EEP2_GPIO_STATE2 /* 12 */
+};
+
+/*
+ * Direct registers
+ */
+
+#define ICEREG1724(ice, x) ((ice)->port + VT1724_REG_##x)
+
+#define VT1724_REG_CONTROL 0x00 /* byte */
+#define VT1724_RESET 0x80 /* reset whole chip */
+#define VT1724_REG_IRQMASK 0x01 /* byte */
+#define VT1724_IRQ_MPU_RX 0x80
+#define VT1724_IRQ_MPU_TX 0x20
+#define VT1724_IRQ_MTPCM 0x10
+#define VT1724_REG_IRQSTAT 0x02 /* byte */
+/* look to VT1724_IRQ_* */
+#define VT1724_REG_SYS_CFG 0x04 /* byte - system configuration PCI60 on Envy24*/
+#define VT1724_CFG_CLOCK 0xc0
+#define VT1724_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */
+#define VT1724_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */
+#define VT1724_CFG_MPU401 0x20 /* MPU401 UARTs */
+#define VT1724_CFG_ADC_MASK 0x0c /* one, two or one and S/PDIF, stereo ADCs */
+#define VT1724_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */
+
+#define VT1724_REG_AC97_CFG 0x05 /* byte */
+#define VT1724_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */
+#define VT1724_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */
+
+#define VT1724_REG_I2S_FEATURES 0x06 /* byte */
+#define VT1724_CFG_I2S_VOLUME 0x80 /* volume/mute capability */
+#define VT1724_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */
+#define VT1724_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */
+#define VT1724_CFG_I2S_192KHZ 0x08 /* supports 192kHz sampling */
+#define VT1724_CFG_I2S_OTHER 0x07 /* other I2S IDs */
+
+#define VT1724_REG_SPDIF_CFG 0x07 /* byte */
+#define VT1724_CFG_SPDIF_OUT_EN 0x80 /*Internal S/PDIF output is enabled*/
+#define VT1724_CFG_SPDIF_OUT_INT 0x40 /*Internal S/PDIF output is implemented*/
+#define VT1724_CFG_I2S_CHIPID 0x3c /* I2S chip ID */
+#define VT1724_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */
+#define VT1724_CFG_SPDIF_OUT 0x01 /* External S/PDIF output is present */
+
+/*there is no consumer AC97 codec with the VT1724*/
+//#define VT1724_REG_AC97_INDEX 0x08 /* byte */
+//#define VT1724_REG_AC97_CMD 0x09 /* byte */
+
+#define VT1724_REG_MPU_TXFIFO 0x0a /*byte ro. number of bytes in TX fifo*/
+#define VT1724_REG_MPU_RXFIFO 0x0b /*byte ro. number of bytes in RX fifo*/
+
+//are these 2 the wrong way around? they don't seem to be used yet anyway
+#define VT1724_REG_MPU_CTRL 0x0c /* byte */
+#define VT1724_REG_MPU_DATA 0x0d /* byte */
+
+#define VT1724_REG_MPU_FIFO_WM 0x0e /*byte set the high/low watermarks for RX/TX fifos*/
+#define VT1724_MPU_RX_FIFO 0x20 //1=rx fifo watermark 0=tx fifo watermark
+#define VT1724_MPU_FIFO_MASK 0x1f
+
+#define VT1724_REG_I2C_DEV_ADDR 0x10 /* byte */
+#define VT1724_I2C_WRITE 0x01 /* write direction */
+#define VT1724_REG_I2C_BYTE_ADDR 0x11 /* byte */
+#define VT1724_REG_I2C_DATA 0x12 /* byte */
+#define VT1724_REG_I2C_CTRL 0x13 /* byte */
+#define VT1724_I2C_EEPROM 0x80 /* 1 = EEPROM exists */
+#define VT1724_I2C_BUSY 0x01 /* busy bit */
+
+#define VT1724_REG_GPIO_DATA 0x14 /* word */
+#define VT1724_REG_GPIO_WRITE_MASK 0x16 /* word */
+#define VT1724_REG_GPIO_DIRECTION 0x18 /* dword? (3 bytes) 0=input 1=output.
+ bit3 - during reset used for Eeprom power-on strapping
+ if TESTEN# pin active, bit 2 always input*/
+#define VT1724_REG_POWERDOWN 0x1c
+#define VT1724_REG_GPIO_DATA_22 0x1e /* byte direction for GPIO 16:22 */
+#define VT1724_REG_GPIO_WRITE_MASK_22 0x1f /* byte write mask for GPIO 16:22 */
+
+
+/*
+ * Professional multi-track direct control registers
+ */
+
+#define ICEMT1724(ice, x) ((ice)->profi_port + VT1724_MT_##x)
+
+#define VT1724_MT_IRQ 0x00 /* byte - interrupt mask */
+#define VT1724_MULTI_PDMA4 0x80 /* SPDIF Out / PDMA4 */
+#define VT1724_MULTI_PDMA3 0x40 /* PDMA3 */
+#define VT1724_MULTI_PDMA2 0x20 /* PDMA2 */
+#define VT1724_MULTI_PDMA1 0x10 /* PDMA1 */
+#define VT1724_MULTI_FIFO_ERR 0x08 /* DMA FIFO underrun/overrun. */
+#define VT1724_MULTI_RDMA1 0x04 /* RDMA1 (S/PDIF input) */
+#define VT1724_MULTI_RDMA0 0x02 /* RMDA0 */
+#define VT1724_MULTI_PDMA0 0x01 /* MC Interleave/PDMA0 */
+
+#define VT1724_MT_RATE 0x01 /* byte - sampling rate select */
+#define VT1724_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */
+#define VT1724_MT_I2S_FORMAT 0x02 /* byte - I2S data format */
+#define VT1724_MT_I2S_MCLK_128X 0x08
+#define VT1724_MT_I2S_FORMAT_MASK 0x03
+#define VT1724_MT_I2S_FORMAT_I2S 0x00
+#define VT1724_MT_DMA_INT_MASK 0x03 /* byte -DMA Interrupt Mask */
+/* lool to VT1724_MULTI_* */
+#define VT1724_MT_AC97_INDEX 0x04 /* byte - AC'97 index */
+#define VT1724_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */
+#define VT1724_AC97_COLD 0x80 /* cold reset */
+#define VT1724_AC97_WARM 0x40 /* warm reset */
+#define VT1724_AC97_WRITE 0x20 /* W: write, R: write in progress */
+#define VT1724_AC97_READ 0x10 /* W: read, R: read in progress */
+#define VT1724_AC97_READY 0x08 /* codec ready status bit */
+#define VT1724_AC97_ID_MASK 0x03 /* codec id mask */
+#define VT1724_MT_AC97_DATA 0x06 /* word - AC'97 data */
+#define VT1724_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */
+#define VT1724_MT_PLAYBACK_SIZE 0x14 /* dword - playback size */
+#define VT1724_MT_DMA_CONTROL 0x18 /* byte - control */
+#define VT1724_PDMA4_START 0x80 /* SPDIF out / PDMA4 start */
+#define VT1724_PDMA3_START 0x40 /* PDMA3 start */
+#define VT1724_PDMA2_START 0x20 /* PDMA2 start */
+#define VT1724_PDMA1_START 0x10 /* PDMA1 start */
+#define VT1724_RDMA1_START 0x04 /* RDMA1 start */
+#define VT1724_RDMA0_START 0x02 /* RMDA0 start */
+#define VT1724_PDMA0_START 0x01 /* MC Interleave / PDMA0 start */
+#define VT1724_MT_BURST 0x19 /* Interleaved playback DMA Active streams / PCI burst size */
+#define VT1724_MT_DMA_FIFO_ERR 0x1a /*Global playback and record DMA FIFO Underrun/Overrun */
+#define VT1724_PDMA4_UNDERRUN 0x80
+#define VT1724_PDMA2_UNDERRUN 0x40
+#define VT1724_PDMA3_UNDERRUN 0x20
+#define VT1724_PDMA1_UNDERRUN 0x10
+#define VT1724_RDMA1_UNDERRUN 0x04
+#define VT1724_RDMA0_UNDERRUN 0x02
+#define VT1724_PDMA0_UNDERRUN 0x01
+#define VT1724_MT_DMA_PAUSE 0x1b /*Global playback and record DMA FIFO pause/resume */
+#define VT1724_PDMA4_PAUSE 0x80
+#define VT1724_PDMA3_PAUSE 0x40
+#define VT1724_PDMA2_PAUSE 0x20
+#define VT1724_PDMA1_PAUSE 0x10
+#define VT1724_RDMA1_PAUSE 0x04
+#define VT1724_RDMA0_PAUSE 0x02
+#define VT1724_PDMA0_PAUSE 0x01
+#define VT1724_MT_PLAYBACK_COUNT 0x1c /* word - playback count */
+#define VT1724_MT_CAPTURE_ADDR 0x20 /* dword - capture address */
+#define VT1724_MT_CAPTURE_SIZE 0x24 /* word - capture size */
+#define VT1724_MT_CAPTURE_COUNT 0x26 /* word - capture count */
+
+#define VT1724_MT_ROUTE_PLAYBACK 0x2c /* word */
+
+#define VT1724_MT_RDMA1_ADDR 0x30 /* dword - RDMA1 capture address */
+#define VT1724_MT_RDMA1_SIZE 0x34 /* word - RDMA1 capture size */
+#define VT1724_MT_RDMA1_COUNT 0x36 /* word - RDMA1 capture count */
+
+#define VT1724_MT_SPDIF_CTRL 0x3c /* word */
+#define VT1724_MT_MONITOR_PEAKINDEX 0x3e /* byte */
+#define VT1724_MT_MONITOR_PEAKDATA 0x3f /* byte */
+
+/* concurrent stereo channels */
+#define VT1724_MT_PDMA4_ADDR 0x40 /* dword */
+#define VT1724_MT_PDMA4_SIZE 0x44 /* word */
+#define VT1724_MT_PDMA4_COUNT 0x46 /* word */
+#define VT1724_MT_PDMA3_ADDR 0x50 /* dword */
+#define VT1724_MT_PDMA3_SIZE 0x54 /* word */
+#define VT1724_MT_PDMA3_COUNT 0x56 /* word */
+#define VT1724_MT_PDMA2_ADDR 0x60 /* dword */
+#define VT1724_MT_PDMA2_SIZE 0x64 /* word */
+#define VT1724_MT_PDMA2_COUNT 0x66 /* word */
+#define VT1724_MT_PDMA1_ADDR 0x70 /* dword */
+#define VT1724_MT_PDMA1_SIZE 0x74 /* word */
+#define VT1724_MT_PDMA1_COUNT 0x76 /* word */
+
+
+unsigned char snd_vt1724_read_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr);
+void snd_vt1724_write_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr, unsigned char data);
+
+#endif /* __SOUND_VT1724_H */
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
new file mode 100644
index 00000000000..e36efa1bdac
--- /dev/null
+++ b/sound/pci/ice1712/ews.c
@@ -0,0 +1,1036 @@
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for Terratec EWS88MT/D, EWX24/96, DMX 6Fire
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ * 2002 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/cs8427.h>
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+#include "ews.h"
+
+#define SND_CS8404
+#include <sound/cs8403.h>
+
+enum {
+ EWS_I2C_CS8404 = 0, EWS_I2C_PCF1, EWS_I2C_PCF2,
+ EWS_I2C_88D = 0,
+ EWS_I2C_6FIRE = 0
+};
+
+
+/*
+ * access via i2c mode (for EWX 24/96, EWS 88MT&D)
+ */
+
+/* send SDA and SCL */
+static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data)
+{
+ ice1712_t *ice = bus->private_data;
+ unsigned char tmp = 0;
+ if (clk)
+ tmp |= ICE1712_EWX2496_SERIAL_CLOCK;
+ if (data)
+ tmp |= ICE1712_EWX2496_SERIAL_DATA;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
+}
+
+static int ewx_i2c_getclock(snd_i2c_bus_t *bus)
+{
+ ice1712_t *ice = bus->private_data;
+ return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0;
+}
+
+static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack)
+{
+ ice1712_t *ice = bus->private_data;
+ int bit;
+ /* set RW pin to low */
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0);
+ if (ack)
+ udelay(5);
+ bit = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA ? 1 : 0;
+ /* set RW pin to high */
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW);
+ /* reset write mask */
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK);
+ return bit;
+}
+
+static void ewx_i2c_start(snd_i2c_bus_t *bus)
+{
+ ice1712_t *ice = bus->private_data;
+ unsigned char mask;
+
+ snd_ice1712_save_gpio_status(ice);
+ /* set RW high */
+ mask = ICE1712_EWX2496_RW;
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWX2496:
+ mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */
+ break;
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ mask |= ICE1712_6FIRE_AK4524_CS_MASK; /* CS high also */
+ break;
+ }
+ snd_ice1712_gpio_write_bits(ice, mask, mask);
+}
+
+static void ewx_i2c_stop(snd_i2c_bus_t *bus)
+{
+ ice1712_t *ice = bus->private_data;
+ snd_ice1712_restore_gpio_status(ice);
+}
+
+static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data)
+{
+ ice1712_t *ice = bus->private_data;
+ unsigned char mask = 0;
+
+ if (clock)
+ mask |= ICE1712_EWX2496_SERIAL_CLOCK; /* write SCL */
+ if (data)
+ mask |= ICE1712_EWX2496_SERIAL_DATA; /* write SDA */
+ ice->gpio.direction &= ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA);
+ ice->gpio.direction |= mask;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio.direction);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
+}
+
+static snd_i2c_bit_ops_t snd_ice1712_ewx_cs8427_bit_ops = {
+ .start = ewx_i2c_start,
+ .stop = ewx_i2c_stop,
+ .direction = ewx_i2c_direction,
+ .setlines = ewx_i2c_setlines,
+ .getclock = ewx_i2c_getclock,
+ .getdata = ewx_i2c_getdata,
+};
+
+
+/*
+ * AK4524 access
+ */
+
+/* AK4524 chip select; address 0x48 bit 0-3 */
+static int snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask)
+{
+ unsigned char data, ndata;
+
+ snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL);
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
+ goto __error;
+ ndata = (data & 0xf0) | chip_mask;
+ if (ndata != data)
+ if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1)
+ goto __error;
+ snd_i2c_unlock(ice->i2c);
+ return 0;
+
+ __error:
+ snd_i2c_unlock(ice->i2c);
+ snd_printk(KERN_ERR "AK4524 chip select failed, check cable to the front module\n");
+ return -EIO;
+}
+
+/* start callback for EWS88MT, needs to select a certain chip mask */
+static void ews88mt_ak4524_lock(akm4xxx_t *ak, int chip)
+{
+ ice1712_t *ice = ak->private_data[0];
+ unsigned char tmp;
+ /* assert AK4524 CS */
+ if (snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f) < 0)
+ snd_printk(KERN_ERR "fatal error (ews88mt chip select)\n");
+ snd_ice1712_save_gpio_status(ice);
+ tmp = ICE1712_EWS88_SERIAL_DATA |
+ ICE1712_EWS88_SERIAL_CLOCK |
+ ICE1712_EWS88_RW;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+ ice->gpio.direction | tmp);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
+}
+
+/* stop callback for EWS88MT, needs to deselect chip mask */
+static void ews88mt_ak4524_unlock(akm4xxx_t *ak, int chip)
+{
+ ice1712_t *ice = ak->private_data[0];
+ snd_ice1712_restore_gpio_status(ice);
+ udelay(1);
+ snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+}
+
+/* start callback for EWX24/96 */
+static void ewx2496_ak4524_lock(akm4xxx_t *ak, int chip)
+{
+ ice1712_t *ice = ak->private_data[0];
+ unsigned char tmp;
+ snd_ice1712_save_gpio_status(ice);
+ tmp = ICE1712_EWX2496_SERIAL_DATA |
+ ICE1712_EWX2496_SERIAL_CLOCK |
+ ICE1712_EWX2496_AK4524_CS |
+ ICE1712_EWX2496_RW;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+ ice->gpio.direction | tmp);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
+}
+
+/* start callback for DMX 6fire */
+static void dmx6fire_ak4524_lock(akm4xxx_t *ak, int chip)
+{
+ struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+ ice1712_t *ice = ak->private_data[0];
+ unsigned char tmp;
+ snd_ice1712_save_gpio_status(ice);
+ tmp = priv->cs_mask = priv->cs_addr = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK;
+ tmp |= ICE1712_6FIRE_SERIAL_DATA |
+ ICE1712_6FIRE_SERIAL_CLOCK |
+ ICE1712_6FIRE_RW;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+ ice->gpio.direction | tmp);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
+}
+
+/*
+ * CS8404 interface on EWS88MT/D
+ */
+
+static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+ unsigned char bytes[2];
+
+ snd_i2c_lock(ice->i2c);
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWS88MT:
+ case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ case ICE1712_SUBDEVICE_PHASE88:
+ if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_CS8404], &bits, 1) != 1)
+ goto _error;
+ break;
+ case ICE1712_SUBDEVICE_EWS88D:
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
+ goto _error;
+ if (bits != bytes[1]) {
+ bytes[1] = bits;
+ if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
+ goto _error;
+ }
+ break;
+ }
+ _error:
+ snd_i2c_unlock(ice->i2c);
+}
+
+/*
+ */
+
+static void ews88_spdif_default_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_bits);
+}
+
+static int ews88_spdif_default_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned int val;
+ int change;
+
+ val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
+ spin_lock_irq(&ice->reg_lock);
+ change = ice->spdif.cs8403_bits != val;
+ ice->spdif.cs8403_bits = val;
+ if (change && ice->playback_pro_substream == NULL) {
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ice1712_ews_cs8404_spdif_write(ice, val);
+ } else {
+ spin_unlock_irq(&ice->reg_lock);
+ }
+ return change;
+}
+
+static void ews88_spdif_stream_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_stream_bits);
+}
+
+static int ews88_spdif_stream_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+ unsigned int val;
+ int change;
+
+ val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
+ spin_lock_irq(&ice->reg_lock);
+ change = ice->spdif.cs8403_stream_bits != val;
+ ice->spdif.cs8403_stream_bits = val;
+ if (change && ice->playback_pro_substream != NULL) {
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ice1712_ews_cs8404_spdif_write(ice, val);
+ } else {
+ spin_unlock_irq(&ice->reg_lock);
+ }
+ return change;
+}
+
+
+/* open callback */
+static void ews88_open_spdif(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+ ice->spdif.cs8403_stream_bits = ice->spdif.cs8403_bits;
+}
+
+/* set up SPDIF for EWS88MT / EWS88D */
+static void ews88_setup_spdif(ice1712_t *ice, int rate)
+{
+ unsigned long flags;
+ unsigned char tmp;
+ int change;
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ tmp = ice->spdif.cs8403_stream_bits;
+ if (tmp & 0x10) /* consumer */
+ tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
+ switch (rate) {
+ case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
+ case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
+ default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ }
+ change = ice->spdif.cs8403_stream_bits != tmp;
+ ice->spdif.cs8403_stream_bits = tmp;
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ if (change)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
+ snd_ice1712_ews_cs8404_spdif_write(ice, tmp);
+}
+
+
+/*
+ */
+static akm4xxx_t akm_ews88mt __devinitdata = {
+ .num_adcs = 8,
+ .num_dacs = 8,
+ .type = SND_AK4524,
+ .ops = {
+ .lock = ews88mt_ak4524_lock,
+ .unlock = ews88mt_ak4524_unlock
+ }
+};
+
+static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 1, /* CIF high */
+ .data_mask = ICE1712_EWS88_SERIAL_DATA,
+ .clk_mask = ICE1712_EWS88_SERIAL_CLOCK,
+ .cs_mask = 0,
+ .cs_addr = 0,
+ .cs_none = 0, /* no chip select on gpio */
+ .add_flags = ICE1712_EWS88_RW, /* set rw bit high */
+ .mask_flags = 0,
+};
+
+static akm4xxx_t akm_ewx2496 __devinitdata = {
+ .num_adcs = 2,
+ .num_dacs = 2,
+ .type = SND_AK4524,
+ .ops = {
+ .lock = ewx2496_ak4524_lock
+ }
+};
+
+static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 1, /* CIF high */
+ .data_mask = ICE1712_EWS88_SERIAL_DATA,
+ .clk_mask = ICE1712_EWS88_SERIAL_CLOCK,
+ .cs_mask = ICE1712_EWX2496_AK4524_CS,
+ .cs_addr = ICE1712_EWX2496_AK4524_CS,
+ .cs_none = 0,
+ .add_flags = ICE1712_EWS88_RW, /* set rw bit high */
+ .mask_flags = 0,
+};
+
+static akm4xxx_t akm_6fire __devinitdata = {
+ .num_adcs = 6,
+ .num_dacs = 6,
+ .type = SND_AK4524,
+ .ops = {
+ .lock = dmx6fire_ak4524_lock
+ }
+};
+
+static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 1, /* CIF high */
+ .data_mask = ICE1712_6FIRE_SERIAL_DATA,
+ .clk_mask = ICE1712_6FIRE_SERIAL_CLOCK,
+ .cs_mask = 0,
+ .cs_addr = 0, /* set later */
+ .cs_none = 0,
+ .add_flags = ICE1712_6FIRE_RW, /* set rw bit high */
+ .mask_flags = 0,
+};
+
+/*
+ * initialize the chip
+ */
+
+/* 6fire specific */
+#define PCF9554_REG_INPUT 0
+#define PCF9554_REG_OUTPUT 1
+#define PCF9554_REG_POLARITY 2
+#define PCF9554_REG_CONFIG 3
+
+static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char reg, unsigned char data);
+
+static int __devinit snd_ice1712_ews_init(ice1712_t *ice)
+{
+ int err;
+ akm4xxx_t *ak;
+
+ /* set the analog DACs */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWX2496:
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 2;
+ break;
+ case ICE1712_SUBDEVICE_EWS88MT:
+ case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ case ICE1712_SUBDEVICE_PHASE88:
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 8;
+ break;
+ case ICE1712_SUBDEVICE_EWS88D:
+ /* Note: not analog but ADAT I/O */
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 8;
+ break;
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ ice->num_total_dacs = 6;
+ ice->num_total_adcs = 6;
+ break;
+ }
+
+ /* create i2c */
+ if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
+ snd_printk("unable to create I2C bus\n");
+ return err;
+ }
+ ice->i2c->private_data = ice;
+ ice->i2c->hw_ops.bit = &snd_ice1712_ewx_cs8427_bit_ops;
+
+ /* create i2c devices */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->spec.i2cdevs[EWS_I2C_6FIRE])) < 0) {
+ snd_printk("PCF9554 initialization failed\n");
+ return err;
+ }
+ snd_ice1712_6fire_write_pca(ice, PCF9554_REG_CONFIG, 0x80);
+ break;
+ case ICE1712_SUBDEVICE_EWS88MT:
+ case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ case ICE1712_SUBDEVICE_PHASE88:
+ if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->spec.i2cdevs[EWS_I2C_CS8404])) < 0)
+ return err;
+ if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF1])) < 0)
+ return err;
+ if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF2])) < 0)
+ return err;
+ /* Check if the front module is connected */
+ if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0)
+ return err;
+ break;
+ case ICE1712_SUBDEVICE_EWS88D:
+ if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->spec.i2cdevs[EWS_I2C_88D])) < 0)
+ return err;
+ break;
+ }
+
+ /* set up SPDIF interface */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWX2496:
+ if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+ return err;
+ snd_cs8427_reg_write(ice->cs8427, CS8427_REG_RECVERRMASK, CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR);
+ break;
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ if ((err = snd_ice1712_init_cs8427(ice, ICE1712_6FIRE_CS8427_ADDR)) < 0)
+ return err;
+ snd_cs8427_reg_write(ice->cs8427, CS8427_REG_RECVERRMASK, CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR);
+ break;
+ case ICE1712_SUBDEVICE_EWS88MT:
+ case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_EWS88D:
+ /* set up CS8404 */
+ ice->spdif.ops.open = ews88_open_spdif;
+ ice->spdif.ops.setup_rate = ews88_setup_spdif;
+ ice->spdif.ops.default_get = ews88_spdif_default_get;
+ ice->spdif.ops.default_put = ews88_spdif_default_put;
+ ice->spdif.ops.stream_get = ews88_spdif_stream_get;
+ ice->spdif.ops.stream_put = ews88_spdif_stream_put;
+ /* Set spdif defaults */
+ snd_ice1712_ews_cs8404_spdif_write(ice, ice->spdif.cs8403_bits);
+ break;
+ }
+
+ /* no analog? */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWS88D:
+ return 0;
+ }
+
+ /* analog section */
+ ak = ice->akm = kmalloc(sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ak)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWS88MT:
+ case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ case ICE1712_SUBDEVICE_PHASE88:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, &akm_ews88mt_priv, ice);
+ break;
+ case ICE1712_SUBDEVICE_EWX2496:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_ewx2496, &akm_ewx2496_priv, ice);
+ break;
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_6fire, &akm_6fire_priv, ice);
+ break;
+ default:
+ err = 0;
+ }
+
+ return err;
+}
+
+/*
+ * EWX 24/96 specific controls
+ */
+
+/* i/o sensitivity - this callback is shared among other devices, too */
+static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){
+
+ static char *texts[2] = {
+ "+4dBu", "-10dBV",
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= 2)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char mask = kcontrol->private_value & 0xff;
+
+ snd_ice1712_save_gpio_status(ice);
+ ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0;
+ snd_ice1712_restore_gpio_status(ice);
+ return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char mask = kcontrol->private_value & 0xff;
+ int val, nval;
+
+ if (kcontrol->private_value & (1 << 31))
+ return -EPERM;
+ nval = ucontrol->value.enumerated.item[0] ? mask : 0;
+ snd_ice1712_save_gpio_status(ice);
+ val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ nval |= val & ~mask;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
+ snd_ice1712_restore_gpio_status(ice);
+ return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ewx2496_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Sensitivity Switch",
+ .info = snd_ice1712_ewx_io_sense_info,
+ .get = snd_ice1712_ewx_io_sense_get,
+ .put = snd_ice1712_ewx_io_sense_put,
+ .private_value = ICE1712_EWX2496_AIN_SEL,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Output Sensitivity Switch",
+ .info = snd_ice1712_ewx_io_sense_info,
+ .get = snd_ice1712_ewx_io_sense_get,
+ .put = snd_ice1712_ewx_io_sense_put,
+ .private_value = ICE1712_EWX2496_AOUT_SEL,
+ },
+};
+
+
+/*
+ * EWS88MT specific controls
+ */
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char data;
+
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ snd_i2c_unlock(ice->i2c);
+ ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */
+ return 0;
+}
+
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char data, ndata;
+
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
+ if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ snd_i2c_unlock(ice->i2c);
+ return ndata != data;
+}
+
+/* analog input sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned char data;
+
+ snd_assert(channel >= 0 && channel <= 7, return 0);
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ /* reversed; high = +4dBu, low = -10dBV */
+ ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1;
+ snd_i2c_unlock(ice->i2c);
+ return 0;
+}
+
+/* analog output sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned char data, ndata;
+
+ snd_assert(channel >= 0 && channel <= 7, return 0);
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
+ if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &ndata, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ snd_i2c_unlock(ice->i2c);
+ return ndata != data;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Sensitivity Switch",
+ .info = snd_ice1712_ewx_io_sense_info,
+ .get = snd_ice1712_ews88mt_input_sense_get,
+ .put = snd_ice1712_ews88mt_input_sense_put,
+ .count = 8,
+};
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Output Sensitivity Switch",
+ .info = snd_ice1712_ewx_io_sense_info,
+ .get = snd_ice1712_ews88mt_output_sense_get,
+ .put = snd_ice1712_ews88mt_output_sense_put,
+};
+
+
+/*
+ * EWS88D specific controls
+ */
+
+static int snd_ice1712_ews88d_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value >> 8) & 1;
+ unsigned char data[2];
+
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ snd_i2c_unlock(ice->i2c);
+ data[0] = (data[shift >> 3] >> (shift & 7)) & 0x01;
+ if (invert)
+ data[0] ^= 0x01;
+ ucontrol->value.integer.value[0] = data[0];
+ return 0;
+}
+
+static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value >> 8) & 1;
+ unsigned char data[2], ndata[2];
+ int change;
+
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ ndata[shift >> 3] = data[shift >> 3] & ~(1 << (shift & 7));
+ if (invert) {
+ if (! ucontrol->value.integer.value[0])
+ ndata[shift >> 3] |= (1 << (shift & 7));
+ } else {
+ if (ucontrol->value.integer.value[0])
+ ndata[shift >> 3] |= (1 << (shift & 7));
+ }
+ change = (data[shift >> 3] != ndata[shift >> 3]);
+ if (change && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ snd_i2c_unlock(ice->i2c);
+ return change;
+}
+
+#define EWS88D_CONTROL(xiface, xname, xshift, xinvert, xaccess) \
+{ .iface = xiface,\
+ .name = xname,\
+ .access = xaccess,\
+ .info = snd_ice1712_ews88d_control_info,\
+ .get = snd_ice1712_ews88d_control_get,\
+ .put = snd_ice1712_ews88d_control_put,\
+ .private_value = xshift | (xinvert << 8),\
+}
+
+static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = {
+ EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */
+ EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0),
+ EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0),
+ EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 3, 0, 0),
+ EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 4, 1, 0),
+};
+
+
+/*
+ * DMX 6Fire specific controls
+ */
+
+static int snd_ice1712_6fire_read_pca(ice1712_t *ice, unsigned char reg)
+{
+ unsigned char byte;
+ snd_i2c_lock(ice->i2c);
+ byte = reg;
+ snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1);
+ byte = 0;
+ if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ printk("cannot read pca\n");
+ return -EIO;
+ }
+ snd_i2c_unlock(ice->i2c);
+ return byte;
+}
+
+static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char reg, unsigned char data)
+{
+ unsigned char bytes[2];
+ snd_i2c_lock(ice->i2c);
+ bytes[0] = reg;
+ bytes[1] = data;
+ if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ snd_i2c_unlock(ice->i2c);
+ return 0;
+}
+
+static int snd_ice1712_6fire_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value >> 8) & 1;
+ int data;
+
+ if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ return data;
+ data = (data >> shift) & 1;
+ if (invert)
+ data ^= 1;
+ ucontrol->value.integer.value[0] = data;
+ return 0;
+}
+
+static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value >> 8) & 1;
+ int data, ndata;
+
+ if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ return data;
+ ndata = data & ~(1 << shift);
+ if (ucontrol->value.integer.value[0])
+ ndata |= (1 << shift);
+ if (invert)
+ ndata ^= (1 << shift);
+ if (data != ndata) {
+ snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata);
+ return 1;
+ }
+ return 0;
+}
+
+static int snd_ice1712_6fire_select_input_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[4] = {
+ "Internal", "Front Input", "Rear Input", "Wave Table"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item >= 4)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ice1712_6fire_select_input_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int data;
+
+ if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ return data;
+ ucontrol->value.integer.value[0] = data & 3;
+ return 0;
+}
+
+static int snd_ice1712_6fire_select_input_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int data, ndata;
+
+ if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ return data;
+ ndata = data & ~3;
+ ndata |= (ucontrol->value.integer.value[0] & 3);
+ if (data != ndata) {
+ snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata);
+ return 1;
+ }
+ return 0;
+}
+
+
+#define DMX6FIRE_CONTROL(xname, xshift, xinvert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+ .name = xname,\
+ .info = snd_ice1712_6fire_control_info,\
+ .get = snd_ice1712_6fire_control_get,\
+ .put = snd_ice1712_6fire_control_put,\
+ .private_value = xshift | (xinvert << 8),\
+}
+
+static snd_kcontrol_new_t snd_ice1712_6fire_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Select",
+ .info = snd_ice1712_6fire_select_input_info,
+ .get = snd_ice1712_6fire_select_input_get,
+ .put = snd_ice1712_6fire_select_input_put,
+ },
+ DMX6FIRE_CONTROL("Front Digital Input Switch", 2, 0),
+ // DMX6FIRE_CONTROL("Master Clock Select", 3, 0),
+ DMX6FIRE_CONTROL("Optical Digital Input Switch", 4, 0),
+ DMX6FIRE_CONTROL("Phono Analog Input Switch", 5, 0),
+ DMX6FIRE_CONTROL("Breakbox LED", 6, 0),
+};
+
+
+static int __devinit snd_ice1712_ews_add_controls(ice1712_t *ice)
+{
+ unsigned int idx;
+ int err;
+
+ /* all terratec cards have spdif, but cs8427 module builds it's own controls */
+ if (ice->cs8427 == NULL) {
+ err = snd_ice1712_spdif_build_controls(ice);
+ if (err < 0)
+ return err;
+ }
+
+ /* ak4524 controls */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWX2496:
+ case ICE1712_SUBDEVICE_EWS88MT:
+ case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+ break;
+ }
+
+ /* card specific controls */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_EWX2496:
+ for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_ewx2496_controls); idx++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx2496_controls[idx], ice));
+ if (err < 0)
+ return err;
+ }
+ break;
+ case ICE1712_SUBDEVICE_EWS88MT:
+ case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ case ICE1712_SUBDEVICE_PHASE88:
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice));
+ if (err < 0)
+ return err;
+ break;
+ case ICE1712_SUBDEVICE_EWS88D:
+ for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_ews88d_controls); idx++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_controls[idx], ice));
+ if (err < 0)
+ return err;
+ }
+ break;
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_6fire_controls); idx++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_controls[idx], ice));
+ if (err < 0)
+ return err;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = {
+ {
+ .subvendor = ICE1712_SUBDEVICE_EWX2496,
+ .name = "TerraTec EWX24/96",
+ .model = "ewx2496",
+ .chip_init = snd_ice1712_ews_init,
+ .build_controls = snd_ice1712_ews_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_EWS88MT,
+ .name = "TerraTec EWS88MT",
+ .model = "ews88mt",
+ .chip_init = snd_ice1712_ews_init,
+ .build_controls = snd_ice1712_ews_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_EWS88MT_NEW,
+ .name = "TerraTec EWS88MT",
+ .model = "ews88mt_new",
+ .chip_init = snd_ice1712_ews_init,
+ .build_controls = snd_ice1712_ews_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_PHASE88,
+ .name = "TerraTec Phase88",
+ .model = "phase88",
+ .chip_init = snd_ice1712_ews_init,
+ .build_controls = snd_ice1712_ews_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_EWS88D,
+ .name = "TerraTec EWS88D",
+ .model = "ews88d",
+ .chip_init = snd_ice1712_ews_init,
+ .build_controls = snd_ice1712_ews_add_controls,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_DMX6FIRE,
+ .name = "TerraTec DMX6Fire",
+ .model = "dmx6fire",
+ .chip_init = snd_ice1712_ews_init,
+ .build_controls = snd_ice1712_ews_add_controls,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h
new file mode 100644
index 00000000000..a12a0b05355
--- /dev/null
+++ b/sound/pci/ice1712/ews.h
@@ -0,0 +1,84 @@
+#ifndef __SOUND_EWS_H
+#define __SOUND_EWS_H
+
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for Terratec EWS88MT/D, EWX24/96, DMX 6Fire
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ * 2002 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define EWS_DEVICE_DESC \
+ "{TerraTec,EWX 24/96},"\
+ "{TerraTec,EWS 88MT},"\
+ "{TerraTec,EWS 88D},"\
+ "{TerraTec,DMX 6Fire},"\
+ "{TerraTec,Phase 88},"
+
+#define ICE1712_SUBDEVICE_EWX2496 0x3b153011
+#define ICE1712_SUBDEVICE_EWS88MT 0x3b151511
+#define ICE1712_SUBDEVICE_EWS88MT_NEW 0x3b152511
+#define ICE1712_SUBDEVICE_EWS88D 0x3b152b11
+#define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811
+#define ICE1712_SUBDEVICE_PHASE88 0x3b155111
+
+/* entry point */
+extern struct snd_ice1712_card_info snd_ice1712_ews_cards[];
+
+
+/* TerraTec EWX 24/96 configuration definitions */
+
+#define ICE1712_EWX2496_AK4524_CS 0x01 /* AK4524 chip select; low = active */
+#define ICE1712_EWX2496_AIN_SEL 0x02 /* input sensitivity switch; high = louder */
+#define ICE1712_EWX2496_AOUT_SEL 0x04 /* output sensitivity switch; high = louder */
+#define ICE1712_EWX2496_RW 0x08 /* read/write switch for i2c; high = write */
+#define ICE1712_EWX2496_SERIAL_DATA 0x10 /* i2c & ak4524 data */
+#define ICE1712_EWX2496_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */
+#define ICE1712_EWX2496_TX2 0x40 /* MIDI2 (not used) */
+#define ICE1712_EWX2496_RX2 0x80 /* MIDI2 (not used) */
+
+/* TerraTec EWS 88MT/D configuration definitions */
+/* RW, SDA snd SCLK are identical with EWX24/96 */
+#define ICE1712_EWS88_CS8414_RATE 0x07 /* CS8414 sample rate: gpio 0-2 */
+#define ICE1712_EWS88_RW 0x08 /* read/write switch for i2c; high = write */
+#define ICE1712_EWS88_SERIAL_DATA 0x10 /* i2c & ak4524 data */
+#define ICE1712_EWS88_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */
+#define ICE1712_EWS88_TX2 0x40 /* MIDI2 (only on 88D) */
+#define ICE1712_EWS88_RX2 0x80 /* MIDI2 (only on 88D) */
+
+/* i2c address */
+#define ICE1712_EWS88MT_CS8404_ADDR (0x40>>1)
+#define ICE1712_EWS88MT_INPUT_ADDR (0x46>>1)
+#define ICE1712_EWS88MT_OUTPUT_ADDR (0x48>>1)
+#define ICE1712_EWS88MT_OUTPUT_SENSE 0x40 /* mask */
+#define ICE1712_EWS88D_PCF_ADDR (0x40>>1)
+
+/* TerraTec DMX 6Fire configuration definitions */
+#define ICE1712_6FIRE_AK4524_CS_MASK 0x07 /* AK4524 chip select #1-#3 */
+#define ICE1712_6FIRE_RW 0x08 /* read/write switch for i2c; high = write */
+#define ICE1712_6FIRE_SERIAL_DATA 0x10 /* i2c & ak4524 data */
+#define ICE1712_6FIRE_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */
+#define ICE1712_6FIRE_TX2 0x40 /* MIDI2 */
+#define ICE1712_6FIRE_RX2 0x80 /* MIDI2 */
+
+#define ICE1712_6FIRE_PCF9554_ADDR (0x40>>1)
+#define ICE1712_6FIRE_CS8427_ADDR (0x22)
+
+#endif /* __SOUND_EWS_H */
diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
new file mode 100644
index 00000000000..ab5fbd0bdfa
--- /dev/null
+++ b/sound/pci/ice1712/hoontech.c
@@ -0,0 +1,326 @@
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for Hoontech STDSP24
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "hoontech.h"
+
+
+static void __devinit snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte)
+{
+ byte |= ICE1712_STDSP24_CLOCK_BIT;
+ udelay(100);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+ byte &= ~ICE1712_STDSP24_CLOCK_BIT;
+ udelay(100);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+ byte |= ICE1712_STDSP24_CLOCK_BIT;
+ udelay(100);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+}
+
+static void __devinit snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate)
+{
+ down(&ice->gpio_mutex);
+ ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+ up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate)
+{
+ down(&ice->gpio_mutex);
+ ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+ up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate)
+{
+ down(&ice->gpio_mutex);
+ ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+ up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate)
+{
+ down(&ice->gpio_mutex);
+
+ /* select box */
+ ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+
+ /* prepare for write */
+ if (chn == 3)
+ ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
+ ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+
+ ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ udelay(100);
+ if (chn == 3) {
+ ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ } else {
+ switch (chn) {
+ case 0: ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 0); break;
+ case 1: ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 0); break;
+ case 2: ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 0); break;
+ }
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
+ }
+ udelay(100);
+ ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ udelay(100);
+
+ ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+
+ up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master)
+{
+ down(&ice->gpio_mutex);
+
+ /* select box */
+ ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+
+ ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, master);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+
+ udelay(100);
+
+ ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 0);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+
+ mdelay(10);
+
+ ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+
+ up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_midi2(ice1712_t *ice, int activate)
+{
+ down(&ice->gpio_mutex);
+ ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+ up(&ice->gpio_mutex);
+}
+
+static int __devinit snd_ice1712_hoontech_init(ice1712_t *ice)
+{
+ int box, chn;
+
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 8;
+
+ ice->spec.hoontech.boxbits[0] =
+ ice->spec.hoontech.boxbits[1] =
+ ice->spec.hoontech.boxbits[2] =
+ ice->spec.hoontech.boxbits[3] = 0; /* should be already */
+
+ ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 0);
+ ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 0, 1);
+ ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, 0);
+ ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, 0);
+
+ ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 1, 1);
+ ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
+
+ ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 2);
+ ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 2, 1);
+ ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
+
+ ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 3);
+ ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 3, 1);
+ ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, 0);
+ ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, 1);
+ ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, 0);
+
+ /* let's go - activate only functions in first box */
+ ice->spec.hoontech.config = 0;
+ /* ICE1712_STDSP24_MUTE |
+ ICE1712_STDSP24_INSEL |
+ ICE1712_STDSP24_DAREAR; */
+ ice->spec.hoontech.boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
+ ICE1712_STDSP24_BOX_CHN2 |
+ ICE1712_STDSP24_BOX_CHN3 |
+ ICE1712_STDSP24_BOX_CHN4 |
+ ICE1712_STDSP24_BOX_MIDI1 |
+ ICE1712_STDSP24_BOX_MIDI2;
+ ice->spec.hoontech.boxconfig[1] =
+ ice->spec.hoontech.boxconfig[2] =
+ ice->spec.hoontech.boxconfig[3] = 0;
+ snd_ice1712_stdsp24_darear(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
+ snd_ice1712_stdsp24_mute(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_MUTE) ? 1 : 0);
+ snd_ice1712_stdsp24_insel(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_INSEL) ? 1 : 0);
+ for (box = 0; box < 4; box++) {
+ for (chn = 0; chn < 4; chn++)
+ snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->spec.hoontech.boxconfig[box] & (1 << chn)) ? 1 : 0);
+ snd_ice1712_stdsp24_box_midi(ice, box,
+ (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
+ if (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
+ snd_ice1712_stdsp24_midi2(ice, 1);
+ }
+
+ return 0;
+}
+
+/*
+ * AK4524 access
+ */
+
+/* start callback for STDSP24 with modified hardware */
+static void stdsp24_ak4524_lock(akm4xxx_t *ak, int chip)
+{
+ ice1712_t *ice = ak->private_data[0];
+ unsigned char tmp;
+ snd_ice1712_save_gpio_status(ice);
+ tmp = ICE1712_STDSP24_SERIAL_DATA |
+ ICE1712_STDSP24_SERIAL_CLOCK |
+ ICE1712_STDSP24_AK4524_CS;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+ ice->gpio.direction | tmp);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
+}
+
+static int __devinit snd_ice1712_value_init(ice1712_t *ice)
+{
+ /* Hoontech STDSP24 with modified hardware */
+ static akm4xxx_t akm_stdsp24_mv __devinitdata = {
+ .num_adcs = 2,
+ .num_dacs = 2,
+ .type = SND_AK4524,
+ .ops = {
+ .lock = stdsp24_ak4524_lock
+ }
+ };
+
+ static struct snd_ak4xxx_private akm_stdsp24_mv_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 1, /* CIF high */
+ .data_mask = ICE1712_STDSP24_SERIAL_DATA,
+ .clk_mask = ICE1712_STDSP24_SERIAL_CLOCK,
+ .cs_mask = ICE1712_STDSP24_AK4524_CS,
+ .cs_addr = ICE1712_STDSP24_AK4524_CS,
+ .cs_none = 0,
+ .add_flags = 0,
+ };
+
+ int err;
+ akm4xxx_t *ak;
+
+ /* set the analog DACs */
+ ice->num_total_dacs = 2;
+
+ /* set the analog ADCs */
+ ice->num_total_adcs = 2;
+
+ /* analog section */
+ ak = ice->akm = kmalloc(sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ak)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+
+ err = snd_ice1712_akm4xxx_init(ak, &akm_stdsp24_mv, &akm_stdsp24_mv_priv, ice);
+ if (err < 0)
+ return err;
+
+ /* ak4524 controls */
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int __devinit snd_ice1712_ez8_init(ice1712_t *ice)
+{
+ ice->gpio.write_mask = ice->eeprom.gpiomask;
+ ice->gpio.direction = ice->eeprom.gpiodir;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
+ return 0;
+}
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
+ {
+ .subvendor = ICE1712_SUBDEVICE_STDSP24,
+ .name = "Hoontech SoundTrack Audio DSP24",
+ .model = "dsp24",
+ .chip_init = snd_ice1712_hoontech_init,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_STDSP24_VALUE, /* a dummy id */
+ .name = "Hoontech SoundTrack Audio DSP24 Value",
+ .model = "dsp24_value",
+ .chip_init = snd_ice1712_value_init,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_STDSP24_MEDIA7_1,
+ .name = "Hoontech STA DSP24 Media 7.1",
+ .model = "dsp24_71",
+ .chip_init = snd_ice1712_hoontech_init,
+ },
+ {
+ .subvendor = ICE1712_SUBDEVICE_EVENT_EZ8, /* a dummy id */
+ .name = "Event Electronics EZ8",
+ .model = "ez8",
+ .chip_init = snd_ice1712_ez8_init,
+ },
+ { } /* terminator */
+};
+
diff --git a/sound/pci/ice1712/hoontech.h b/sound/pci/ice1712/hoontech.h
new file mode 100644
index 00000000000..1ee538b20fb
--- /dev/null
+++ b/sound/pci/ice1712/hoontech.h
@@ -0,0 +1,77 @@
+#ifndef __SOUND_HOONTECH_H
+#define __SOUND_HOONTECH_H
+
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for Hoontech STDSP24
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define HOONTECH_DEVICE_DESC \
+ "{Hoontech,SoundTrack DSP 24}," \
+ "{Hoontech,SoundTrack DSP 24 Value}," \
+ "{Hoontech,SoundTrack DSP 24 Media 7.1}," \
+ "{Event Electronics,EZ8},"
+
+#define ICE1712_SUBDEVICE_STDSP24 0x12141217 /* Hoontech SoundTrack Audio DSP 24 */
+#define ICE1712_SUBDEVICE_STDSP24_VALUE 0x00010010 /* A dummy id for Hoontech SoundTrack Audio DSP 24 Value */
+#define ICE1712_SUBDEVICE_STDSP24_MEDIA7_1 0x16141217 /* Hoontech ST Audio DSP24 Media 7.1 */
+#define ICE1712_SUBDEVICE_EVENT_EZ8 0x00010001 /* A dummy id for EZ8 */
+
+extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[];
+
+
+/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */
+
+#define ICE1712_STDSP24_0_BOX(r, x) r[0] = ((r[0] & ~3) | ((x)&3))
+#define ICE1712_STDSP24_0_DAREAR(r, x) r[0] = ((r[0] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_1_CHN1(r, x) r[1] = ((r[1] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_1_CHN2(r, x) r[1] = ((r[1] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_1_CHN3(r, x) r[1] = ((r[1] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_2_CHN4(r, x) r[2] = ((r[2] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_2_MIDIIN(r, x) r[2] = ((r[2] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_2_MIDI1(r, x) r[2] = ((r[2] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_3_MIDI2(r, x) r[3] = ((r[3] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_3_MUTE(r, x) r[3] = ((r[3] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_3_INSEL(r, x) r[3] = ((r[3] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_SET_ADDR(r, a) r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3))
+#define ICE1712_STDSP24_CLOCK(r, a, c) r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5))
+#define ICE1712_STDSP24_CLOCK_BIT (1<<5)
+
+/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */
+
+#define ICE1712_STDSP24_DAREAR (1<<0)
+#define ICE1712_STDSP24_MUTE (1<<1)
+#define ICE1712_STDSP24_INSEL (1<<2)
+
+#define ICE1712_STDSP24_BOX_CHN1 (1<<0) /* input channel 1 */
+#define ICE1712_STDSP24_BOX_CHN2 (1<<1) /* input channel 2 */
+#define ICE1712_STDSP24_BOX_CHN3 (1<<2) /* input channel 3 */
+#define ICE1712_STDSP24_BOX_CHN4 (1<<3) /* input channel 4 */
+#define ICE1712_STDSP24_BOX_MIDI1 (1<<8)
+#define ICE1712_STDSP24_BOX_MIDI2 (1<<9)
+
+/* Hoontech SoundTrack Audio DSP 24 Value definitions for modified hardware */
+
+#define ICE1712_STDSP24_AK4524_CS 0x03 /* AK4524 chip select; low = active */
+#define ICE1712_STDSP24_SERIAL_DATA 0x0c /* ak4524 data */
+#define ICE1712_STDSP24_SERIAL_CLOCK 0x30 /* ak4524 clock */
+
+#endif /* __SOUND_HOONTECH_H */
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
new file mode 100644
index 00000000000..79fba6be350
--- /dev/null
+++ b/sound/pci/ice1712/ice1712.c
@@ -0,0 +1,2760 @@
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ NOTES:
+ - spdif nonaudio consumer mode does not work (at least with my
+ Sony STR-DB830)
+*/
+
+/*
+ * Changes:
+ *
+ * 2002.09.09 Takashi Iwai <tiwai@suse.de>
+ * split the code to several files. each low-level routine
+ * is stored in the local file and called from registration
+ * function from card_info struct.
+ *
+ * 2002.11.26 James Stafford <jstafford@ampltd.com>
+ * Added support for VT1724 (Envy24HT)
+ * I have left out support for 176.4 and 192 KHz for the moment.
+ * I also haven't done anything with the internal S/PDIF transmitter or the MPU-401
+ *
+ * 2003.02.20 Taksahi Iwai <tiwai@suse.de>
+ * Split vt1724 part to an independent driver.
+ * The GPIO is accessed through the callback functions now.
+ *
+ * 2004.03.31 Doug McLain <nostar@comcast.net>
+ * Added support for Event Electronics EZ8 card to hoontech.c.
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/cs8427.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+
+/* lowlevel routines */
+#include "delta.h"
+#include "ews.h"
+#include "hoontech.h"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{"
+ HOONTECH_DEVICE_DESC
+ DELTA_DEVICE_DESC
+ EWS_DEVICE_DESC
+ "{ICEnsemble,Generic ICE1712},"
+ "{ICEnsemble,Generic Envy24}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static char *model[SNDRV_CARDS];
+static int omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */
+static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transciever reset timeout value in msec */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ICE1712 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ICE1712 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ICE1712 soundcard.");
+module_param_array(omni, bool, NULL, 0444);
+MODULE_PARM_DESC(omni, "Enable Midiman M-Audio Delta Omni I/O support.");
+module_param_array(cs8427_timeout, int, NULL, 0444);
+MODULE_PARM_DESC(cs8427_timeout, "Define reset timeout for cs8427 chip in msec resolution.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+
+#ifndef PCI_VENDOR_ID_ICE
+#define PCI_VENDOR_ID_ICE 0x1412
+#endif
+#ifndef PCI_DEVICE_ID_ICE_1712
+#define PCI_DEVICE_ID_ICE_1712 0x1712
+#endif
+
+static struct pci_device_id snd_ice1712_ids[] = {
+ { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);
+
+static int snd_ice1712_build_pro_mixer(ice1712_t *ice);
+static int snd_ice1712_build_controls(ice1712_t *ice);
+
+static int PRO_RATE_LOCKED;
+static int PRO_RATE_RESET = 1;
+static unsigned int PRO_RATE_DEFAULT = 44100;
+
+/*
+ * Basic I/O
+ */
+
+/* check whether the clock mode is spdif-in */
+static inline int is_spdif_master(ice1712_t *ice)
+{
+ return (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) ? 1 : 0;
+}
+
+static inline int is_pro_rate_locked(ice1712_t *ice)
+{
+ return is_spdif_master(ice) || PRO_RATE_LOCKED;
+}
+
+static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data)
+{
+ outb((channel << 4) | addr, ICEDS(ice, INDEX));
+ outl(data, ICEDS(ice, DATA));
+}
+
+static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr)
+{
+ outb((channel << 4) | addr, ICEDS(ice, INDEX));
+ return inl(ICEDS(ice, DATA));
+}
+
+static void snd_ice1712_ac97_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEREG(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEREG(ice, AC97_INDEX));
+ outw(val, ICEREG(ice, AC97_DATA));
+ old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+ outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+ break;
+}
+
+static unsigned short snd_ice1712_ac97_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEREG(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEREG(ice, AC97_INDEX));
+ outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+ break;
+ if (tm >= 0x10000) /* timeout */
+ return ~0;
+ return inw(ICEREG(ice, AC97_DATA));
+}
+
+/*
+ * pro ac97 section
+ */
+
+static void snd_ice1712_pro_ac97_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEMT(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEMT(ice, AC97_INDEX));
+ outw(val, ICEMT(ice, AC97_DATA));
+ old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+ outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+ break;
+}
+
+
+static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEMT(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEMT(ice, AC97_INDEX));
+ outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+ break;
+ if (tm >= 0x10000) /* timeout */
+ return ~0;
+ return inw(ICEMT(ice, AC97_DATA));
+}
+
+/*
+ * consumer ac97 digital mix
+ */
+static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
+ return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val, nval;
+
+ spin_lock_irq(&ice->reg_lock);
+ val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
+ nval = val & ~ICE1712_ROUTE_AC97;
+ if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
+ outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
+ spin_unlock_irq(&ice->reg_lock);
+ return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Mixer To AC97",
+ .info = snd_ice1712_digmix_route_ac97_info,
+ .get = snd_ice1712_digmix_route_ac97_get,
+ .put = snd_ice1712_digmix_route_ac97_put,
+};
+
+
+/*
+ * gpio operations
+ */
+static void snd_ice1712_set_gpio_dir(ice1712_t *ice, unsigned int data)
+{
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, data);
+ inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
+}
+
+static void snd_ice1712_set_gpio_mask(ice1712_t *ice, unsigned int data)
+{
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data);
+ inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
+}
+
+static unsigned int snd_ice1712_get_gpio_data(ice1712_t *ice)
+{
+ return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+}
+
+static void snd_ice1712_set_gpio_data(ice1712_t *ice, unsigned int val)
+{
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, val);
+ inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
+}
+
+
+/*
+ *
+ * CS8427 interface
+ *
+ */
+
+/*
+ * change the input clock selection
+ * spdif_clock = 1 - IEC958 input, 0 - Envy24
+ */
+static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock)
+{
+ unsigned char reg[2] = { 0x80 | 4, 0 }; /* CS8427 auto increment | register number 4 + data */
+ unsigned char val, nval;
+ int res = 0;
+
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ nval = val & 0xf0;
+ if (spdif_clock)
+ nval |= 0x01;
+ else
+ nval |= 0x04;
+ if (val != nval) {
+ reg[1] = nval;
+ if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {
+ res = -EIO;
+ } else {
+ res++;
+ }
+ }
+ snd_i2c_unlock(ice->i2c);
+ return res;
+}
+
+/*
+ * spdif callbacks
+ */
+static void open_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+ snd_cs8427_iec958_active(ice->cs8427, 1);
+}
+
+static void close_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+ snd_cs8427_iec958_active(ice->cs8427, 0);
+}
+
+static void setup_cs8427(ice1712_t *ice, int rate)
+{
+ snd_cs8427_iec958_pcm(ice->cs8427, rate);
+}
+
+/*
+ * create and initialize callbacks for cs8427 interface
+ */
+int __devinit snd_ice1712_init_cs8427(ice1712_t *ice, int addr)
+{
+ int err;
+
+ if ((err = snd_cs8427_create(ice->i2c, addr,
+ (ice->cs8427_timeout * HZ) / 1000,
+ &ice->cs8427)) < 0) {
+ snd_printk("CS8427 initialization failed\n");
+ return err;
+ }
+ ice->spdif.ops.open = open_cs8427;
+ ice->spdif.ops.close = close_cs8427;
+ ice->spdif.ops.setup_rate = setup_cs8427;
+ return 0;
+}
+
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ice1712_t *ice = dev_id;
+ unsigned char status;
+ int handled = 0;
+
+ while (1) {
+ status = inb(ICEREG(ice, IRQSTAT));
+ if (status == 0)
+ break;
+ handled = 1;
+ if (status & ICE1712_IRQ_MPU1) {
+ if (ice->rmidi[0])
+ snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);
+ outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));
+ status &= ~ICE1712_IRQ_MPU1;
+ }
+ if (status & ICE1712_IRQ_TIMER)
+ outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));
+ if (status & ICE1712_IRQ_MPU2) {
+ if (ice->rmidi[1])
+ snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs);
+ outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));
+ status &= ~ICE1712_IRQ_MPU2;
+ }
+ if (status & ICE1712_IRQ_PROPCM) {
+ unsigned char mtstat = inb(ICEMT(ice, IRQ));
+ if (mtstat & ICE1712_MULTI_PBKSTATUS) {
+ if (ice->playback_pro_substream)
+ snd_pcm_period_elapsed(ice->playback_pro_substream);
+ outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));
+ }
+ if (mtstat & ICE1712_MULTI_CAPSTATUS) {
+ if (ice->capture_pro_substream)
+ snd_pcm_period_elapsed(ice->capture_pro_substream);
+ outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));
+ }
+ }
+ if (status & ICE1712_IRQ_FM)
+ outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT));
+ if (status & ICE1712_IRQ_PBKDS) {
+ u32 idx;
+ u16 pbkstatus;
+ snd_pcm_substream_t *substream;
+ pbkstatus = inw(ICEDS(ice, INTSTAT));
+ //printk("pbkstatus = 0x%x\n", pbkstatus);
+ for (idx = 0; idx < 6; idx++) {
+ if ((pbkstatus & (3 << (idx * 2))) == 0)
+ continue;
+ if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
+ snd_pcm_period_elapsed(substream);
+ outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
+ }
+ outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT));
+ }
+ if (status & ICE1712_IRQ_CONCAP) {
+ if (ice->capture_con_substream)
+ snd_pcm_period_elapsed(ice->capture_con_substream);
+ outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT));
+ }
+ if (status & ICE1712_IRQ_CONPBK) {
+ if (ice->playback_con_substream)
+ snd_pcm_period_elapsed(ice->playback_con_substream);
+ outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT));
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * PCM part - misc
+ */
+
+static int snd_ice1712_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ice1712_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * PCM part - consumer I/O
+ */
+
+static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int result = 0;
+ u32 tmp;
+
+ spin_lock(&ice->reg_lock);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ tmp |= 1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ tmp &= ~1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+ tmp |= 2;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+ tmp &= ~2;
+ } else {
+ result = -EINVAL;
+ }
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+ spin_unlock(&ice->reg_lock);
+ return result;
+}
+
+static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int result = 0;
+ u32 tmp;
+
+ spin_lock(&ice->reg_lock);
+ tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ tmp |= 1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ tmp &= ~1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+ tmp |= 2;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+ tmp &= ~2;
+ } else {
+ result = -EINVAL;
+ }
+ snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
+ spin_unlock(&ice->reg_lock);
+ return result;
+}
+
+static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int result = 0;
+ u8 tmp;
+
+ spin_lock(&ice->reg_lock);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ tmp |= 1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ tmp &= ~1;
+ } else {
+ result = -EINVAL;
+ }
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+ spin_unlock(&ice->reg_lock);
+ return result;
+}
+
+static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ u32 period_size, buf_size, rate, tmp;
+
+ period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+ buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+ tmp = 0x0000;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ tmp |= 0x10;
+ if (runtime->channels == 2)
+ tmp |= 0x08;
+ rate = (runtime->rate * 8192) / 375;
+ if (rate > 0x000fffff)
+ rate = 0x000fffff;
+ spin_lock_irq(&ice->reg_lock);
+ outb(0, ice->ddma_port + 15);
+ outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
+ outl(runtime->dma_addr, ice->ddma_port + 0);
+ outw(buf_size, ice->ddma_port + 4);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ u32 period_size, buf_size, rate, tmp, chn;
+
+ period_size = snd_pcm_lib_period_bytes(substream) - 1;
+ buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+ tmp = 0x0064;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ tmp &= ~0x04;
+ if (runtime->channels == 2)
+ tmp |= 0x08;
+ rate = (runtime->rate * 8192) / 375;
+ if (rate > 0x000fffff)
+ rate = 0x000fffff;
+ ice->playback_con_active_buf[substream->number] = 0;
+ ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
+ chn = substream->number * 2;
+ spin_lock_irq(&ice->reg_lock);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp);
+ if (runtime->channels == 2) {
+ snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
+ snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
+ }
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ u32 period_size, buf_size;
+ u8 tmp;
+
+ period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+ buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+ tmp = 0x06;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ tmp &= ~0x04;
+ if (runtime->channels == 2)
+ tmp &= ~0x02;
+ spin_lock_irq(&ice->reg_lock);
+ outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
+ outw(buf_size, ICEREG(ice, CONCAP_COUNT));
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ size_t ptr;
+
+ if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
+ return 0;
+ ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+ if (ptr == runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ u8 addr;
+ size_t ptr;
+
+ if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1))
+ return 0;
+ if (ice->playback_con_active_buf[substream->number])
+ addr = ICE1712_DSC_ADDR1;
+ else
+ addr = ICE1712_DSC_ADDR0;
+ ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
+ ice->playback_con_virt_addr[substream->number];
+ if (ptr == substream->runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
+ return 0;
+ ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+ if (ptr == substream->runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_ice1712_playback =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (64*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_playback_ds =
+{
+ .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_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (64*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static int snd_ice1712_playback_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->playback_con_substream = substream;
+ runtime->hw = snd_ice1712_playback;
+ return 0;
+}
+
+static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ u32 tmp;
+
+ ice->playback_con_substream_ds[substream->number] = substream;
+ runtime->hw = snd_ice1712_playback_ds;
+ spin_lock_irq(&ice->reg_lock);
+ tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
+ outw(tmp, ICEDS(ice, INTMASK));
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_capture_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->capture_con_substream = substream;
+ runtime->hw = snd_ice1712_capture;
+ runtime->hw.rates = ice->ac97->rates[AC97_RATES_ADC];
+ if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+ runtime->hw.rate_min = 48000;
+ return 0;
+}
+
+static int snd_ice1712_playback_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->playback_con_substream = NULL;
+ return 0;
+}
+
+static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ u32 tmp;
+
+ spin_lock_irq(&ice->reg_lock);
+ tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
+ outw(tmp, ICEDS(ice, INTMASK));
+ spin_unlock_irq(&ice->reg_lock);
+ ice->playback_con_substream_ds[substream->number] = NULL;
+ return 0;
+}
+
+static int snd_ice1712_capture_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->capture_con_substream = NULL;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ice1712_playback_ops = {
+ .open = snd_ice1712_playback_open,
+ .close = snd_ice1712_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_playback_prepare,
+ .trigger = snd_ice1712_playback_trigger,
+ .pointer = snd_ice1712_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_playback_ds_ops = {
+ .open = snd_ice1712_playback_ds_open,
+ .close = snd_ice1712_playback_ds_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_playback_ds_prepare,
+ .trigger = snd_ice1712_playback_ds_trigger,
+ .pointer = snd_ice1712_playback_ds_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_ops = {
+ .open = snd_ice1712_capture_open,
+ .close = snd_ice1712_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_capture_prepare,
+ .trigger = snd_ice1712_capture_trigger,
+ .pointer = snd_ice1712_capture_pointer,
+};
+
+static void snd_ice1712_pcm_free(snd_pcm_t *pcm)
+{
+ ice1712_t *ice = pcm->private_data;
+ ice->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops);
+
+ pcm->private_data = ice;
+ pcm->private_free = snd_ice1712_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ICE1712 consumer");
+ ice->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 64*1024, 64*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n");
+
+ return 0;
+}
+
+static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm)
+{
+ ice1712_t *ice = pcm->private_data;
+ ice->pcm_ds = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops);
+
+ pcm->private_data = ice;
+ pcm->private_free = snd_ice1712_pcm_free_ds;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ICE1712 consumer (DS)");
+ ice->pcm_ds = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+/*
+ * PCM code - professional part (multitrack)
+ */
+
+static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000 };
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ {
+ unsigned int what;
+ unsigned int old;
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+ what = ICE1712_PLAYBACK_PAUSE;
+ snd_pcm_trigger_done(substream, substream);
+ spin_lock(&ice->reg_lock);
+ old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ old |= what;
+ else
+ old &= ~what;
+ outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+ spin_unlock(&ice->reg_lock);
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ unsigned int what = 0;
+ unsigned int old;
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == ice->playback_pro_substream) {
+ what |= ICE1712_PLAYBACK_START;
+ snd_pcm_trigger_done(s, substream);
+ } else if (s == ice->capture_pro_substream) {
+ what |= ICE1712_CAPTURE_START_SHADOW;
+ snd_pcm_trigger_done(s, substream);
+ }
+ }
+ spin_lock(&ice->reg_lock);
+ old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ old |= what;
+ else
+ old &= ~what;
+ outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+ spin_unlock(&ice->reg_lock);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ */
+static void snd_ice1712_set_pro_rate(ice1712_t *ice, unsigned int rate, int force)
+{
+ unsigned long flags;
+ unsigned char val, old;
+ unsigned int i;
+
+ switch (rate) {
+ case 8000: val = 6; break;
+ case 9600: val = 3; break;
+ case 11025: val = 10; break;
+ case 12000: val = 2; break;
+ case 16000: val = 5; break;
+ case 22050: val = 9; break;
+ case 24000: val = 1; break;
+ case 32000: val = 4; break;
+ case 44100: val = 8; break;
+ case 48000: val = 0; break;
+ case 64000: val = 15; break;
+ case 88200: val = 11; break;
+ case 96000: val = 7; break;
+ default:
+ snd_BUG();
+ val = 0;
+ rate = 48000;
+ break;
+ }
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
+ ICE1712_PLAYBACK_PAUSE|
+ ICE1712_PLAYBACK_START)) {
+ __out:
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ return;
+ }
+ if (!force && is_pro_rate_locked(ice))
+ goto __out;
+
+ old = inb(ICEMT(ice, RATE));
+ if (!force && old == val)
+ goto __out;
+ outb(val, ICEMT(ice, RATE));
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+ if (ice->gpio.set_pro_rate)
+ ice->gpio.set_pro_rate(ice, rate);
+ for (i = 0; i < ice->akm_codecs; i++) {
+ if (ice->akm[i].ops.set_rate_val)
+ ice->akm[i].ops.set_rate_val(&ice->akm[i], rate);
+ }
+ if (ice->spdif.ops.setup_rate)
+ ice->spdif.ops.setup_rate(ice, rate);
+}
+
+static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
+ spin_lock_irq(&ice->reg_lock);
+ outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
+ outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
+ outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
+ spin_unlock_irq(&ice->reg_lock);
+
+ return 0;
+}
+
+static int snd_ice1712_playback_pro_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
+ spin_lock_irq(&ice->reg_lock);
+ outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
+ outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
+ outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_capture_pro_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
+ return 0;
+ ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+ if (ptr == substream->runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
+ return 0;
+ ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
+ if (ptr == substream->runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_ice1712_playback_pro =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 4000,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 10,
+ .buffer_bytes_max = (256*1024),
+ .period_bytes_min = 10 * 4 * 2,
+ .period_bytes_max = 131040,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture_pro =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 4000,
+ .rate_max = 96000,
+ .channels_min = 12,
+ .channels_max = 12,
+ .buffer_bytes_max = (256*1024),
+ .period_bytes_min = 12 * 4 * 2,
+ .period_bytes_max = 131040,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->playback_pro_substream = substream;
+ runtime->hw = snd_ice1712_playback_pro;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+
+ if (ice->spdif.ops.open)
+ ice->spdif.ops.open(ice, substream);
+
+ return 0;
+}
+
+static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ ice->capture_pro_substream = substream;
+ runtime->hw = snd_ice1712_capture_pro;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+ return 0;
+}
+
+static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ if (PRO_RATE_RESET)
+ snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ ice->playback_pro_substream = NULL;
+ if (ice->spdif.ops.close)
+ ice->spdif.ops.close(ice, substream);
+
+ return 0;
+}
+
+static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ if (PRO_RATE_RESET)
+ snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ ice->capture_pro_substream = NULL;
+ return 0;
+}
+
+static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm)
+{
+ ice1712_t *ice = pcm->private_data;
+ ice->pcm_pro = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static snd_pcm_ops_t snd_ice1712_playback_pro_ops = {
+ .open = snd_ice1712_playback_pro_open,
+ .close = snd_ice1712_playback_pro_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_playback_pro_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_playback_pro_prepare,
+ .trigger = snd_ice1712_pro_trigger,
+ .pointer = snd_ice1712_playback_pro_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_pro_ops = {
+ .open = snd_ice1712_capture_pro_open,
+ .close = snd_ice1712_capture_pro_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_capture_pro_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_capture_pro_prepare,
+ .trigger = snd_ice1712_pro_trigger,
+ .pointer = snd_ice1712_capture_pro_pointer,
+};
+
+static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops);
+
+ pcm->private_data = ice;
+ pcm->private_free = snd_ice1712_pcm_profi_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ICE1712 multi");
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 256*1024, 256*1024);
+
+ ice->pcm_pro = pcm;
+ if (rpcm)
+ *rpcm = pcm;
+
+ if (ice->cs8427) {
+ /* assign channels to iec958 */
+ err = snd_cs8427_iec958_build(ice->cs8427,
+ pcm->streams[0].substream,
+ pcm->streams[1].substream);
+ if (err < 0)
+ return err;
+ }
+
+ if ((err = snd_ice1712_build_pro_mixer(ice)) < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * Mixer section
+ */
+
+static void snd_ice1712_update_volume(ice1712_t *ice, int index)
+{
+ unsigned int vol = ice->pro_volumes[index];
+ unsigned short val = 0;
+
+ val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f;
+ val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8;
+ outb(index, ICEMT(ice, MONITOR_INDEX));
+ outw(val, ICEMT(ice, MONITOR_VOLUME));
+}
+
+static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+
+ spin_lock_irq(&ice->reg_lock);
+ ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
+ ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1);
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+ unsigned int nval, change;
+
+ nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
+ (ucontrol->value.integer.value[1] ? 0 : 0x80000000);
+ spin_lock_irq(&ice->reg_lock);
+ nval |= ice->pro_volumes[index] & ~0x80008000;
+ change = nval != ice->pro_volumes[index];
+ ice->pro_volumes[index] = nval;
+ snd_ice1712_update_volume(ice, index);
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 96;
+ return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+
+ spin_lock_irq(&ice->reg_lock);
+ ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
+ ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127;
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+ unsigned int nval, change;
+
+ nval = (ucontrol->value.integer.value[0] & 127) |
+ ((ucontrol->value.integer.value[1] & 127) << 16);
+ spin_lock_irq(&ice->reg_lock);
+ nval |= ice->pro_volumes[index] & ~0x007f007f;
+ change = nval != ice->pro_volumes[index];
+ ice->pro_volumes[index] = nval;
+ snd_ice1712_update_volume(ice, index);
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+
+static snd_kcontrol_new_t snd_ice1712_multi_playback_ctrls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Playback Switch",
+ .info = snd_ice1712_pro_mixer_switch_info,
+ .get = snd_ice1712_pro_mixer_switch_get,
+ .put = snd_ice1712_pro_mixer_switch_put,
+ .private_value = 0,
+ .count = 10,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Playback Volume",
+ .info = snd_ice1712_pro_mixer_volume_info,
+ .get = snd_ice1712_pro_mixer_volume_get,
+ .put = snd_ice1712_pro_mixer_volume_put,
+ .private_value = 0,
+ .count = 10,
+ },
+};
+
+static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_switch __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "H/W Multi Capture Switch",
+ .info = snd_ice1712_pro_mixer_switch_info,
+ .get = snd_ice1712_pro_mixer_switch_get,
+ .put = snd_ice1712_pro_mixer_switch_put,
+ .private_value = 10,
+};
+
+static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_switch __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Multi Capture Switch",
+ .info = snd_ice1712_pro_mixer_switch_info,
+ .get = snd_ice1712_pro_mixer_switch_get,
+ .put = snd_ice1712_pro_mixer_switch_put,
+ .private_value = 18,
+ .count = 2,
+};
+
+static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_volume __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "H/W Multi Capture Volume",
+ .info = snd_ice1712_pro_mixer_volume_info,
+ .get = snd_ice1712_pro_mixer_volume_get,
+ .put = snd_ice1712_pro_mixer_volume_put,
+ .private_value = 10,
+};
+
+static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_volume __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Multi Capture Volume",
+ .info = snd_ice1712_pro_mixer_volume_info,
+ .get = snd_ice1712_pro_mixer_volume_get,
+ .put = snd_ice1712_pro_mixer_volume_put,
+ .private_value = 18,
+ .count = 2,
+};
+
+static int __devinit snd_ice1712_build_pro_mixer(ice1712_t *ice)
+{
+ snd_card_t * card = ice->card;
+ unsigned int idx;
+ int err;
+
+ /* multi-channel mixer */
+ for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_multi_playback_ctrls); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_playback_ctrls[idx], ice));
+ if (err < 0)
+ return err;
+ }
+
+ if (ice->num_total_adcs > 0) {
+ snd_kcontrol_new_t tmp = snd_ice1712_multi_capture_analog_switch;
+ tmp.count = ice->num_total_adcs;
+ err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice));
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_switch, ice));
+ if (err < 0)
+ return err;
+
+ if (ice->num_total_adcs > 0) {
+ snd_kcontrol_new_t tmp = snd_ice1712_multi_capture_analog_volume;
+ tmp.count = ice->num_total_adcs;
+ err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice));
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_volume, ice));
+ if (err < 0)
+ return err;
+
+ /* initialize volumes */
+ for (idx = 0; idx < 10; idx++) {
+ ice->pro_volumes[idx] = 0x80008000; /* mute */
+ snd_ice1712_update_volume(ice, idx);
+ }
+ for (idx = 10; idx < 10 + ice->num_total_adcs; idx++) {
+ ice->pro_volumes[idx] = 0x80008000; /* mute */
+ snd_ice1712_update_volume(ice, idx);
+ }
+ for (idx = 18; idx < 20; idx++) {
+ ice->pro_volumes[idx] = 0x80008000; /* mute */
+ snd_ice1712_update_volume(ice, idx);
+ }
+ return 0;
+}
+
+static void snd_ice1712_mixer_free_ac97(ac97_t *ac97)
+{
+ ice1712_t *ice = ac97->private_data;
+ ice->ac97 = NULL;
+}
+
+static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
+{
+ int err, bus_num = 0;
+ ac97_template_t ac97;
+ ac97_bus_t *pbus;
+ static ac97_bus_ops_t con_ops = {
+ .write = snd_ice1712_ac97_write,
+ .read = snd_ice1712_ac97_read,
+ };
+ static ac97_bus_ops_t pro_ops = {
+ .write = snd_ice1712_pro_ac97_write,
+ .read = snd_ice1712_pro_ac97_read,
+ };
+
+ if (ice_has_con_ac97(ice)) {
+ if ((err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus)) < 0)
+ return err;
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = ice;
+ ac97.private_free = snd_ice1712_mixer_free_ac97;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+ printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
+ else {
+ if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
+ return err;
+ return 0;
+ }
+ }
+
+ if (! (ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) {
+ if ((err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus)) < 0)
+ return err;
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = ice;
+ ac97.private_free = snd_ice1712_mixer_free_ac97;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+ printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+ else
+ return 0;
+ }
+ /* I2S mixer only */
+ strcat(ice->card->mixername, "ICE1712 - multitrack");
+ return 0;
+}
+
+/*
+ *
+ */
+
+static inline unsigned int eeprom_double(ice1712_t *ice, int idx)
+{
+ return (unsigned int)ice->eeprom.data[idx] | ((unsigned int)ice->eeprom.data[idx + 1] << 8);
+}
+
+static void snd_ice1712_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ice1712_t *ice = entry->private_data;
+ unsigned int idx;
+
+ snd_iprintf(buffer, "%s\n\n", ice->card->longname);
+ snd_iprintf(buffer, "EEPROM:\n");
+
+ snd_iprintf(buffer, " Subvendor : 0x%x\n", ice->eeprom.subvendor);
+ snd_iprintf(buffer, " Size : %i bytes\n", ice->eeprom.size);
+ snd_iprintf(buffer, " Version : %i\n", ice->eeprom.version);
+ snd_iprintf(buffer, " Codec : 0x%x\n", ice->eeprom.data[ICE_EEP1_CODEC]);
+ snd_iprintf(buffer, " ACLink : 0x%x\n", ice->eeprom.data[ICE_EEP1_ACLINK]);
+ snd_iprintf(buffer, " I2S ID : 0x%x\n", ice->eeprom.data[ICE_EEP1_I2SID]);
+ snd_iprintf(buffer, " S/PDIF : 0x%x\n", ice->eeprom.data[ICE_EEP1_SPDIF]);
+ snd_iprintf(buffer, " GPIO mask : 0x%x\n", ice->eeprom.gpiomask);
+ snd_iprintf(buffer, " GPIO state : 0x%x\n", ice->eeprom.gpiostate);
+ snd_iprintf(buffer, " GPIO direction : 0x%x\n", ice->eeprom.gpiodir);
+ snd_iprintf(buffer, " AC'97 main : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_MAIN_LO));
+ snd_iprintf(buffer, " AC'97 pcm : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_PCM_LO));
+ snd_iprintf(buffer, " AC'97 record : 0x%x\n", eeprom_double(ice, ICE_EEP1_AC97_REC_LO));
+ snd_iprintf(buffer, " AC'97 record src : 0x%x\n", ice->eeprom.data[ICE_EEP1_AC97_RECSRC]);
+ for (idx = 0; idx < 4; idx++)
+ snd_iprintf(buffer, " DAC ID #%i : 0x%x\n", idx, ice->eeprom.data[ICE_EEP1_DAC_ID + idx]);
+ for (idx = 0; idx < 4; idx++)
+ snd_iprintf(buffer, " ADC ID #%i : 0x%x\n", idx, ice->eeprom.data[ICE_EEP1_ADC_ID + idx]);
+ for (idx = 0x1c; idx < ice->eeprom.size; idx++)
+ snd_iprintf(buffer, " Extra #%02i : 0x%x\n", idx, ice->eeprom.data[idx]);
+
+ snd_iprintf(buffer, "\nRegisters:\n");
+ snd_iprintf(buffer, " PSDOUT03 : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_PSDOUT03)));
+ snd_iprintf(buffer, " CAPTURE : 0x%08x\n", inl(ICEMT(ice, ROUTE_CAPTURE)));
+ snd_iprintf(buffer, " SPDOUT : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT)));
+ snd_iprintf(buffer, " RATE : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE)));
+}
+
+static void __devinit snd_ice1712_proc_init(ice1712_t * ice)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(ice->card, "ice1712", &entry))
+ snd_info_set_text_ops(entry, ice, 1024, snd_ice1712_proc_read);
+}
+
+/*
+ *
+ */
+
+static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(ice1712_eeprom_t);
+ return 0;
+}
+
+static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_eeprom __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "ICE1712 EEPROM",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_ice1712_eeprom_info,
+ .get = snd_ice1712_eeprom_get
+};
+
+/*
+ */
+static int snd_ice1712_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ if (ice->spdif.ops.default_get)
+ ice->spdif.ops.default_get(ice, ucontrol);
+ return 0;
+}
+
+static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ if (ice->spdif.ops.default_put)
+ return ice->spdif.ops.default_put(ice, ucontrol);
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_default __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_ice1712_spdif_info,
+ .get = snd_ice1712_spdif_default_get,
+ .put = snd_ice1712_spdif_default_put
+};
+
+static int snd_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ if (ice->spdif.ops.default_get) {
+ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_CON_NOT_COPYRIGHT |
+ IEC958_AES0_CON_EMPHASIS;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
+ IEC958_AES1_CON_CATEGORY;
+ ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+ } else {
+ ucontrol->value.iec958.status[0] = 0xff;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0xff;
+ ucontrol->value.iec958.status[3] = 0xff;
+ ucontrol->value.iec958.status[4] = 0xff;
+ }
+ return 0;
+}
+
+static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ if (ice->spdif.ops.default_get) {
+ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_PRO_FS |
+ IEC958_AES0_PRO_EMPHASIS;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE;
+ } else {
+ ucontrol->value.iec958.status[0] = 0xff;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0xff;
+ ucontrol->value.iec958.status[3] = 0xff;
+ ucontrol->value.iec958.status[4] = 0xff;
+ }
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_ice1712_spdif_info,
+ .get = snd_ice1712_spdif_maskc_get,
+};
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .info = snd_ice1712_spdif_info,
+ .get = snd_ice1712_spdif_maskp_get,
+};
+
+static int snd_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ if (ice->spdif.ops.stream_get)
+ ice->spdif.ops.stream_get(ice, ucontrol);
+ return 0;
+}
+
+static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ if (ice->spdif.ops.stream_put)
+ return ice->spdif.ops.stream_put(ice, ucontrol);
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_stream __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_ice1712_spdif_info,
+ .get = snd_ice1712_spdif_stream_get,
+ .put = snd_ice1712_spdif_stream_put
+};
+
+int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char mask = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
+
+ snd_ice1712_save_gpio_status(ice);
+ ucontrol->value.integer.value[0] = (snd_ice1712_gpio_read(ice) & mask ? 1 : 0) ^ invert;
+ snd_ice1712_restore_gpio_status(ice);
+ return 0;
+}
+
+int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char mask = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value & (1<<24)) ? mask : 0;
+ unsigned int val, nval;
+
+ if (kcontrol->private_value & (1 << 31))
+ return -EPERM;
+ nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert;
+ snd_ice1712_save_gpio_status(ice);
+ val = snd_ice1712_gpio_read(ice);
+ nval |= val & ~mask;
+ if (val != nval)
+ snd_ice1712_gpio_write(ice, nval);
+ snd_ice1712_restore_gpio_status(ice);
+ return val != nval;
+}
+
+/*
+ * rate
+ */
+static int snd_ice1712_pro_internal_clock_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {
+ "8000", /* 0: 6 */
+ "9600", /* 1: 3 */
+ "11025", /* 2: 10 */
+ "12000", /* 3: 2 */
+ "16000", /* 4: 5 */
+ "22050", /* 5: 9 */
+ "24000", /* 6: 1 */
+ "32000", /* 7: 4 */
+ "44100", /* 8: 8 */
+ "48000", /* 9: 0 */
+ "64000", /* 10: 15 */
+ "88200", /* 11: 11 */
+ "96000", /* 12: 7 */
+ "IEC958 Input", /* 13: -- */
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 14;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ice1712_pro_internal_clock_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ static unsigned char xlate[16] = {
+ 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10
+ };
+ unsigned char val;
+
+ spin_lock_irq(&ice->reg_lock);
+ if (is_spdif_master(ice)) {
+ ucontrol->value.enumerated.item[0] = 13;
+ } else {
+ val = xlate[inb(ICEMT(ice, RATE)) & 15];
+ if (val == 255) {
+ snd_BUG();
+ val = 0;
+ }
+ ucontrol->value.enumerated.item[0] = val;
+ }
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_pro_internal_clock_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ static unsigned int xrate[13] = {
+ 8000, 9600, 11025, 12000, 1600, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000
+ };
+ unsigned char oval;
+ int change = 0;
+
+ spin_lock_irq(&ice->reg_lock);
+ oval = inb(ICEMT(ice, RATE));
+ if (ucontrol->value.enumerated.item[0] == 13) {
+ outb(oval | ICE1712_SPDIF_MASTER, ICEMT(ice, RATE));
+ } else {
+ PRO_RATE_DEFAULT = xrate[ucontrol->value.integer.value[0] % 13];
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ice1712_set_pro_rate(ice, PRO_RATE_DEFAULT, 1);
+ spin_lock_irq(&ice->reg_lock);
+ }
+ change = inb(ICEMT(ice, RATE)) != oval;
+ spin_unlock_irq(&ice->reg_lock);
+
+ if ((oval & ICE1712_SPDIF_MASTER) != (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER)) {
+ /* change CS8427 clock source too */
+ if (ice->cs8427) {
+ snd_ice1712_cs8427_set_input_clock(ice, is_spdif_master(ice));
+ }
+ /* notify ak4524 chip as well */
+ if (is_spdif_master(ice)) {
+ unsigned int i;
+ for (i = 0; i < ice->akm_codecs; i++) {
+ if (ice->akm[i].ops.set_rate_val)
+ ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
+ }
+ }
+ }
+
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_pro_internal_clock __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Internal Clock",
+ .info = snd_ice1712_pro_internal_clock_info,
+ .get = snd_ice1712_pro_internal_clock_get,
+ .put = snd_ice1712_pro_internal_clock_put
+};
+
+static int snd_ice1712_pro_internal_clock_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {
+ "8000", /* 0: 6 */
+ "9600", /* 1: 3 */
+ "11025", /* 2: 10 */
+ "12000", /* 3: 2 */
+ "16000", /* 4: 5 */
+ "22050", /* 5: 9 */
+ "24000", /* 6: 1 */
+ "32000", /* 7: 4 */
+ "44100", /* 8: 8 */
+ "48000", /* 9: 0 */
+ "64000", /* 10: 15 */
+ "88200", /* 11: 11 */
+ "96000", /* 12: 7 */
+ // "IEC958 Input", /* 13: -- */
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 13;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ice1712_pro_internal_clock_default_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ int val;
+ static unsigned int xrate[13] = {
+ 8000, 9600, 11025, 12000, 1600, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000
+ };
+
+ for (val = 0; val < 13; val++) {
+ if (xrate[val] == PRO_RATE_DEFAULT)
+ break;
+ }
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int snd_ice1712_pro_internal_clock_default_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ static unsigned int xrate[13] = {
+ 8000, 9600, 11025, 12000, 1600, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000
+ };
+ unsigned char oval;
+ int change = 0;
+
+ oval = PRO_RATE_DEFAULT;
+ PRO_RATE_DEFAULT = xrate[ucontrol->value.integer.value[0] % 13];
+ change = PRO_RATE_DEFAULT != oval;
+
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_pro_internal_clock_default __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Internal Clock Default",
+ .info = snd_ice1712_pro_internal_clock_default_info,
+ .get = snd_ice1712_pro_internal_clock_default_get,
+ .put = snd_ice1712_pro_internal_clock_default_put
+};
+
+static int snd_ice1712_pro_rate_locking_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ice1712_pro_rate_locking_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.integer.value[0] = PRO_RATE_LOCKED;
+ return 0;
+}
+
+static int snd_ice1712_pro_rate_locking_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change = 0, nval;
+
+ nval = ucontrol->value.integer.value[0] ? 1 : 0;
+ spin_lock_irq(&ice->reg_lock);
+ change = PRO_RATE_LOCKED != nval;
+ PRO_RATE_LOCKED = nval;
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_pro_rate_locking __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Rate Locking",
+ .info = snd_ice1712_pro_rate_locking_info,
+ .get = snd_ice1712_pro_rate_locking_get,
+ .put = snd_ice1712_pro_rate_locking_put
+};
+
+static int snd_ice1712_pro_rate_reset_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ice1712_pro_rate_reset_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.integer.value[0] = PRO_RATE_RESET;
+ return 0;
+}
+
+static int snd_ice1712_pro_rate_reset_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change = 0, nval;
+
+ nval = ucontrol->value.integer.value[0] ? 1 : 0;
+ spin_lock_irq(&ice->reg_lock);
+ change = PRO_RATE_RESET != nval;
+ PRO_RATE_RESET = nval;
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_pro_rate_reset __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Rate Reset",
+ .info = snd_ice1712_pro_rate_reset_info,
+ .get = snd_ice1712_pro_rate_reset_get,
+ .put = snd_ice1712_pro_rate_reset_put
+};
+
+/*
+ * routing
+ */
+static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {
+ "PCM Out", /* 0 */
+ "H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */
+ "H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */
+ "IEC958 In L", "IEC958 In R", /* 9-10 */
+ "Digital Mixer", /* 11 - optional */
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int val, cval;
+
+ spin_lock_irq(&ice->reg_lock);
+ val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ cval = inl(ICEMT(ice, ROUTE_CAPTURE));
+ spin_unlock_irq(&ice->reg_lock);
+
+ val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
+ val &= 3;
+ cval >>= ((idx / 2) * 8) + ((idx % 2) * 4);
+ if (val == 1 && idx < 2)
+ ucontrol->value.enumerated.item[0] = 11;
+ else if (val == 2)
+ ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+ else if (val == 3)
+ ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+ return 0;
+}
+
+static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change, shift;
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int val, old_val, nval;
+
+ /* update PSDOUT */
+ if (ucontrol->value.enumerated.item[0] >= 11)
+ nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */
+ else if (ucontrol->value.enumerated.item[0] >= 9)
+ nval = 3; /* spdif in */
+ else if (ucontrol->value.enumerated.item[0] >= 1)
+ nval = 2; /* analog in */
+ else
+ nval = 0; /* pcm */
+ shift = ((idx % 2) * 8) + ((idx / 2) * 2);
+ spin_lock_irq(&ice->reg_lock);
+ val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ val &= ~(0x03 << shift);
+ val |= nval << shift;
+ change = val != old_val;
+ if (change)
+ outw(val, ICEMT(ice, ROUTE_PSDOUT03));
+ spin_unlock_irq(&ice->reg_lock);
+ if (nval < 2) /* dig mixer of pcm */
+ return change;
+
+ /* update CAPTURE */
+ spin_lock_irq(&ice->reg_lock);
+ val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
+ shift = ((idx / 2) * 8) + ((idx % 2) * 4);
+ if (nval == 2) { /* analog in */
+ nval = ucontrol->value.enumerated.item[0] - 1;
+ val &= ~(0x07 << shift);
+ val |= nval << shift;
+ } else { /* spdif in */
+ nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+ val &= ~(0x08 << shift);
+ val |= nval << shift;
+ }
+ if (val != old_val) {
+ change = 1;
+ outl(val, ICEMT(ice, ROUTE_CAPTURE));
+ }
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int val, cval;
+ val = inw(ICEMT(ice, ROUTE_SPDOUT));
+ cval = (val >> (idx * 4 + 8)) & 0x0f;
+ val = (val >> (idx * 2)) & 0x03;
+ if (val == 1)
+ ucontrol->value.enumerated.item[0] = 11;
+ else if (val == 2)
+ ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+ else if (val == 3)
+ ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+ return 0;
+}
+
+static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change, shift;
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int val, old_val, nval;
+
+ /* update SPDOUT */
+ spin_lock_irq(&ice->reg_lock);
+ val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
+ if (ucontrol->value.enumerated.item[0] >= 11)
+ nval = 1;
+ else if (ucontrol->value.enumerated.item[0] >= 9)
+ nval = 3;
+ else if (ucontrol->value.enumerated.item[0] >= 1)
+ nval = 2;
+ else
+ nval = 0;
+ shift = idx * 2;
+ val &= ~(0x03 << shift);
+ val |= nval << shift;
+ shift = idx * 4 + 8;
+ if (nval == 2) {
+ nval = ucontrol->value.enumerated.item[0] - 1;
+ val &= ~(0x07 << shift);
+ val |= nval << shift;
+ } else if (nval == 3) {
+ nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+ val &= ~(0x08 << shift);
+ val |= nval << shift;
+ }
+ change = val != old_val;
+ if (change)
+ outw(val, ICEMT(ice, ROUTE_SPDOUT));
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "H/W Playback Route",
+ .info = snd_ice1712_pro_route_info,
+ .get = snd_ice1712_pro_route_analog_get,
+ .put = snd_ice1712_pro_route_analog_put,
+};
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Route",
+ .info = snd_ice1712_pro_route_info,
+ .get = snd_ice1712_pro_route_spdif_get,
+ .put = snd_ice1712_pro_route_spdif_put,
+ .count = 2,
+};
+
+
+static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
+ return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change;
+
+ spin_lock_irq(&ice->reg_lock);
+ change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
+ outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Volume Rate",
+ .info = snd_ice1712_pro_volume_rate_info,
+ .get = snd_ice1712_pro_volume_rate_get,
+ .put = snd_ice1712_pro_volume_rate_put
+};
+
+static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 22;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx;
+
+ spin_lock_irq(&ice->reg_lock);
+ for (idx = 0; idx < 22; idx++) {
+ outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
+ ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
+ }
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Peak",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ice1712_pro_peak_info,
+ .get = snd_ice1712_pro_peak_get
+};
+
+/*
+ *
+ */
+
+/*
+ * list of available boards
+ */
+static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+ snd_ice1712_hoontech_cards,
+ snd_ice1712_delta_cards,
+ snd_ice1712_ews_cards,
+ NULL,
+};
+
+static unsigned char __devinit snd_ice1712_read_i2c(ice1712_t *ice,
+ unsigned char dev,
+ unsigned char addr)
+{
+ long t = 0x10000;
+
+ outb(addr, ICEREG(ice, I2C_BYTE_ADDR));
+ outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR));
+ while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ;
+ return inb(ICEREG(ice, I2C_DATA));
+}
+
+static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice, const char *modelname)
+{
+ int dev = 0xa0; /* EEPROM device address */
+ unsigned int i, size;
+ struct snd_ice1712_card_info **tbl, *c;
+
+ if (! modelname || ! *modelname) {
+ ice->eeprom.subvendor = 0;
+ if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) != 0)
+ ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
+ (snd_ice1712_read_i2c(ice, dev, 0x01) << 8) |
+ (snd_ice1712_read_i2c(ice, dev, 0x02) << 16) |
+ (snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
+ if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
+ /* invalid subvendor from EEPROM, try the PCI subststem ID instead */
+ u16 vendor, device;
+ pci_read_config_word(ice->pci, PCI_SUBSYSTEM_VENDOR_ID, &vendor);
+ pci_read_config_word(ice->pci, PCI_SUBSYSTEM_ID, &device);
+ ice->eeprom.subvendor = ((unsigned int)swab16(vendor) << 16) | swab16(device);
+ if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
+ printk(KERN_ERR "ice1712: No valid ID is found\n");
+ return -ENXIO;
+ }
+ }
+ }
+ for (tbl = card_tables; *tbl; tbl++) {
+ for (c = *tbl; c->subvendor; c++) {
+ if (modelname && c->model && ! strcmp(modelname, c->model)) {
+ printk(KERN_INFO "ice1712: Using board model %s\n", c->name);
+ ice->eeprom.subvendor = c->subvendor;
+ } else if (c->subvendor != ice->eeprom.subvendor)
+ continue;
+ if (! c->eeprom_size || ! c->eeprom_data)
+ goto found;
+ /* if the EEPROM is given by the driver, use it */
+ snd_printdd("using the defined eeprom..\n");
+ ice->eeprom.version = 1;
+ ice->eeprom.size = c->eeprom_size + 6;
+ memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
+ goto read_skipped;
+ }
+ }
+ printk(KERN_WARNING "ice1712: No matching model found for ID 0x%x\n", ice->eeprom.subvendor);
+
+ found:
+ ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04);
+ if (ice->eeprom.size < 6)
+ ice->eeprom.size = 32; /* FIXME: any cards without the correct size? */
+ else if (ice->eeprom.size > 32) {
+ snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
+ return -EIO;
+ }
+ ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
+ if (ice->eeprom.version != 1) {
+ snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
+ /* return -EIO; */
+ }
+ size = ice->eeprom.size - 6;
+ for (i = 0; i < size; i++)
+ ice->eeprom.data[i] = snd_ice1712_read_i2c(ice, dev, i + 6);
+
+ read_skipped:
+ ice->eeprom.gpiomask = ice->eeprom.data[ICE_EEP1_GPIO_MASK];
+ ice->eeprom.gpiostate = ice->eeprom.data[ICE_EEP1_GPIO_STATE];
+ ice->eeprom.gpiodir = ice->eeprom.data[ICE_EEP1_GPIO_DIR];
+
+ return 0;
+}
+
+
+
+static int __devinit snd_ice1712_chip_init(ice1712_t *ice)
+{
+ outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
+ udelay(200);
+ outb(ICE1712_NATIVE, ICEREG(ice, CONTROL));
+ udelay(200);
+ pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]);
+ pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]);
+ pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]);
+ pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]);
+ if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
+ ice->gpio.write_mask = ice->eeprom.gpiomask;
+ ice->gpio.direction = ice->eeprom.gpiodir;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
+ } else {
+ ice->gpio.write_mask = 0xc0;
+ ice->gpio.direction = 0xff;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT);
+ }
+ snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0);
+ if (!(ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97)) {
+ outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD));
+ udelay(100);
+ outb(0, ICEREG(ice, AC97_CMD));
+ udelay(200);
+ snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
+ }
+ snd_ice1712_set_pro_rate(ice, 48000, 1);
+
+ return 0;
+}
+
+int __devinit snd_ice1712_spdif_build_controls(ice1712_t *ice)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+
+ snd_assert(ice->pcm_pro != NULL, return -EIO);
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm_pro->device;
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm_pro->device;
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm_pro->device;
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm_pro->device;
+ ice->spdif.stream_ctl = kctl;
+ return 0;
+}
+
+
+static int __devinit snd_ice1712_build_controls(ice1712_t *ice)
+{
+ int err;
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_internal_clock, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_internal_clock_default, ice));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_rate_locking, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_rate_reset, ice));
+ if (err < 0)
+ return err;
+
+ if (ice->num_total_dacs > 0) {
+ snd_kcontrol_new_t tmp = snd_ice1712_mixer_pro_analog_route;
+ tmp.count = ice->num_total_dacs;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice));
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int snd_ice1712_free(ice1712_t *ice)
+{
+ if (! ice->port)
+ goto __hw_end;
+ /* mask all interrupts */
+ outb(0xc0, ICEMT(ice, IRQ));
+ outb(0xff, ICEREG(ice, IRQMASK));
+ /* --- */
+ __hw_end:
+ if (ice->irq >= 0) {
+ synchronize_irq(ice->irq);
+ free_irq(ice->irq, (void *) ice);
+ }
+ if (ice->port)
+ pci_release_regions(ice->pci);
+ snd_ice1712_akm4xxx_free(ice);
+ pci_disable_device(ice->pci);
+ kfree(ice);
+ return 0;
+}
+
+static int snd_ice1712_dev_free(snd_device_t *device)
+{
+ ice1712_t *ice = device->device_data;
+ return snd_ice1712_free(ice);
+}
+
+static int __devinit snd_ice1712_create(snd_card_t * card,
+ struct pci_dev *pci,
+ const char *modelname,
+ int omni,
+ int cs8427_timeout,
+ ice1712_t ** r_ice1712)
+{
+ ice1712_t *ice;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_ice1712_dev_free,
+ };
+
+ *r_ice1712 = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ /* check, if we can restrict PCI DMA transfers to 28 bits */
+ if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
+ snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ ice = kcalloc(1, sizeof(*ice), GFP_KERNEL);
+ if (ice == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ ice->omni = omni ? 1 : 0;
+ if (cs8427_timeout < 1)
+ cs8427_timeout = 1;
+ else if (cs8427_timeout > 1000)
+ cs8427_timeout = 1000;
+ ice->cs8427_timeout = cs8427_timeout;
+ spin_lock_init(&ice->reg_lock);
+ init_MUTEX(&ice->gpio_mutex);
+ init_MUTEX(&ice->i2c_mutex);
+ init_MUTEX(&ice->open_mutex);
+ ice->gpio.set_mask = snd_ice1712_set_gpio_mask;
+ ice->gpio.set_dir = snd_ice1712_set_gpio_dir;
+ ice->gpio.set_data = snd_ice1712_set_gpio_data;
+ ice->gpio.get_data = snd_ice1712_get_gpio_data;
+
+ ice->spdif.cs8403_bits =
+ ice->spdif.cs8403_stream_bits = (0x01 | /* consumer format */
+ 0x10 | /* no emphasis */
+ 0x20); /* PCM encoder/decoder */
+ ice->card = card;
+ ice->pci = pci;
+ ice->irq = -1;
+ pci_set_master(pci);
+ pci_write_config_word(ice->pci, 0x40, 0x807f);
+ pci_write_config_word(ice->pci, 0x42, 0x0006);
+ snd_ice1712_proc_init(ice);
+ synchronize_irq(pci->irq);
+
+ if ((err = pci_request_regions(pci, "ICE1712")) < 0) {
+ kfree(ice);
+ pci_disable_device(pci);
+ return err;
+ }
+ ice->port = pci_resource_start(pci, 0);
+ ice->ddma_port = pci_resource_start(pci, 1);
+ ice->dmapath_port = pci_resource_start(pci, 2);
+ ice->profi_port = pci_resource_start(pci, 3);
+
+ if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_ice1712_free(ice);
+ return -EIO;
+ }
+
+ ice->irq = pci->irq;
+
+ if (snd_ice1712_read_eeprom(ice, modelname) < 0) {
+ snd_ice1712_free(ice);
+ return -EIO;
+ }
+ if (snd_ice1712_chip_init(ice) < 0) {
+ snd_ice1712_free(ice);
+ return -EIO;
+ }
+
+ /* unmask used interrupts */
+ outb((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 |
+ (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0,
+ ICEREG(ice, IRQMASK));
+ outb(0x00, ICEMT(ice, IRQ));
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+ snd_ice1712_free(ice);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_ice1712 = ice;
+ return 0;
+}
+
+
+/*
+ *
+ * Registration
+ *
+ */
+
+static struct snd_ice1712_card_info no_matched __devinitdata;
+
+static int __devinit snd_ice1712_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ ice1712_t *ice;
+ int pcm_dev = 0, err;
+ struct snd_ice1712_card_info **tbl, *c;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ strcpy(card->driver, "ICE1712");
+ strcpy(card->shortname, "ICEnsemble ICE1712");
+
+ if ((err = snd_ice1712_create(card, pci, model[dev], omni[dev], cs8427_timeout[dev], &ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ for (tbl = card_tables; *tbl; tbl++) {
+ for (c = *tbl; c->subvendor; c++) {
+ if (c->subvendor == ice->eeprom.subvendor) {
+ strcpy(card->shortname, c->name);
+ if (c->driver) /* specific driver? */
+ strcpy(card->driver, c->driver);
+ if (c->chip_init) {
+ if ((err = c->chip_init(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ goto __found;
+ }
+ }
+ }
+ c = &no_matched;
+ __found:
+
+ if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (ice_has_con_ac97(ice))
+ if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_ice1712_ac97_mixer(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_ice1712_build_controls(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (c->build_controls) {
+ if ((err = c->build_controls(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+
+ if (ice_has_con_ac97(ice))
+ if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (! c->no_mpu401) {
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
+ ICEREG(ice, MPU1_CTRL), 1,
+ ice->irq, 0,
+ &ice->rmidi[0])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401)
+ if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
+ ICEREG(ice, MPU2_CTRL), 1,
+ ice->irq, 0,
+ &ice->rmidi[1])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, ice->port, ice->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_ice1712_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ICE1712",
+ .id_table = snd_ice1712_ids,
+ .probe = snd_ice1712_probe,
+ .remove = __devexit_p(snd_ice1712_remove),
+};
+
+static int __init alsa_card_ice1712_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_ice1712_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ice1712_init)
+module_exit(alsa_card_ice1712_exit)
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
new file mode 100644
index 00000000000..8bb1c58c26a
--- /dev/null
+++ b/sound/pci/ice1712/ice1712.h
@@ -0,0 +1,494 @@
+#ifndef __SOUND_ICE1712_H
+#define __SOUND_ICE1712_H
+
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+#include <sound/rawmidi.h>
+#include <sound/i2c.h>
+#include <sound/ak4xxx-adda.h>
+#include <sound/ak4114.h>
+#include <sound/pcm.h>
+
+
+/*
+ * Direct registers
+ */
+
+#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
+
+#define ICE1712_REG_CONTROL 0x00 /* byte */
+#define ICE1712_RESET 0x80 /* reset whole chip */
+#define ICE1712_SERR_LEVEL 0x04 /* SERR# level otherwise edge */
+#define ICE1712_NATIVE 0x01 /* native mode otherwise SB */
+#define ICE1712_REG_IRQMASK 0x01 /* byte */
+#define ICE1712_IRQ_MPU1 0x80
+#define ICE1712_IRQ_TIMER 0x40
+#define ICE1712_IRQ_MPU2 0x20
+#define ICE1712_IRQ_PROPCM 0x10
+#define ICE1712_IRQ_FM 0x08 /* FM/MIDI - legacy */
+#define ICE1712_IRQ_PBKDS 0x04 /* playback DS channels */
+#define ICE1712_IRQ_CONCAP 0x02 /* consumer capture */
+#define ICE1712_IRQ_CONPBK 0x01 /* consumer playback */
+#define ICE1712_REG_IRQSTAT 0x02 /* byte */
+/* look to ICE1712_IRQ_* */
+#define ICE1712_REG_INDEX 0x03 /* byte - indirect CCIxx regs */
+#define ICE1712_REG_DATA 0x04 /* byte - indirect CCIxx regs */
+#define ICE1712_REG_NMI_STAT1 0x05 /* byte */
+#define ICE1712_REG_NMI_DATA 0x06 /* byte */
+#define ICE1712_REG_NMI_INDEX 0x07 /* byte */
+#define ICE1712_REG_AC97_INDEX 0x08 /* byte */
+#define ICE1712_REG_AC97_CMD 0x09 /* byte */
+#define ICE1712_AC97_COLD 0x80 /* cold reset */
+#define ICE1712_AC97_WARM 0x40 /* warm reset */
+#define ICE1712_AC97_WRITE 0x20 /* W: write, R: write in progress */
+#define ICE1712_AC97_READ 0x10 /* W: read, R: read in progress */
+#define ICE1712_AC97_READY 0x08 /* codec ready status bit */
+#define ICE1712_AC97_PBK_VSR 0x02 /* playback VSR */
+#define ICE1712_AC97_CAP_VSR 0x01 /* capture VSR */
+#define ICE1712_REG_AC97_DATA 0x0a /* word (little endian) */
+#define ICE1712_REG_MPU1_CTRL 0x0c /* byte */
+#define ICE1712_REG_MPU1_DATA 0x0d /* byte */
+#define ICE1712_REG_I2C_DEV_ADDR 0x10 /* byte */
+#define ICE1712_I2C_WRITE 0x01 /* write direction */
+#define ICE1712_REG_I2C_BYTE_ADDR 0x11 /* byte */
+#define ICE1712_REG_I2C_DATA 0x12 /* byte */
+#define ICE1712_REG_I2C_CTRL 0x13 /* byte */
+#define ICE1712_I2C_EEPROM 0x80 /* EEPROM exists */
+#define ICE1712_I2C_BUSY 0x01 /* busy bit */
+#define ICE1712_REG_CONCAP_ADDR 0x14 /* dword - consumer capture */
+#define ICE1712_REG_CONCAP_COUNT 0x18 /* word - current/base count */
+#define ICE1712_REG_SERR_SHADOW 0x1b /* byte */
+#define ICE1712_REG_MPU2_CTRL 0x1c /* byte */
+#define ICE1712_REG_MPU2_DATA 0x1d /* byte */
+#define ICE1712_REG_TIMER 0x1e /* word */
+
+/*
+ * Indirect registers
+ */
+
+#define ICE1712_IREG_PBK_COUNT_LO 0x00
+#define ICE1712_IREG_PBK_COUNT_HI 0x01
+#define ICE1712_IREG_PBK_CTRL 0x02
+#define ICE1712_IREG_PBK_LEFT 0x03 /* left volume */
+#define ICE1712_IREG_PBK_RIGHT 0x04 /* right volume */
+#define ICE1712_IREG_PBK_SOFT 0x05 /* soft volume */
+#define ICE1712_IREG_PBK_RATE_LO 0x06
+#define ICE1712_IREG_PBK_RATE_MID 0x07
+#define ICE1712_IREG_PBK_RATE_HI 0x08
+#define ICE1712_IREG_CAP_COUNT_LO 0x10
+#define ICE1712_IREG_CAP_COUNT_HI 0x11
+#define ICE1712_IREG_CAP_CTRL 0x12
+#define ICE1712_IREG_GPIO_DATA 0x20
+#define ICE1712_IREG_GPIO_WRITE_MASK 0x21
+#define ICE1712_IREG_GPIO_DIRECTION 0x22
+#define ICE1712_IREG_CONSUMER_POWERDOWN 0x30
+#define ICE1712_IREG_PRO_POWERDOWN 0x31
+
+/*
+ * Consumer section direct DMA registers
+ */
+
+#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
+
+#define ICE1712_DS_INTMASK 0x00 /* word - interrupt mask */
+#define ICE1712_DS_INTSTAT 0x02 /* word - interrupt status */
+#define ICE1712_DS_DATA 0x04 /* dword - channel data */
+#define ICE1712_DS_INDEX 0x08 /* dword - channel index */
+
+/*
+ * Consumer section channel registers
+ */
+
+#define ICE1712_DSC_ADDR0 0x00 /* dword - base address 0 */
+#define ICE1712_DSC_COUNT0 0x01 /* word - count 0 */
+#define ICE1712_DSC_ADDR1 0x02 /* dword - base address 1 */
+#define ICE1712_DSC_COUNT1 0x03 /* word - count 1 */
+#define ICE1712_DSC_CONTROL 0x04 /* byte - control & status */
+#define ICE1712_BUFFER1 0x80 /* buffer1 is active */
+#define ICE1712_BUFFER1_AUTO 0x40 /* buffer1 auto init */
+#define ICE1712_BUFFER0_AUTO 0x20 /* buffer0 auto init */
+#define ICE1712_FLUSH 0x10 /* flush FIFO */
+#define ICE1712_STEREO 0x08 /* stereo */
+#define ICE1712_16BIT 0x04 /* 16-bit data */
+#define ICE1712_PAUSE 0x02 /* pause */
+#define ICE1712_START 0x01 /* start */
+#define ICE1712_DSC_RATE 0x05 /* dword - rate */
+#define ICE1712_DSC_VOLUME 0x06 /* word - volume control */
+
+/*
+ * Professional multi-track direct control registers
+ */
+
+#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x)
+
+#define ICE1712_MT_IRQ 0x00 /* byte - interrupt mask */
+#define ICE1712_MULTI_CAPTURE 0x80 /* capture IRQ */
+#define ICE1712_MULTI_PLAYBACK 0x40 /* playback IRQ */
+#define ICE1712_MULTI_CAPSTATUS 0x02 /* capture IRQ status */
+#define ICE1712_MULTI_PBKSTATUS 0x01 /* playback IRQ status */
+#define ICE1712_MT_RATE 0x01 /* byte - sampling rate select */
+#define ICE1712_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */
+#define ICE1712_MT_I2S_FORMAT 0x02 /* byte - I2S data format */
+#define ICE1712_MT_AC97_INDEX 0x04 /* byte - AC'97 index */
+#define ICE1712_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */
+/* look to ICE1712_AC97_* */
+#define ICE1712_MT_AC97_DATA 0x06 /* word - AC'97 data */
+#define ICE1712_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */
+#define ICE1712_MT_PLAYBACK_SIZE 0x14 /* word - playback size */
+#define ICE1712_MT_PLAYBACK_COUNT 0x16 /* word - playback count */
+#define ICE1712_MT_PLAYBACK_CONTROL 0x18 /* byte - control */
+#define ICE1712_CAPTURE_START_SHADOW 0x04 /* capture start */
+#define ICE1712_PLAYBACK_PAUSE 0x02 /* playback pause */
+#define ICE1712_PLAYBACK_START 0x01 /* playback start */
+#define ICE1712_MT_CAPTURE_ADDR 0x20 /* dword - capture address */
+#define ICE1712_MT_CAPTURE_SIZE 0x24 /* word - capture size */
+#define ICE1712_MT_CAPTURE_COUNT 0x26 /* word - capture count */
+#define ICE1712_MT_CAPTURE_CONTROL 0x28 /* byte - control */
+#define ICE1712_CAPTURE_START 0x01 /* capture start */
+#define ICE1712_MT_ROUTE_PSDOUT03 0x30 /* word */
+#define ICE1712_MT_ROUTE_SPDOUT 0x32 /* word */
+#define ICE1712_MT_ROUTE_CAPTURE 0x34 /* dword */
+#define ICE1712_MT_MONITOR_VOLUME 0x38 /* word */
+#define ICE1712_MT_MONITOR_INDEX 0x3a /* byte */
+#define ICE1712_MT_MONITOR_RATE 0x3b /* byte */
+#define ICE1712_MT_MONITOR_ROUTECTRL 0x3c /* byte */
+#define ICE1712_ROUTE_AC97 0x01 /* route digital mixer output to AC'97 */
+#define ICE1712_MT_MONITOR_PEAKINDEX 0x3e /* byte */
+#define ICE1712_MT_MONITOR_PEAKDATA 0x3f /* byte */
+
+/*
+ * Codec configuration bits
+ */
+
+/* PCI[60] System Configuration */
+#define ICE1712_CFG_CLOCK 0xc0
+#define ICE1712_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */
+#define ICE1712_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */
+#define ICE1712_CFG_EXT 0x80 /* external clock */
+#define ICE1712_CFG_2xMPU401 0x20 /* two MPU401 UARTs */
+#define ICE1712_CFG_NO_CON_AC97 0x10 /* consumer AC'97 codec is not present */
+#define ICE1712_CFG_ADC_MASK 0x0c /* one, two, three, four stereo ADCs */
+#define ICE1712_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */
+/* PCI[61] AC-Link Configuration */
+#define ICE1712_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */
+#define ICE1712_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */
+/* PCI[62] I2S Features */
+#define ICE1712_CFG_I2S_VOLUME 0x80 /* volume/mute capability */
+#define ICE1712_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */
+#define ICE1712_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */
+#define ICE1712_CFG_I2S_OTHER 0x0f /* other I2S IDs */
+/* PCI[63] S/PDIF Configuration */
+#define ICE1712_CFG_I2S_CHIPID 0xfc /* I2S chip ID */
+#define ICE1712_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */
+#define ICE1712_CFG_SPDIF_OUT 0x01 /* S/PDIF output is present */
+
+/*
+ * DMA mode values
+ * identical with DMA_XXX on i386 architecture.
+ */
+#define ICE1712_DMA_MODE_WRITE 0x48
+#define ICE1712_DMA_AUTOINIT 0x10
+
+
+/*
+ *
+ */
+
+typedef struct _snd_ice1712 ice1712_t;
+
+typedef struct {
+ unsigned int subvendor; /* PCI[2c-2f] */
+ unsigned char size; /* size of EEPROM image in bytes */
+ unsigned char version; /* must be 1 (or 2 for vt1724) */
+ unsigned char data[32];
+ unsigned int gpiomask;
+ unsigned int gpiostate;
+ unsigned int gpiodir;
+} ice1712_eeprom_t;
+
+enum {
+ ICE_EEP1_CODEC = 0, /* 06 */
+ ICE_EEP1_ACLINK, /* 07 */
+ ICE_EEP1_I2SID, /* 08 */
+ ICE_EEP1_SPDIF, /* 09 */
+ ICE_EEP1_GPIO_MASK, /* 0a */
+ ICE_EEP1_GPIO_STATE, /* 0b */
+ ICE_EEP1_GPIO_DIR, /* 0c */
+ ICE_EEP1_AC97_MAIN_LO, /* 0d */
+ ICE_EEP1_AC97_MAIN_HI, /* 0e */
+ ICE_EEP1_AC97_PCM_LO, /* 0f */
+ ICE_EEP1_AC97_PCM_HI, /* 10 */
+ ICE_EEP1_AC97_REC_LO, /* 11 */
+ ICE_EEP1_AC97_REC_HI, /* 12 */
+ ICE_EEP1_AC97_RECSRC, /* 13 */
+ ICE_EEP1_DAC_ID, /* 14 */
+ ICE_EEP1_DAC_ID1,
+ ICE_EEP1_DAC_ID2,
+ ICE_EEP1_DAC_ID3,
+ ICE_EEP1_ADC_ID, /* 18 */
+ ICE_EEP1_ADC_ID1,
+ ICE_EEP1_ADC_ID2,
+ ICE_EEP1_ADC_ID3
+};
+
+#define ice_has_con_ac97(ice) (!((ice)->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97))
+
+
+struct snd_ak4xxx_private {
+ unsigned int cif: 1; /* CIF mode */
+ unsigned char caddr; /* C0 and C1 bits */
+ unsigned int data_mask; /* DATA gpio bit */
+ unsigned int clk_mask; /* CLK gpio bit */
+ unsigned int cs_mask; /* bit mask for select/deselect address */
+ unsigned int cs_addr; /* bits to select address */
+ unsigned int cs_none; /* bits to deselect address */
+ unsigned int add_flags; /* additional bits at init */
+ unsigned int mask_flags; /* total mask bits */
+ struct snd_akm4xxx_ops {
+ void (*set_rate_val)(akm4xxx_t *ak, unsigned int rate);
+ } ops;
+};
+
+struct snd_ice1712_spdif {
+ unsigned char cs8403_bits;
+ unsigned char cs8403_stream_bits;
+ snd_kcontrol_t *stream_ctl;
+
+ struct snd_ice1712_spdif_ops {
+ void (*open)(ice1712_t *, snd_pcm_substream_t *);
+ void (*setup_rate)(ice1712_t *, int rate);
+ void (*close)(ice1712_t *, snd_pcm_substream_t *);
+ void (*default_get)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+ int (*default_put)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+ void (*stream_get)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+ int (*stream_put)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+ } ops;
+};
+
+
+struct _snd_ice1712 {
+ unsigned long conp_dma_size;
+ unsigned long conc_dma_size;
+ unsigned long prop_dma_size;
+ unsigned long proc_dma_size;
+ int irq;
+
+ unsigned long port;
+ unsigned long ddma_port;
+ unsigned long dmapath_port;
+ unsigned long profi_port;
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ snd_pcm_t *pcm_ds;
+ snd_pcm_t *pcm_pro;
+ snd_pcm_substream_t *playback_con_substream;
+ snd_pcm_substream_t *playback_con_substream_ds[6];
+ snd_pcm_substream_t *capture_con_substream;
+ snd_pcm_substream_t *playback_pro_substream;
+ snd_pcm_substream_t *capture_pro_substream;
+ unsigned int playback_pro_size;
+ unsigned int capture_pro_size;
+ unsigned int playback_con_virt_addr[6];
+ unsigned int playback_con_active_buf[6];
+ unsigned int capture_con_virt_addr;
+ unsigned int ac97_ext_id;
+ ac97_t *ac97;
+ snd_rawmidi_t *rmidi[2];
+
+ spinlock_t reg_lock;
+ snd_info_entry_t *proc_entry;
+
+ ice1712_eeprom_t eeprom;
+
+ unsigned int pro_volumes[20];
+ unsigned int omni: 1; /* Delta Omni I/O */
+ unsigned int vt1724: 1;
+ unsigned int vt1720: 1;
+ unsigned int has_spdif: 1; /* VT1720/4 - has SPDIF I/O */
+ unsigned int force_pdma4: 1; /* VT1720/4 - PDMA4 as non-spdif */
+ unsigned int force_rdma1: 1; /* VT1720/4 - RDMA1 as non-spdif */
+ unsigned int num_total_dacs; /* total DACs */
+ unsigned int num_total_adcs; /* total ADCs */
+ unsigned int cur_rate; /* current rate */
+
+ struct semaphore open_mutex;
+ snd_pcm_substream_t *pcm_reserved[4];
+ snd_pcm_hw_constraint_list_t *hw_rates; /* card-specific rate constraints */
+
+ unsigned int akm_codecs;
+ akm4xxx_t *akm;
+ struct snd_ice1712_spdif spdif;
+
+ struct semaphore i2c_mutex; /* I2C mutex for ICE1724 registers */
+ snd_i2c_bus_t *i2c; /* I2C bus */
+ snd_i2c_device_t *cs8427; /* CS8427 I2C device */
+ unsigned int cs8427_timeout; /* CS8427 reset timeout in HZ/100 */
+
+ struct ice1712_gpio {
+ unsigned int direction; /* current direction bits */
+ unsigned int write_mask; /* current mask bits */
+ unsigned int saved[2]; /* for ewx_i2c */
+ /* operators */
+ void (*set_mask)(ice1712_t *ice, unsigned int data);
+ void (*set_dir)(ice1712_t *ice, unsigned int data);
+ void (*set_data)(ice1712_t *ice, unsigned int data);
+ unsigned int (*get_data)(ice1712_t *ice);
+ /* misc operators - move to another place? */
+ void (*set_pro_rate)(ice1712_t *ice, unsigned int rate);
+ void (*i2s_mclk_changed)(ice1712_t *ice);
+ } gpio;
+ struct semaphore gpio_mutex;
+
+ /* other board-specific data */
+ union {
+ /* additional i2c devices for EWS boards */
+ snd_i2c_device_t *i2cdevs[3];
+ /* AC97 register cache for Aureon */
+ struct aureon_spec {
+ unsigned short stac9744[64];
+ unsigned int cs8415_mux;
+ unsigned short master[2];
+ unsigned short vol[8];
+ } aureon;
+ /* Hoontech-specific setting */
+ struct hoontech_spec {
+ unsigned char boxbits[4];
+ unsigned int config;
+ unsigned short boxconfig[4];
+ } hoontech;
+ struct {
+ ak4114_t *ak4114;
+ unsigned int analog: 1;
+ } juli;
+ } spec;
+
+};
+
+
+/*
+ * gpio access functions
+ */
+static inline void snd_ice1712_gpio_set_dir(ice1712_t *ice, unsigned int bits)
+{
+ ice->gpio.set_dir(ice, bits);
+}
+
+static inline void snd_ice1712_gpio_set_mask(ice1712_t *ice, unsigned int bits)
+{
+ ice->gpio.set_mask(ice, bits);
+}
+
+static inline void snd_ice1712_gpio_write(ice1712_t *ice, unsigned int val)
+{
+ ice->gpio.set_data(ice, val);
+}
+
+static inline unsigned int snd_ice1712_gpio_read(ice1712_t *ice)
+{
+ return ice->gpio.get_data(ice);
+}
+
+/*
+ * save and restore gpio status
+ * The access to gpio will be protected by mutex, so don't forget to
+ * restore!
+ */
+static inline void snd_ice1712_save_gpio_status(ice1712_t *ice)
+{
+ down(&ice->gpio_mutex);
+ ice->gpio.saved[0] = ice->gpio.direction;
+ ice->gpio.saved[1] = ice->gpio.write_mask;
+}
+
+static inline void snd_ice1712_restore_gpio_status(ice1712_t *ice)
+{
+ ice->gpio.set_dir(ice, ice->gpio.saved[0]);
+ ice->gpio.set_mask(ice, ice->gpio.saved[1]);
+ ice->gpio.direction = ice->gpio.saved[0];
+ ice->gpio.write_mask = ice->gpio.saved[1];
+ up(&ice->gpio_mutex);
+}
+
+/* for bit controls */
+#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \
+{ .iface = xiface, .name = xname, .access = xaccess, .info = snd_ice1712_gpio_info, \
+ .get = snd_ice1712_gpio_get, .put = snd_ice1712_gpio_put, \
+ .private_value = mask | (invert << 24) }
+
+int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+/*
+ * set gpio direction, write mask and data
+ */
+static inline void snd_ice1712_gpio_write_bits(ice1712_t *ice, unsigned int mask, unsigned int bits)
+{
+ ice->gpio.direction |= mask;
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+ snd_ice1712_gpio_set_mask(ice, ~mask);
+ snd_ice1712_gpio_write(ice, mask & bits);
+}
+
+int snd_ice1712_spdif_build_controls(ice1712_t *ice);
+
+int snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *template, const struct snd_ak4xxx_private *priv, ice1712_t *ice);
+void snd_ice1712_akm4xxx_free(ice1712_t *ice);
+int snd_ice1712_akm4xxx_build_controls(ice1712_t *ice);
+
+int snd_ice1712_init_cs8427(ice1712_t *ice, int addr);
+
+static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data)
+{
+ outb(addr, ICEREG(ice, INDEX));
+ outb(data, ICEREG(ice, DATA));
+}
+
+static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr)
+{
+ outb(addr, ICEREG(ice, INDEX));
+ return inb(ICEREG(ice, DATA));
+}
+
+
+/*
+ * entry pointer
+ */
+
+struct snd_ice1712_card_info {
+ unsigned int subvendor;
+ char *name;
+ char *model;
+ char *driver;
+ int (*chip_init)(ice1712_t *);
+ int (*build_controls)(ice1712_t *);
+ unsigned int no_mpu401: 1;
+ unsigned int eeprom_size;
+ unsigned char *eeprom_data;
+};
+
+
+#endif /* __SOUND_ICE1712_H */
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
new file mode 100644
index 00000000000..95500f06f0c
--- /dev/null
+++ b/sound/pci/ice1712/ice1724.c
@@ -0,0 +1,2340 @@
+/*
+ * ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT)
+ * VIA VT1720 (Envy24PT)
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ * 2002 James Stafford <jstafford@ampltd.com>
+ * 2003 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+
+/* lowlevel routines */
+#include "amp.h"
+#include "revo.h"
+#include "aureon.h"
+#include "vt1720_mobo.h"
+#include "pontis.h"
+#include "prodigy192.h"
+#include "juli.h"
+#include "phase.h"
+
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{"
+ REVO_DEVICE_DESC
+ AMP_AUDIO2000_DEVICE_DESC
+ AUREON_DEVICE_DESC
+ VT1720_MOBO_DEVICE_DESC
+ PONTIS_DEVICE_DESC
+ PRODIGY192_DEVICE_DESC
+ JULI_DEVICE_DESC
+ PHASE_DEVICE_DESC
+ "{VIA,VT1720},"
+ "{VIA,VT1724},"
+ "{ICEnsemble,Generic ICE1724},"
+ "{ICEnsemble,Generic Envy24HT}"
+ "{ICEnsemble,Generic Envy24PT}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static char *model[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ICE1724 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ICE1724 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ICE1724 soundcard.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+
+#ifndef PCI_VENDOR_ID_ICE
+#define PCI_VENDOR_ID_ICE 0x1412
+#endif
+#ifndef PCI_DEVICE_ID_VT1724
+#define PCI_DEVICE_ID_VT1724 0x1724
+#endif
+
+/* Both VT1720 and VT1724 have the same PCI IDs */
+static struct pci_device_id snd_vt1724_ids[] = {
+ { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_vt1724_ids);
+
+
+static int PRO_RATE_LOCKED;
+static int PRO_RATE_RESET = 1;
+static unsigned int PRO_RATE_DEFAULT = 44100;
+
+/*
+ * Basic I/O
+ */
+
+/* check whether the clock mode is spdif-in */
+static inline int is_spdif_master(ice1712_t *ice)
+{
+ return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;
+}
+
+static inline int is_pro_rate_locked(ice1712_t *ice)
+{
+ return is_spdif_master(ice) || PRO_RATE_LOCKED;
+}
+
+/*
+ * ac97 section
+ */
+
+static unsigned char snd_vt1724_ac97_ready(ice1712_t *ice)
+{
+ unsigned char old_cmd;
+ int tm;
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEMT1724(ice, AC97_CMD));
+ if (old_cmd & (VT1724_AC97_WRITE | VT1724_AC97_READ))
+ continue;
+ if (!(old_cmd & VT1724_AC97_READY))
+ continue;
+ return old_cmd;
+ }
+ snd_printd(KERN_ERR "snd_vt1724_ac97_ready: timeout\n");
+ return old_cmd;
+}
+
+static int snd_vt1724_ac97_wait_bit(ice1712_t *ice, unsigned char bit)
+{
+ int tm;
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0)
+ return 0;
+ snd_printd(KERN_ERR "snd_vt1724_ac97_wait_bit: timeout\n");
+ return -EIO;
+}
+
+static void snd_vt1724_ac97_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ unsigned char old_cmd;
+
+ old_cmd = snd_vt1724_ac97_ready(ice);
+ old_cmd &= ~VT1724_AC97_ID_MASK;
+ old_cmd |= ac97->num;
+ outb(reg, ICEMT1724(ice, AC97_INDEX));
+ outw(val, ICEMT1724(ice, AC97_DATA));
+ outb(old_cmd | VT1724_AC97_WRITE, ICEMT1724(ice, AC97_CMD));
+ snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_WRITE);
+}
+
+static unsigned short snd_vt1724_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ unsigned char old_cmd;
+
+ old_cmd = snd_vt1724_ac97_ready(ice);
+ old_cmd &= ~VT1724_AC97_ID_MASK;
+ old_cmd |= ac97->num;
+ outb(reg, ICEMT1724(ice, AC97_INDEX));
+ outb(old_cmd | VT1724_AC97_READ, ICEMT1724(ice, AC97_CMD));
+ if (snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_READ) < 0)
+ return ~0;
+ return inw(ICEMT1724(ice, AC97_DATA));
+}
+
+
+/*
+ * GPIO operations
+ */
+
+/* set gpio direction 0 = read, 1 = write */
+static void snd_vt1724_set_gpio_dir(ice1712_t *ice, unsigned int data)
+{
+ outl(data, ICEREG1724(ice, GPIO_DIRECTION));
+ inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */
+}
+
+/* set the gpio mask (0 = writable) */
+static void snd_vt1724_set_gpio_mask(ice1712_t *ice, unsigned int data)
+{
+ outw(data, ICEREG1724(ice, GPIO_WRITE_MASK));
+ if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */
+ outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22));
+ inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */
+}
+
+static void snd_vt1724_set_gpio_data(ice1712_t *ice, unsigned int data)
+{
+ outw(data, ICEREG1724(ice, GPIO_DATA));
+ if (! ice->vt1720)
+ outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22));
+ inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */
+}
+
+static unsigned int snd_vt1724_get_gpio_data(ice1712_t *ice)
+{
+ unsigned int data;
+ if (! ice->vt1720)
+ data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22));
+ else
+ data = 0;
+ data = (data << 16) | inw(ICEREG1724(ice, GPIO_DATA));
+ return data;
+}
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ice1712_t *ice = dev_id;
+ unsigned char status;
+ int handled = 0;
+
+ while (1) {
+ status = inb(ICEREG1724(ice, IRQSTAT));
+ if (status == 0)
+ break;
+
+ handled = 1;
+ /* these should probably be separated at some point,
+ but as we don't currently have MPU support on the board I will leave it */
+ if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) {
+ if (ice->rmidi[0])
+ snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);
+ outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT));
+ status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX);
+ }
+ if (status & VT1724_IRQ_MTPCM) {
+ /*
+ * Multi-track PCM
+ * PCM assignment are:
+ * Playback DMA0 (M/C) = playback_pro_substream
+ * Playback DMA1 = playback_con_substream_ds[0]
+ * Playback DMA2 = playback_con_substream_ds[1]
+ * Playback DMA3 = playback_con_substream_ds[2]
+ * Playback DMA4 (SPDIF) = playback_con_substream
+ * Record DMA0 = capture_pro_substream
+ * Record DMA1 = capture_con_substream
+ */
+ unsigned char mtstat = inb(ICEMT1724(ice, IRQ));
+ if (mtstat & VT1724_MULTI_PDMA0) {
+ if (ice->playback_pro_substream)
+ snd_pcm_period_elapsed(ice->playback_pro_substream);
+ }
+ if (mtstat & VT1724_MULTI_RDMA0) {
+ if (ice->capture_pro_substream)
+ snd_pcm_period_elapsed(ice->capture_pro_substream);
+ }
+ if (mtstat & VT1724_MULTI_PDMA1) {
+ if (ice->playback_con_substream_ds[0])
+ snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]);
+ }
+ if (mtstat & VT1724_MULTI_PDMA2) {
+ if (ice->playback_con_substream_ds[1])
+ snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]);
+ }
+ if (mtstat & VT1724_MULTI_PDMA3) {
+ if (ice->playback_con_substream_ds[2])
+ snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]);
+ }
+ if (mtstat & VT1724_MULTI_PDMA4) {
+ if (ice->playback_con_substream)
+ snd_pcm_period_elapsed(ice->playback_con_substream);
+ }
+ if (mtstat & VT1724_MULTI_RDMA1) {
+ if (ice->capture_con_substream)
+ snd_pcm_period_elapsed(ice->capture_con_substream);
+ }
+ /* ack anyway to avoid freeze */
+ outb(mtstat, ICEMT1724(ice, IRQ));
+ /* ought to really handle this properly */
+ if (mtstat & VT1724_MULTI_FIFO_ERR) {
+ unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR));
+ outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));
+ outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));
+ /* If I don't do this, I get machine lockup due to continual interrupts */
+ }
+
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ * PCM code - professional part (multitrack)
+ */
+
+static unsigned int rates[] = {
+ 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000,
+ 176400, 192000,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates_96 = {
+ .count = ARRAY_SIZE(rates) - 2, /* up to 96000 */
+ .list = rates,
+ .mask = 0,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates_48 = {
+ .count = ARRAY_SIZE(rates) - 5, /* up to 48000 */
+ .list = rates,
+ .mask = 0,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates_192 = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+struct vt1724_pcm_reg {
+ unsigned int addr; /* ADDR register offset */
+ unsigned int size; /* SIZE register offset */
+ unsigned int count; /* COUNT register offset */
+ unsigned int start; /* start & pause bit */
+};
+
+static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ unsigned char what;
+ unsigned char old;
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+
+ what = 0;
+ snd_pcm_group_for_each(pos, substream) {
+ struct vt1724_pcm_reg *reg;
+ s = snd_pcm_group_substream_entry(pos);
+ reg = s->runtime->private_data;
+ what |= reg->start;
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock(&ice->reg_lock);
+ old = inb(ICEMT1724(ice, DMA_PAUSE));
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ old |= what;
+ else
+ old &= ~what;
+ outb(old, ICEMT1724(ice, DMA_PAUSE));
+ spin_unlock(&ice->reg_lock);
+ break;
+
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_STOP:
+ spin_lock(&ice->reg_lock);
+ old = inb(ICEMT1724(ice, DMA_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ old |= what;
+ else
+ old &= ~what;
+ outb(old, ICEMT1724(ice, DMA_CONTROL));
+ spin_unlock(&ice->reg_lock);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ */
+
+#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\
+ VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START)
+#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\
+ VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)
+
+static int get_max_rate(ice1712_t *ice)
+{
+ if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
+ if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)
+ return 192000;
+ else
+ return 96000;
+ } else
+ return 48000;
+}
+
+static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force)
+{
+ unsigned long flags;
+ unsigned char val, old;
+ unsigned int i, mclk_change;
+
+ if (rate > get_max_rate(ice))
+ return;
+
+ switch (rate) {
+ case 8000: val = 6; break;
+ case 9600: val = 3; break;
+ case 11025: val = 10; break;
+ case 12000: val = 2; break;
+ case 16000: val = 5; break;
+ case 22050: val = 9; break;
+ case 24000: val = 1; break;
+ case 32000: val = 4; break;
+ case 44100: val = 8; break;
+ case 48000: val = 0; break;
+ case 64000: val = 15; break;
+ case 88200: val = 11; break;
+ case 96000: val = 7; break;
+ case 176400: val = 12; break;
+ case 192000: val = 14; break;
+ default:
+ snd_BUG();
+ val = 0;
+ break;
+ }
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
+ (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
+ /* running? we cannot change the rate now... */
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ return;
+ }
+ if (!force && is_pro_rate_locked(ice)) {
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ return;
+ }
+
+ old = inb(ICEMT1724(ice, RATE));
+ if (force || old != val)
+ outb(val, ICEMT1724(ice, RATE));
+ else if (rate == ice->cur_rate) {
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ return;
+ }
+
+ ice->cur_rate = rate;
+
+ /* check MT02 */
+ mclk_change = 0;
+ if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
+ val = old = inb(ICEMT1724(ice, I2S_FORMAT));
+ if (rate > 96000)
+ val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */
+ else
+ val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */
+ if (val != old) {
+ outb(val, ICEMT1724(ice, I2S_FORMAT));
+ mclk_change = 1;
+ }
+ }
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+ if (mclk_change && ice->gpio.i2s_mclk_changed)
+ ice->gpio.i2s_mclk_changed(ice);
+ if (ice->gpio.set_pro_rate)
+ ice->gpio.set_pro_rate(ice, rate);
+
+ /* set up codecs */
+ for (i = 0; i < ice->akm_codecs; i++) {
+ if (ice->akm[i].ops.set_rate_val)
+ ice->akm[i].ops.set_rate_val(&ice->akm[i], rate);
+ }
+ if (ice->spdif.ops.setup_rate)
+ ice->spdif.ops.setup_rate(ice, rate);
+}
+
+static int snd_vt1724_pcm_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int i, chs;
+
+ chs = params_channels(hw_params);
+ down(&ice->open_mutex);
+ /* mark surround channels */
+ if (substream == ice->playback_pro_substream) {
+ /* PDMA0 can be multi-channel up to 8 */
+ chs = chs / 2 - 1;
+ for (i = 0; i < chs; i++) {
+ if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
+ up(&ice->open_mutex);
+ return -EBUSY;
+ }
+ ice->pcm_reserved[i] = substream;
+ }
+ for (; i < 3; i++) {
+ if (ice->pcm_reserved[i] == substream)
+ ice->pcm_reserved[i] = NULL;
+ }
+ } else {
+ for (i = 0; i < 3; i++) {
+ /* check individual playback stream */
+ if (ice->playback_con_substream_ds[i] == substream) {
+ if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
+ up(&ice->open_mutex);
+ return -EBUSY;
+ }
+ ice->pcm_reserved[i] = substream;
+ break;
+ }
+ }
+ }
+ up(&ice->open_mutex);
+ snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_vt1724_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int i;
+
+ down(&ice->open_mutex);
+ /* unmark surround channels */
+ for (i = 0; i < 3; i++)
+ if (ice->pcm_reserved[i] == substream)
+ ice->pcm_reserved[i] = NULL;
+ up(&ice->open_mutex);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_vt1724_playback_pro_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ unsigned char val;
+ unsigned int size;
+
+ spin_lock_irq(&ice->reg_lock);
+ val = (8 - substream->runtime->channels) >> 1;
+ outb(val, ICEMT1724(ice, BURST));
+
+ outl(substream->runtime->dma_addr, ICEMT1724(ice, PLAYBACK_ADDR));
+
+ size = (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1;
+ // outl(size, ICEMT1724(ice, PLAYBACK_SIZE));
+ outw(size, ICEMT1724(ice, PLAYBACK_SIZE));
+ outb(size >> 16, ICEMT1724(ice, PLAYBACK_SIZE) + 2);
+ size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+ // outl(size, ICEMT1724(ice, PLAYBACK_COUNT));
+ outw(size, ICEMT1724(ice, PLAYBACK_COUNT));
+ outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2);
+
+ spin_unlock_irq(&ice->reg_lock);
+
+ // printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream));
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & VT1724_PDMA0_START))
+ return 0;
+#if 0 /* read PLAYBACK_ADDR */
+ ptr = inl(ICEMT1724(ice, PLAYBACK_ADDR));
+ if (ptr < substream->runtime->dma_addr) {
+ snd_printd("ice1724: invalid negative ptr\n");
+ return 0;
+ }
+ ptr -= substream->runtime->dma_addr;
+ ptr = bytes_to_frames(substream->runtime, ptr);
+ if (ptr >= substream->runtime->buffer_size) {
+ snd_printd("ice1724: invalid ptr %d (size=%d)\n", (int)ptr, (int)substream->runtime->period_size);
+ return 0;
+ }
+#else /* read PLAYBACK_SIZE */
+ ptr = inl(ICEMT1724(ice, PLAYBACK_SIZE)) & 0xffffff;
+ ptr = (ptr + 1) << 2;
+ ptr = bytes_to_frames(substream->runtime, ptr);
+ if (! ptr)
+ ;
+ else if (ptr <= substream->runtime->buffer_size)
+ ptr = substream->runtime->buffer_size - ptr;
+ else {
+ snd_printd("ice1724: invalid ptr %d (size=%d)\n", (int)ptr, (int)substream->runtime->buffer_size);
+ ptr = 0;
+ }
+#endif
+ return ptr;
+}
+
+static int snd_vt1724_pcm_prepare(snd_pcm_substream_t *substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ struct vt1724_pcm_reg *reg = substream->runtime->private_data;
+
+ spin_lock_irq(&ice->reg_lock);
+ outl(substream->runtime->dma_addr, ice->profi_port + reg->addr);
+ outw((snd_pcm_lib_buffer_bytes(substream) >> 2) - 1, ice->profi_port + reg->size);
+ outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ice->profi_port + reg->count);
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_vt1724_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ struct vt1724_pcm_reg *reg = substream->runtime->private_data;
+ size_t ptr;
+
+ if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & reg->start))
+ return 0;
+#if 0 /* use ADDR register */
+ ptr = inl(ice->profi_port + reg->addr);
+ ptr -= substream->runtime->dma_addr;
+ return bytes_to_frames(substream->runtime, ptr);
+#else /* use SIZE register */
+ ptr = inw(ice->profi_port + reg->size);
+ ptr = (ptr + 1) << 2;
+ ptr = bytes_to_frames(substream->runtime, ptr);
+ if (! ptr)
+ ;
+ else if (ptr <= substream->runtime->buffer_size)
+ ptr = substream->runtime->buffer_size - ptr;
+ else {
+ snd_printd("ice1724: invalid ptr %d (size=%d)\n", (int)ptr, (int)substream->runtime->buffer_size);
+ ptr = 0;
+ }
+ return ptr;
+#endif
+}
+
+static struct vt1724_pcm_reg vt1724_playback_pro_reg = {
+ .addr = VT1724_MT_PLAYBACK_ADDR,
+ .size = VT1724_MT_PLAYBACK_SIZE,
+ .count = VT1724_MT_PLAYBACK_COUNT,
+ .start = VT1724_PDMA0_START,
+};
+
+static struct vt1724_pcm_reg vt1724_capture_pro_reg = {
+ .addr = VT1724_MT_CAPTURE_ADDR,
+ .size = VT1724_MT_CAPTURE_SIZE,
+ .count = VT1724_MT_CAPTURE_COUNT,
+ .start = VT1724_RDMA0_START,
+};
+
+static snd_pcm_hardware_t snd_vt1724_playback_pro =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .buffer_bytes_max = (1UL << 21), /* 19bits dword */
+ .period_bytes_min = 8 * 4 * 2, /* FIXME: constraints needed */
+ .period_bytes_max = (1UL << 21),
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+static snd_pcm_hardware_t snd_vt1724_spdif =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (1UL << 18), /* 16bits dword */
+ .period_bytes_min = 2 * 4 * 2,
+ .period_bytes_max = (1UL << 18),
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+static snd_pcm_hardware_t snd_vt1724_2ch_stereo =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (1UL << 18), /* 16bits dword */
+ .period_bytes_min = 2 * 4 * 2,
+ .period_bytes_max = (1UL << 18),
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+/*
+ * set rate constraints
+ */
+static int set_rate_constraints(ice1712_t *ice, snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (ice->hw_rates) {
+ /* hardware specific */
+ runtime->hw.rate_min = ice->hw_rates->list[0];
+ runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1];
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+ return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, ice->hw_rates);
+ }
+ if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
+ /* I2S */
+ /* VT1720 doesn't support more than 96kHz */
+ if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)
+ return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
+ else {
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000;
+ runtime->hw.rate_max = 96000;
+ return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
+ }
+ } else if (ice->ac97) {
+ /* ACLINK */
+ runtime->hw.rate_max = 48000;
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000;
+ return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_48);
+ }
+ return 0;
+}
+
+/* multi-channel playback needs alignment 8x32bit regardless of the channels
+ * actually used
+ */
+#define VT1724_BUFFER_ALIGN 0x20
+
+static int snd_vt1724_playback_pro_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int chs;
+
+ runtime->private_data = &vt1724_playback_pro_reg;
+ ice->playback_pro_substream = substream;
+ runtime->hw = snd_vt1724_playback_pro;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ set_rate_constraints(ice, substream);
+ down(&ice->open_mutex);
+ /* calculate the currently available channels */
+ for (chs = 0; chs < 3; chs++) {
+ if (ice->pcm_reserved[chs])
+ break;
+ }
+ chs = (chs + 1) * 2;
+ runtime->hw.channels_max = chs;
+ if (chs > 2) /* channels must be even */
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ up(&ice->open_mutex);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ VT1724_BUFFER_ALIGN);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ VT1724_BUFFER_ALIGN);
+ return 0;
+}
+
+static int snd_vt1724_capture_pro_open(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ runtime->private_data = &vt1724_capture_pro_reg;
+ ice->capture_pro_substream = substream;
+ runtime->hw = snd_vt1724_2ch_stereo;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ set_rate_constraints(ice, substream);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ VT1724_BUFFER_ALIGN);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ VT1724_BUFFER_ALIGN);
+ return 0;
+}
+
+static int snd_vt1724_playback_pro_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ if (PRO_RATE_RESET)
+ snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ ice->playback_pro_substream = NULL;
+
+ return 0;
+}
+
+static int snd_vt1724_capture_pro_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ if (PRO_RATE_RESET)
+ snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ ice->capture_pro_substream = NULL;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_vt1724_playback_pro_ops = {
+ .open = snd_vt1724_playback_pro_open,
+ .close = snd_vt1724_playback_pro_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_vt1724_pcm_hw_params,
+ .hw_free = snd_vt1724_pcm_hw_free,
+ .prepare = snd_vt1724_playback_pro_prepare,
+ .trigger = snd_vt1724_pcm_trigger,
+ .pointer = snd_vt1724_playback_pro_pointer,
+};
+
+static snd_pcm_ops_t snd_vt1724_capture_pro_ops = {
+ .open = snd_vt1724_capture_pro_open,
+ .close = snd_vt1724_capture_pro_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_vt1724_pcm_hw_params,
+ .hw_free = snd_vt1724_pcm_hw_free,
+ .prepare = snd_vt1724_pcm_prepare,
+ .trigger = snd_vt1724_pcm_trigger,
+ .pointer = snd_vt1724_pcm_pointer,
+};
+
+static int __devinit snd_vt1724_pcm_profi(ice1712_t * ice, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ err = snd_pcm_new(ice->card, "ICE1724", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_vt1724_playback_pro_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_vt1724_capture_pro_ops);
+
+ pcm->private_data = ice;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ICE1724");
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 256*1024, 256*1024);
+
+ ice->pcm_pro = pcm;
+
+ return 0;
+}
+
+
+/*
+ * SPDIF PCM
+ */
+
+static struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
+ .addr = VT1724_MT_PDMA4_ADDR,
+ .size = VT1724_MT_PDMA4_SIZE,
+ .count = VT1724_MT_PDMA4_COUNT,
+ .start = VT1724_PDMA4_START,
+};
+
+static struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
+ .addr = VT1724_MT_RDMA1_ADDR,
+ .size = VT1724_MT_RDMA1_SIZE,
+ .count = VT1724_MT_RDMA1_COUNT,
+ .start = VT1724_RDMA1_START,
+};
+
+/* update spdif control bits; call with reg_lock */
+static void update_spdif_bits(ice1712_t *ice, unsigned int val)
+{
+ unsigned char cbit, disabled;
+
+ cbit = inb(ICEREG1724(ice, SPDIF_CFG));
+ disabled = cbit & ~VT1724_CFG_SPDIF_OUT_EN;
+ if (cbit != disabled)
+ outb(disabled, ICEREG1724(ice, SPDIF_CFG));
+ outw(val, ICEMT1724(ice, SPDIF_CTRL));
+ if (cbit != disabled)
+ outb(cbit, ICEREG1724(ice, SPDIF_CFG));
+ outw(val, ICEMT1724(ice, SPDIF_CTRL));
+}
+
+/* update SPDIF control bits according to the given rate */
+static void update_spdif_rate(ice1712_t *ice, unsigned int rate)
+{
+ unsigned int val, nval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ nval = val = inw(ICEMT1724(ice, SPDIF_CTRL));
+ nval &= ~(7 << 12);
+ switch (rate) {
+ case 44100: break;
+ case 48000: nval |= 2 << 12; break;
+ case 32000: nval |= 3 << 12; break;
+ }
+ if (val != nval)
+ update_spdif_bits(ice, nval);
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static int snd_vt1724_playback_spdif_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ if (! ice->force_pdma4)
+ update_spdif_rate(ice, substream->runtime->rate);
+ return snd_vt1724_pcm_prepare(substream);
+}
+
+static int snd_vt1724_playback_spdif_open(snd_pcm_substream_t *substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ runtime->private_data = &vt1724_playback_spdif_reg;
+ ice->playback_con_substream = substream;
+ if (ice->force_pdma4) {
+ runtime->hw = snd_vt1724_2ch_stereo;
+ set_rate_constraints(ice, substream);
+ } else
+ runtime->hw = snd_vt1724_spdif;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ VT1724_BUFFER_ALIGN);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ VT1724_BUFFER_ALIGN);
+ return 0;
+}
+
+static int snd_vt1724_playback_spdif_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ if (PRO_RATE_RESET)
+ snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ ice->playback_con_substream = NULL;
+
+ return 0;
+}
+
+static int snd_vt1724_capture_spdif_open(snd_pcm_substream_t *substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ runtime->private_data = &vt1724_capture_spdif_reg;
+ ice->capture_con_substream = substream;
+ if (ice->force_rdma1) {
+ runtime->hw = snd_vt1724_2ch_stereo;
+ set_rate_constraints(ice, substream);
+ } else
+ runtime->hw = snd_vt1724_spdif;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ VT1724_BUFFER_ALIGN);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ VT1724_BUFFER_ALIGN);
+ return 0;
+}
+
+static int snd_vt1724_capture_spdif_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ if (PRO_RATE_RESET)
+ snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ ice->capture_con_substream = NULL;
+
+ return 0;
+}
+
+static snd_pcm_ops_t snd_vt1724_playback_spdif_ops = {
+ .open = snd_vt1724_playback_spdif_open,
+ .close = snd_vt1724_playback_spdif_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_vt1724_pcm_hw_params,
+ .hw_free = snd_vt1724_pcm_hw_free,
+ .prepare = snd_vt1724_playback_spdif_prepare,
+ .trigger = snd_vt1724_pcm_trigger,
+ .pointer = snd_vt1724_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_vt1724_capture_spdif_ops = {
+ .open = snd_vt1724_capture_spdif_open,
+ .close = snd_vt1724_capture_spdif_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_vt1724_pcm_hw_params,
+ .hw_free = snd_vt1724_pcm_hw_free,
+ .prepare = snd_vt1724_pcm_prepare,
+ .trigger = snd_vt1724_pcm_trigger,
+ .pointer = snd_vt1724_pcm_pointer,
+};
+
+
+static int __devinit snd_vt1724_pcm_spdif(ice1712_t * ice, int device)
+{
+ char *name;
+ snd_pcm_t *pcm;
+ int play, capt;
+ int err;
+
+ if (ice->force_pdma4 ||
+ (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_OUT_INT)) {
+ play = 1;
+ ice->has_spdif = 1;
+ } else
+ play = 0;
+ if (ice->force_rdma1 ||
+ (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_IN)) {
+ capt = 1;
+ ice->has_spdif = 1;
+ } else
+ capt = 0;
+ if (! play && ! capt)
+ return 0; /* no spdif device */
+
+ if (ice->force_pdma4 || ice->force_rdma1)
+ name = "ICE1724 Secondary";
+ else
+ name = "IEC1724 IEC958";
+ err = snd_pcm_new(ice->card, name, device, play, capt, &pcm);
+ if (err < 0)
+ return err;
+
+ if (play)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_vt1724_playback_spdif_ops);
+ if (capt)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_vt1724_capture_spdif_ops);
+
+ pcm->private_data = ice;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, name);
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 64*1024, 64*1024);
+
+ ice->pcm = pcm;
+
+ return 0;
+}
+
+
+/*
+ * independent surround PCMs
+ */
+
+static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = {
+ {
+ .addr = VT1724_MT_PDMA1_ADDR,
+ .size = VT1724_MT_PDMA1_SIZE,
+ .count = VT1724_MT_PDMA1_COUNT,
+ .start = VT1724_PDMA1_START,
+ },
+ {
+ .addr = VT1724_MT_PDMA2_ADDR,
+ .size = VT1724_MT_PDMA2_SIZE,
+ .count = VT1724_MT_PDMA2_COUNT,
+ .start = VT1724_PDMA2_START,
+ },
+ {
+ .addr = VT1724_MT_PDMA3_ADDR,
+ .size = VT1724_MT_PDMA3_SIZE,
+ .count = VT1724_MT_PDMA3_COUNT,
+ .start = VT1724_PDMA3_START,
+ },
+};
+
+static int snd_vt1724_playback_indep_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ unsigned char val;
+
+ spin_lock_irq(&ice->reg_lock);
+ val = 3 - substream->number;
+ if (inb(ICEMT1724(ice, BURST)) < val)
+ outb(val, ICEMT1724(ice, BURST));
+ spin_unlock_irq(&ice->reg_lock);
+ return snd_vt1724_pcm_prepare(substream);
+}
+
+static int snd_vt1724_playback_indep_open(snd_pcm_substream_t *substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ down(&ice->open_mutex);
+ /* already used by PDMA0? */
+ if (ice->pcm_reserved[substream->number]) {
+ up(&ice->open_mutex);
+ return -EBUSY; /* FIXME: should handle blocking mode properly */
+ }
+ up(&ice->open_mutex);
+ runtime->private_data = &vt1724_playback_dma_regs[substream->number];
+ ice->playback_con_substream_ds[substream->number] = substream;
+ runtime->hw = snd_vt1724_2ch_stereo;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ set_rate_constraints(ice, substream);
+ return 0;
+}
+
+static int snd_vt1724_playback_indep_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ if (PRO_RATE_RESET)
+ snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ ice->playback_con_substream_ds[substream->number] = NULL;
+ ice->pcm_reserved[substream->number] = NULL;
+
+ return 0;
+}
+
+static snd_pcm_ops_t snd_vt1724_playback_indep_ops = {
+ .open = snd_vt1724_playback_indep_open,
+ .close = snd_vt1724_playback_indep_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_vt1724_pcm_hw_params,
+ .hw_free = snd_vt1724_pcm_hw_free,
+ .prepare = snd_vt1724_playback_indep_prepare,
+ .trigger = snd_vt1724_pcm_trigger,
+ .pointer = snd_vt1724_pcm_pointer,
+};
+
+
+static int __devinit snd_vt1724_pcm_indep(ice1712_t * ice, int device)
+{
+ snd_pcm_t *pcm;
+ int play;
+ int err;
+
+ play = ice->num_total_dacs / 2 - 1;
+ if (play <= 0)
+ return 0;
+
+ err = snd_pcm_new(ice->card, "ICE1724 Surrounds", device, play, 0, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_vt1724_playback_indep_ops);
+
+ pcm->private_data = ice;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ICE1724 Surround PCM");
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 64*1024, 64*1024);
+
+ ice->pcm_ds = pcm;
+
+ return 0;
+}
+
+
+/*
+ * Mixer section
+ */
+
+static int __devinit snd_vt1724_ac97_mixer(ice1712_t * ice)
+{
+ int err;
+
+ if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ static ac97_bus_ops_t ops = {
+ .write = snd_vt1724_ac97_write,
+ .read = snd_vt1724_ac97_read,
+ };
+
+ /* cold reset */
+ outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD));
+ mdelay(5); /* FIXME */
+ outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
+
+ if ((err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = ice;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+ printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+ else
+ return 0;
+ }
+ /* I2S mixer only */
+ strcat(ice->card->mixername, "ICE1724 - multitrack");
+ return 0;
+}
+
+/*
+ *
+ */
+
+static inline unsigned int eeprom_triple(ice1712_t *ice, int idx)
+{
+ return (unsigned int)ice->eeprom.data[idx] | \
+ ((unsigned int)ice->eeprom.data[idx + 1] << 8) | \
+ ((unsigned int)ice->eeprom.data[idx + 2] << 16);
+}
+
+static void snd_vt1724_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ice1712_t *ice = entry->private_data;
+ unsigned int idx;
+
+ snd_iprintf(buffer, "%s\n\n", ice->card->longname);
+ snd_iprintf(buffer, "EEPROM:\n");
+
+ snd_iprintf(buffer, " Subvendor : 0x%x\n", ice->eeprom.subvendor);
+ snd_iprintf(buffer, " Size : %i bytes\n", ice->eeprom.size);
+ snd_iprintf(buffer, " Version : %i\n", ice->eeprom.version);
+ snd_iprintf(buffer, " System Config : 0x%x\n", ice->eeprom.data[ICE_EEP2_SYSCONF]);
+ snd_iprintf(buffer, " ACLink : 0x%x\n", ice->eeprom.data[ICE_EEP2_ACLINK]);
+ snd_iprintf(buffer, " I2S : 0x%x\n", ice->eeprom.data[ICE_EEP2_I2S]);
+ snd_iprintf(buffer, " S/PDIF : 0x%x\n", ice->eeprom.data[ICE_EEP2_SPDIF]);
+ snd_iprintf(buffer, " GPIO direction : 0x%x\n", ice->eeprom.gpiodir);
+ snd_iprintf(buffer, " GPIO mask : 0x%x\n", ice->eeprom.gpiomask);
+ snd_iprintf(buffer, " GPIO state : 0x%x\n", ice->eeprom.gpiostate);
+ for (idx = 0x12; idx < ice->eeprom.size; idx++)
+ snd_iprintf(buffer, " Extra #%02i : 0x%x\n", idx, ice->eeprom.data[idx]);
+
+ snd_iprintf(buffer, "\nRegisters:\n");
+
+ snd_iprintf(buffer, " PSDOUT03 : 0x%08x\n", (unsigned)inl(ICEMT1724(ice, ROUTE_PLAYBACK)));
+ for (idx = 0x0; idx < 0x20 ; idx++)
+ snd_iprintf(buffer, " CCS%02x : 0x%02x\n", idx, inb(ice->port+idx));
+ for (idx = 0x0; idx < 0x30 ; idx++)
+ snd_iprintf(buffer, " MT%02x : 0x%02x\n", idx, inb(ice->profi_port+idx));
+}
+
+static void __devinit snd_vt1724_proc_init(ice1712_t * ice)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(ice->card, "ice1724", &entry))
+ snd_info_set_text_ops(entry, ice, 1024, snd_vt1724_proc_read);
+}
+
+/*
+ *
+ */
+
+static int snd_vt1724_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(ice1712_eeprom_t);
+ return 0;
+}
+
+static int snd_vt1724_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_vt1724_eeprom __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "ICE1724 EEPROM",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_vt1724_eeprom_info,
+ .get = snd_vt1724_eeprom_get
+};
+
+/*
+ */
+static int snd_vt1724_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static unsigned int encode_spdif_bits(snd_aes_iec958_t *diga)
+{
+ unsigned int val;
+
+ val = diga->status[0] & 0x03; /* professional, non-audio */
+ if (val & 0x01) {
+ /* professional */
+ if ((diga->status[0] & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
+ val |= 1U << 3;
+ switch (diga->status[0] & IEC958_AES0_PRO_FS) {
+ case IEC958_AES0_PRO_FS_44100:
+ break;
+ case IEC958_AES0_PRO_FS_32000:
+ val |= 3U << 12;
+ break;
+ default:
+ val |= 2U << 12;
+ break;
+ }
+ } else {
+ /* consumer */
+ val |= diga->status[1] & 0x04; /* copyright */
+ if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS)== IEC958_AES0_CON_EMPHASIS_5015)
+ val |= 1U << 3;
+ val |= (unsigned int)(diga->status[1] & 0x3f) << 4; /* category */
+ val |= (unsigned int)(diga->status[3] & IEC958_AES3_CON_FS) << 12; /* fs */
+ }
+ return val;
+}
+
+static void decode_spdif_bits(snd_aes_iec958_t *diga, unsigned int val)
+{
+ memset(diga->status, 0, sizeof(diga->status));
+ diga->status[0] = val & 0x03; /* professional, non-audio */
+ if (val & 0x01) {
+ /* professional */
+ if (val & (1U << 3))
+ diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015;
+ switch ((val >> 12) & 0x7) {
+ case 0:
+ break;
+ case 2:
+ diga->status[0] |= IEC958_AES0_PRO_FS_32000;
+ break;
+ default:
+ diga->status[0] |= IEC958_AES0_PRO_FS_48000;
+ break;
+ }
+ } else {
+ /* consumer */
+ diga->status[0] |= val & (1U << 2); /* copyright */
+ if (val & (1U << 3))
+ diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015;
+ diga->status[1] |= (val >> 4) & 0x3f; /* category */
+ diga->status[3] |= (val >> 12) & 0x07; /* fs */
+ }
+}
+
+static int snd_vt1724_spdif_default_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ val = inw(ICEMT1724(ice, SPDIF_CTRL));
+ decode_spdif_bits(&ucontrol->value.iec958, val);
+ return 0;
+}
+
+static int snd_vt1724_spdif_default_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val, old;
+
+ val = encode_spdif_bits(&ucontrol->value.iec958);
+ spin_lock_irq(&ice->reg_lock);
+ old = inw(ICEMT1724(ice, SPDIF_CTRL));
+ if (val != old)
+ update_spdif_bits(ice, val);
+ spin_unlock_irq(&ice->reg_lock);
+ return (val != old);
+}
+
+static snd_kcontrol_new_t snd_vt1724_spdif_default __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_vt1724_spdif_info,
+ .get = snd_vt1724_spdif_default_get,
+ .put = snd_vt1724_spdif_default_put
+};
+
+static int snd_vt1724_spdif_maskc_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_CON_NOT_COPYRIGHT |
+ IEC958_AES0_CON_EMPHASIS;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
+ IEC958_AES1_CON_CATEGORY;
+ ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+ return 0;
+}
+
+static int snd_vt1724_spdif_maskp_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_PRO_FS |
+ IEC958_AES0_PRO_EMPHASIS;
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_vt1724_spdif_maskc __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_vt1724_spdif_info,
+ .get = snd_vt1724_spdif_maskc_get,
+};
+
+static snd_kcontrol_new_t snd_vt1724_spdif_maskp __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .info = snd_vt1724_spdif_info,
+ .get = snd_vt1724_spdif_maskp_get,
+};
+
+static int snd_vt1724_spdif_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_vt1724_spdif_sw_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = inb(ICEREG1724(ice, SPDIF_CFG)) & VT1724_CFG_SPDIF_OUT_EN ? 1 : 0;
+ return 0;
+}
+
+static int snd_vt1724_spdif_sw_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char old, val;
+
+ spin_lock_irq(&ice->reg_lock);
+ old = val = inb(ICEREG1724(ice, SPDIF_CFG));
+ val &= ~VT1724_CFG_SPDIF_OUT_EN;
+ if (ucontrol->value.integer.value[0])
+ val |= VT1724_CFG_SPDIF_OUT_EN;
+ if (old != val)
+ outb(val, ICEREG1724(ice, SPDIF_CFG));
+ spin_unlock_irq(&ice->reg_lock);
+ return old != val;
+}
+
+static snd_kcontrol_new_t snd_vt1724_spdif_switch __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* FIXME: the following conflict with IEC958 Playback Route */
+ // .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+ .name = "IEC958 Output Switch",
+ .info = snd_vt1724_spdif_sw_info,
+ .get = snd_vt1724_spdif_sw_get,
+ .put = snd_vt1724_spdif_sw_put
+};
+
+
+#if 0 /* NOT USED YET */
+/*
+ * GPIO access from extern
+ */
+
+int snd_vt1724_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+int snd_vt1724_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
+
+ snd_ice1712_save_gpio_status(ice);
+ ucontrol->value.integer.value[0] = (snd_ice1712_gpio_read(ice) & (1 << shift) ? 1 : 0) ^ invert;
+ snd_ice1712_restore_gpio_status(ice);
+ return 0;
+}
+
+int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int shift = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value & (1<<24)) ? mask : 0;
+ unsigned int val, nval;
+
+ if (kcontrol->private_value & (1 << 31))
+ return -EPERM;
+ nval = (ucontrol->value.integer.value[0] ? (1 << shift) : 0) ^ invert;
+ snd_ice1712_save_gpio_status(ice);
+ val = snd_ice1712_gpio_read(ice);
+ nval |= val & ~(1 << shift);
+ if (val != nval)
+ snd_ice1712_gpio_write(ice, nval);
+ snd_ice1712_restore_gpio_status(ice);
+ return val != nval;
+}
+#endif /* NOT USED YET */
+
+/*
+ * rate
+ */
+static int snd_vt1724_pro_internal_clock_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts_1724[] = {
+ "8000", /* 0: 6 */
+ "9600", /* 1: 3 */
+ "11025", /* 2: 10 */
+ "12000", /* 3: 2 */
+ "16000", /* 4: 5 */
+ "22050", /* 5: 9 */
+ "24000", /* 6: 1 */
+ "32000", /* 7: 4 */
+ "44100", /* 8: 8 */
+ "48000", /* 9: 0 */
+ "64000", /* 10: 15 */
+ "88200", /* 11: 11 */
+ "96000", /* 12: 7 */
+ "176400", /* 13: 12 */
+ "192000", /* 14: 14 */
+ "IEC958 Input", /* 15: -- */
+ };
+ static char *texts_1720[] = {
+ "8000", /* 0: 6 */
+ "9600", /* 1: 3 */
+ "11025", /* 2: 10 */
+ "12000", /* 3: 2 */
+ "16000", /* 4: 5 */
+ "22050", /* 5: 9 */
+ "24000", /* 6: 1 */
+ "32000", /* 7: 4 */
+ "44100", /* 8: 8 */
+ "48000", /* 9: 0 */
+ "64000", /* 10: 15 */
+ "88200", /* 11: 11 */
+ "96000", /* 12: 7 */
+ "IEC958 Input", /* 13: -- */
+ };
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = ice->vt1720 ? 14 : 16;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ ice->vt1720 ? texts_1720[uinfo->value.enumerated.item] :
+ texts_1724[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_vt1724_pro_internal_clock_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ static unsigned char xlate[16] = {
+ 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10
+ };
+ unsigned char val;
+
+ spin_lock_irq(&ice->reg_lock);
+ if (is_spdif_master(ice)) {
+ ucontrol->value.enumerated.item[0] = ice->vt1720 ? 13 : 15;
+ } else {
+ val = xlate[inb(ICEMT1724(ice, RATE)) & 15];
+ if (val == 255) {
+ snd_BUG();
+ val = 0;
+ }
+ ucontrol->value.enumerated.item[0] = val;
+ }
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_vt1724_pro_internal_clock_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char oval;
+ int rate;
+ int change = 0;
+ int spdif = ice->vt1720 ? 13 : 15;
+
+ spin_lock_irq(&ice->reg_lock);
+ oval = inb(ICEMT1724(ice, RATE));
+ if (ucontrol->value.enumerated.item[0] == spdif) {
+ outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+ } else {
+ rate = rates[ucontrol->value.integer.value[0] % 15];
+ if (rate <= get_max_rate(ice)) {
+ PRO_RATE_DEFAULT = rate;
+ spin_unlock_irq(&ice->reg_lock);
+ snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1);
+ spin_lock_irq(&ice->reg_lock);
+ }
+ }
+ change = inb(ICEMT1724(ice, RATE)) != oval;
+ spin_unlock_irq(&ice->reg_lock);
+
+ if ((oval & VT1724_SPDIF_MASTER) != (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER)) {
+ /* notify akm chips as well */
+ if (is_spdif_master(ice)) {
+ unsigned int i;
+ for (i = 0; i < ice->akm_codecs; i++) {
+ if (ice->akm[i].ops.set_rate_val)
+ ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
+ }
+ }
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_vt1724_pro_internal_clock __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Internal Clock",
+ .info = snd_vt1724_pro_internal_clock_info,
+ .get = snd_vt1724_pro_internal_clock_get,
+ .put = snd_vt1724_pro_internal_clock_put
+};
+
+static int snd_vt1724_pro_rate_locking_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_vt1724_pro_rate_locking_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.integer.value[0] = PRO_RATE_LOCKED;
+ return 0;
+}
+
+static int snd_vt1724_pro_rate_locking_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change = 0, nval;
+
+ nval = ucontrol->value.integer.value[0] ? 1 : 0;
+ spin_lock_irq(&ice->reg_lock);
+ change = PRO_RATE_LOCKED != nval;
+ PRO_RATE_LOCKED = nval;
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_vt1724_pro_rate_locking __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Rate Locking",
+ .info = snd_vt1724_pro_rate_locking_info,
+ .get = snd_vt1724_pro_rate_locking_get,
+ .put = snd_vt1724_pro_rate_locking_put
+};
+
+static int snd_vt1724_pro_rate_reset_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_vt1724_pro_rate_reset_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.integer.value[0] = PRO_RATE_RESET ? 1 : 0;
+ return 0;
+}
+
+static int snd_vt1724_pro_rate_reset_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int change = 0, nval;
+
+ nval = ucontrol->value.integer.value[0] ? 1 : 0;
+ spin_lock_irq(&ice->reg_lock);
+ change = PRO_RATE_RESET != nval;
+ PRO_RATE_RESET = nval;
+ spin_unlock_irq(&ice->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_vt1724_pro_rate_reset __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Rate Reset",
+ .info = snd_vt1724_pro_rate_reset_info,
+ .get = snd_vt1724_pro_rate_reset_get,
+ .put = snd_vt1724_pro_rate_reset_put
+};
+
+
+/*
+ * routing
+ */
+static int snd_vt1724_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {
+ "PCM Out", /* 0 */
+ "H/W In 0", "H/W In 1", /* 1-2 */
+ "IEC958 In L", "IEC958 In R", /* 3-4 */
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 5;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static inline int analog_route_shift(int idx)
+{
+ return (idx % 2) * 12 + ((idx / 2) * 3) + 8;
+}
+
+static inline int digital_route_shift(int idx)
+{
+ return idx * 3;
+}
+
+static int get_route_val(ice1712_t *ice, int shift)
+{
+ unsigned long val;
+ unsigned char eitem;
+ static unsigned char xlate[8] = {
+ 0, 255, 1, 2, 255, 255, 3, 4,
+ };
+
+ val = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
+ val >>= shift;
+ val &= 7; //we now have 3 bits per output
+ eitem = xlate[val];
+ if (eitem == 255) {
+ snd_BUG();
+ return 0;
+ }
+ return eitem;
+}
+
+static int put_route_val(ice1712_t *ice, unsigned int val, int shift)
+{
+ unsigned int old_val, nval;
+ int change;
+ static unsigned char xroute[8] = {
+ 0, /* PCM */
+ 2, /* PSDIN0 Left */
+ 3, /* PSDIN0 Right */
+ 6, /* SPDIN Left */
+ 7, /* SPDIN Right */
+ };
+
+ nval = xroute[val % 5];
+ val = old_val = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
+ val &= ~(0x07 << shift);
+ val |= nval << shift;
+ change = val != old_val;
+ if (change)
+ outl(val, ICEMT1724(ice, ROUTE_PLAYBACK));
+ return change;
+}
+
+static int snd_vt1724_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ ucontrol->value.enumerated.item[0] = get_route_val(ice, analog_route_shift(idx));
+ return 0;
+}
+
+static int snd_vt1724_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ return put_route_val(ice, ucontrol->value.enumerated.item[0],
+ analog_route_shift(idx));
+}
+
+static int snd_vt1724_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ ucontrol->value.enumerated.item[0] = get_route_val(ice, digital_route_shift(idx));
+ return 0;
+}
+
+static int snd_vt1724_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ return put_route_val(ice, ucontrol->value.enumerated.item[0],
+ digital_route_shift(idx));
+}
+
+static snd_kcontrol_new_t snd_vt1724_mixer_pro_analog_route __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "H/W Playback Route",
+ .info = snd_vt1724_pro_route_info,
+ .get = snd_vt1724_pro_route_analog_get,
+ .put = snd_vt1724_pro_route_analog_put,
+};
+
+static snd_kcontrol_new_t snd_vt1724_mixer_pro_spdif_route __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
+ .info = snd_vt1724_pro_route_info,
+ .get = snd_vt1724_pro_route_spdif_get,
+ .put = snd_vt1724_pro_route_spdif_put,
+ .count = 2,
+};
+
+
+static int snd_vt1724_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 22; /* FIXME: for compatibility with ice1712... */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_vt1724_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx;
+
+ spin_lock_irq(&ice->reg_lock);
+ for (idx = 0; idx < 22; idx++) {
+ outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX));
+ ucontrol->value.integer.value[idx] = inb(ICEMT1724(ice, MONITOR_PEAKDATA));
+ }
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_vt1724_mixer_pro_peak __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Multi Track Peak",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_vt1724_pro_peak_info,
+ .get = snd_vt1724_pro_peak_get
+};
+
+/*
+ *
+ */
+
+static struct snd_ice1712_card_info no_matched __devinitdata;
+
+static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+ snd_vt1724_revo_cards,
+ snd_vt1724_amp_cards,
+ snd_vt1724_aureon_cards,
+ snd_vt1720_mobo_cards,
+ snd_vt1720_pontis_cards,
+ snd_vt1724_prodigy192_cards,
+ snd_vt1724_juli_cards,
+ snd_vt1724_phase_cards,
+ NULL,
+};
+
+
+/*
+ */
+
+static void wait_i2c_busy(ice1712_t *ice)
+{
+ int t = 0x10000;
+ while ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY) && t--)
+ ;
+ if (t == -1)
+ printk(KERN_ERR "ice1724: i2c busy timeout\n");
+}
+
+unsigned char snd_vt1724_read_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr)
+{
+ unsigned char val;
+
+ down(&ice->i2c_mutex);
+ outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
+ outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
+ wait_i2c_busy(ice);
+ val = inb(ICEREG1724(ice, I2C_DATA));
+ up(&ice->i2c_mutex);
+ //printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
+ return val;
+}
+
+void snd_vt1724_write_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr, unsigned char data)
+{
+ down(&ice->i2c_mutex);
+ wait_i2c_busy(ice);
+ //printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
+ outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
+ outb(data, ICEREG1724(ice, I2C_DATA));
+ outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
+ wait_i2c_busy(ice);
+ up(&ice->i2c_mutex);
+}
+
+static int __devinit snd_vt1724_read_eeprom(ice1712_t *ice, const char *modelname)
+{
+ const int dev = 0xa0; /* EEPROM device address */
+ unsigned int i, size;
+ struct snd_ice1712_card_info **tbl, *c;
+
+ if (! modelname || ! *modelname) {
+ ice->eeprom.subvendor = 0;
+ if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) != 0)
+ ice->eeprom.subvendor =
+ (snd_vt1724_read_i2c(ice, dev, 0x00) << 0) |
+ (snd_vt1724_read_i2c(ice, dev, 0x01) << 8) |
+ (snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |
+ (snd_vt1724_read_i2c(ice, dev, 0x03) << 24);
+ if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
+ /* invalid subvendor from EEPROM, try the PCI subststem ID instead */
+ u16 vendor, device;
+ pci_read_config_word(ice->pci, PCI_SUBSYSTEM_VENDOR_ID, &vendor);
+ pci_read_config_word(ice->pci, PCI_SUBSYSTEM_ID, &device);
+ ice->eeprom.subvendor = ((unsigned int)swab16(vendor) << 16) | swab16(device);
+ if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
+ printk(KERN_ERR "ice1724: No valid ID is found\n");
+ return -ENXIO;
+ }
+ }
+ }
+ for (tbl = card_tables; *tbl; tbl++) {
+ for (c = *tbl; c->subvendor; c++) {
+ if (modelname && c->model && ! strcmp(modelname, c->model)) {
+ printk(KERN_INFO "ice1724: Using board model %s\n", c->name);
+ ice->eeprom.subvendor = c->subvendor;
+ } else if (c->subvendor != ice->eeprom.subvendor)
+ continue;
+ if (! c->eeprom_size || ! c->eeprom_data)
+ goto found;
+ /* if the EEPROM is given by the driver, use it */
+ snd_printdd("using the defined eeprom..\n");
+ ice->eeprom.version = 2;
+ ice->eeprom.size = c->eeprom_size + 6;
+ memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
+ goto read_skipped;
+ }
+ }
+ printk(KERN_WARNING "ice1724: No matching model found for ID 0x%x\n", ice->eeprom.subvendor);
+
+ found:
+ ice->eeprom.size = snd_vt1724_read_i2c(ice, dev, 0x04);
+ if (ice->eeprom.size < 6)
+ ice->eeprom.size = 32;
+ else if (ice->eeprom.size > 32) {
+ printk(KERN_ERR "ice1724: Invalid EEPROM (size = %i)\n", ice->eeprom.size);
+ return -EIO;
+ }
+ ice->eeprom.version = snd_vt1724_read_i2c(ice, dev, 0x05);
+ if (ice->eeprom.version != 2)
+ printk(KERN_WARNING "ice1724: Invalid EEPROM version %i\n", ice->eeprom.version);
+ size = ice->eeprom.size - 6;
+ for (i = 0; i < size; i++)
+ ice->eeprom.data[i] = snd_vt1724_read_i2c(ice, dev, i + 6);
+
+ read_skipped:
+ ice->eeprom.gpiomask = eeprom_triple(ice, ICE_EEP2_GPIO_MASK);
+ ice->eeprom.gpiostate = eeprom_triple(ice, ICE_EEP2_GPIO_STATE);
+ ice->eeprom.gpiodir = eeprom_triple(ice, ICE_EEP2_GPIO_DIR);
+
+ return 0;
+}
+
+
+
+static int __devinit snd_vt1724_chip_init(ice1712_t *ice)
+{
+ outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
+ udelay(200);
+ outb(0, ICEREG1724(ice, CONTROL));
+ udelay(200);
+ outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
+ outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
+ outb(ice->eeprom.data[ICE_EEP2_I2S], ICEREG1724(ice, I2S_FEATURES));
+ outb(ice->eeprom.data[ICE_EEP2_SPDIF], ICEREG1724(ice, SPDIF_CFG));
+
+ ice->gpio.write_mask = ice->eeprom.gpiomask;
+ ice->gpio.direction = ice->eeprom.gpiodir;
+ snd_vt1724_set_gpio_mask(ice, ice->eeprom.gpiomask);
+ snd_vt1724_set_gpio_dir(ice, ice->eeprom.gpiodir);
+ snd_vt1724_set_gpio_data(ice, ice->eeprom.gpiostate);
+
+ outb(0, ICEREG1724(ice, POWERDOWN));
+
+ return 0;
+}
+
+static int __devinit snd_vt1724_spdif_build_controls(ice1712_t *ice)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+
+ snd_assert(ice->pcm != NULL, return -EIO);
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm->device;
+#if 0 /* use default only */
+ err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice));
+ if (err < 0)
+ return err;
+ kctl->id.device = ice->pcm->device;
+ ice->spdif.stream_ctl = kctl;
+#endif
+ return 0;
+}
+
+
+static int __devinit snd_vt1724_build_controls(ice1712_t *ice)
+{
+ int err;
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_eeprom, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_internal_clock, ice));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_rate_locking, ice));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_rate_reset, ice));
+ if (err < 0)
+ return err;
+
+ if (ice->num_total_dacs > 0) {
+ snd_kcontrol_new_t tmp = snd_vt1724_mixer_pro_analog_route;
+ tmp.count = ice->num_total_dacs;
+ if (ice->vt1720 && tmp.count > 2)
+ tmp.count = 2;
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice));
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int snd_vt1724_free(ice1712_t *ice)
+{
+ if (! ice->port)
+ goto __hw_end;
+ /* mask all interrupts */
+ outb(0xff, ICEMT1724(ice, DMA_INT_MASK));
+ outb(0xff, ICEREG1724(ice, IRQMASK));
+ /* --- */
+ __hw_end:
+ if (ice->irq >= 0) {
+ synchronize_irq(ice->irq);
+ free_irq(ice->irq, (void *) ice);
+ }
+ pci_release_regions(ice->pci);
+ snd_ice1712_akm4xxx_free(ice);
+ pci_disable_device(ice->pci);
+ kfree(ice);
+ return 0;
+}
+
+static int snd_vt1724_dev_free(snd_device_t *device)
+{
+ ice1712_t *ice = device->device_data;
+ return snd_vt1724_free(ice);
+}
+
+static int __devinit snd_vt1724_create(snd_card_t * card,
+ struct pci_dev *pci,
+ const char *modelname,
+ ice1712_t ** r_ice1712)
+{
+ ice1712_t *ice;
+ int err;
+ unsigned char mask;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_vt1724_dev_free,
+ };
+
+ *r_ice1712 = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ ice = kcalloc(1, sizeof(*ice), GFP_KERNEL);
+ if (ice == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ ice->vt1724 = 1;
+ spin_lock_init(&ice->reg_lock);
+ init_MUTEX(&ice->gpio_mutex);
+ init_MUTEX(&ice->open_mutex);
+ init_MUTEX(&ice->i2c_mutex);
+ ice->gpio.set_mask = snd_vt1724_set_gpio_mask;
+ ice->gpio.set_dir = snd_vt1724_set_gpio_dir;
+ ice->gpio.set_data = snd_vt1724_set_gpio_data;
+ ice->gpio.get_data = snd_vt1724_get_gpio_data;
+ ice->card = card;
+ ice->pci = pci;
+ ice->irq = -1;
+ pci_set_master(pci);
+ snd_vt1724_proc_init(ice);
+ synchronize_irq(pci->irq);
+
+ if ((err = pci_request_regions(pci, "ICE1724")) < 0) {
+ kfree(ice);
+ pci_disable_device(pci);
+ return err;
+ }
+ ice->port = pci_resource_start(pci, 0);
+ ice->profi_port = pci_resource_start(pci, 1);
+
+ if (request_irq(pci->irq, snd_vt1724_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1724", (void *) ice)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_vt1724_free(ice);
+ return -EIO;
+ }
+
+ ice->irq = pci->irq;
+
+ if (snd_vt1724_read_eeprom(ice, modelname) < 0) {
+ snd_vt1724_free(ice);
+ return -EIO;
+ }
+ if (snd_vt1724_chip_init(ice) < 0) {
+ snd_vt1724_free(ice);
+ return -EIO;
+ }
+
+ /* unmask used interrupts */
+ if (! (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401))
+ mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
+ else
+ mask = 0;
+ outb(mask, ICEREG1724(ice, IRQMASK));
+ /* don't handle FIFO overrun/underruns (just yet), since they cause machine lockups */
+ outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+ snd_vt1724_free(ice);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_ice1712 = ice;
+ return 0;
+}
+
+
+/*
+ *
+ * Registration
+ *
+ */
+
+static int __devinit snd_vt1724_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ ice1712_t *ice;
+ int pcm_dev = 0, err;
+ struct snd_ice1712_card_info **tbl, *c;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ strcpy(card->driver, "ICE1724");
+ strcpy(card->shortname, "ICEnsemble ICE1724");
+
+ if ((err = snd_vt1724_create(card, pci, model[dev], &ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ for (tbl = card_tables; *tbl; tbl++) {
+ for (c = *tbl; c->subvendor; c++) {
+ if (c->subvendor == ice->eeprom.subvendor) {
+ strcpy(card->shortname, c->name);
+ if (c->driver) /* specific driver? */
+ strcpy(card->driver, c->driver);
+ if (c->chip_init) {
+ if ((err = c->chip_init(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ goto __found;
+ }
+ }
+ }
+ c = &no_matched;
+ __found:
+
+ if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_vt1724_pcm_spdif(ice, pcm_dev++)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_vt1724_ac97_mixer(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_vt1724_build_controls(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */
+ if ((err = snd_vt1724_spdif_build_controls(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+
+ if (c->build_controls) {
+ if ((err = c->build_controls(ice)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+
+ if (! c->no_mpu401) {
+ if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
+ ICEREG1724(ice, MPU_CTRL), 1,
+ ice->irq, 0,
+ &ice->rmidi[0])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ }
+
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, ice->port, ice->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_vt1724_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "ICE1724",
+ .id_table = snd_vt1724_ids,
+ .probe = snd_vt1724_probe,
+ .remove = __devexit_p(snd_vt1724_remove),
+};
+
+static int __init alsa_card_ice1724_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_ice1724_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ice1724_init)
+module_exit(alsa_card_ice1724_exit)
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
new file mode 100644
index 00000000000..3fb297b969c
--- /dev/null
+++ b/sound/pci/ice1712/juli.c
@@ -0,0 +1,230 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for ESI Juli@ cards
+ *
+ * Copyright (c) 2004 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "juli.h"
+
+/*
+ * chip addresses on I2C bus
+ */
+#define AK4114_ADDR 0x20 /* S/PDIF receiver */
+#define AK4358_ADDR 0x22 /* DAC */
+
+/*
+ * GPIO pins
+ */
+#define GPIO_FREQ_MASK (3<<0)
+#define GPIO_FREQ_32KHZ (0<<0)
+#define GPIO_FREQ_44KHZ (1<<0)
+#define GPIO_FREQ_48KHZ (2<<0)
+#define GPIO_MULTI_MASK (3<<2)
+#define GPIO_MULTI_4X (0<<2)
+#define GPIO_MULTI_2X (1<<2)
+#define GPIO_MULTI_1X (2<<2) /* also external */
+#define GPIO_MULTI_HALF (3<<2)
+#define GPIO_INTERNAL_CLOCK (1<<4)
+#define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */
+#define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */
+#define GPIO_AK5385A_CKS0 (1<<8)
+#define GPIO_AK5385A_DFS0 (1<<9) /* swapped with DFS1 according doc? */
+#define GPIO_AK5385A_DFS1 (1<<10)
+#define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */
+#define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */
+#define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */
+#define GPIO_AK5385A_MCLK (1<<14) /* must be 0 */
+#define GPIO_MUTE_CONTROL (1<<15) /* 0 = off, 1 = on */
+
+static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val)
+{
+ snd_vt1724_write_i2c((ice1712_t *)private_data, AK4114_ADDR, reg, val);
+}
+
+static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)
+{
+ return snd_vt1724_read_i2c((ice1712_t *)private_data, AK4114_ADDR, reg);
+}
+
+/*
+ * AK4358 section
+ */
+
+static void juli_akm_lock(akm4xxx_t *ak, int chip)
+{
+}
+
+static void juli_akm_unlock(akm4xxx_t *ak, int chip)
+{
+}
+
+static void juli_akm_write(akm4xxx_t *ak, int chip,
+ unsigned char addr, unsigned char data)
+{
+ ice1712_t *ice = ak->private_data[0];
+
+ snd_assert(chip == 0, return);
+ snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data);
+}
+
+/*
+ * change the rate of envy24HT, AK4358
+ */
+static void juli_akm_set_rate_val(akm4xxx_t *ak, unsigned int rate)
+{
+ unsigned char old, tmp, dfs;
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+
+ /* adjust DFS on codecs */
+ if (rate > 96000)
+ dfs = 2;
+ else if (rate > 48000)
+ dfs = 1;
+ else
+ dfs = 0;
+
+ tmp = snd_akm4xxx_get(ak, 0, 2);
+ old = (tmp >> 4) & 0x03;
+ if (old == dfs)
+ return;
+ /* reset DFS */
+ snd_akm4xxx_reset(ak, 1);
+ tmp = snd_akm4xxx_get(ak, 0, 2);
+ tmp &= ~(0x03 << 4);
+ tmp |= dfs << 4;
+ snd_akm4xxx_set(ak, 0, 2, tmp);
+ snd_akm4xxx_reset(ak, 0);
+}
+
+static akm4xxx_t akm_juli_dac __devinitdata = {
+ .type = SND_AK4358,
+ .num_dacs = 2,
+ .ops = {
+ .lock = juli_akm_lock,
+ .unlock = juli_akm_unlock,
+ .write = juli_akm_write,
+ .set_rate_val = juli_akm_set_rate_val
+ }
+};
+
+static int __devinit juli_add_controls(ice1712_t *ice)
+{
+ return snd_ice1712_akm4xxx_build_controls(ice);
+}
+
+/*
+ * initialize the chip
+ */
+static int __devinit juli_init(ice1712_t *ice)
+{
+ static unsigned char ak4114_init_vals[] = {
+ /* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
+ /* AK4114_REQ_FORMAT */ AK4114_DIF_I24I2S,
+ /* AK4114_REG_IO0 */ AK4114_TX1E,
+ /* AK4114_REG_IO1 */ AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1),
+ /* AK4114_REG_INT0_MASK */ 0,
+ /* AK4114_REG_INT1_MASK */ 0
+ };
+ static unsigned char ak4114_init_txcsb[] = {
+ 0x41, 0x02, 0x2c, 0x00, 0x00
+ };
+ int err;
+ akm4xxx_t *ak;
+
+#if 0
+ for (err = 0; err < 0x20; err++)
+ juli_ak4114_read(ice, err);
+ juli_ak4114_write(ice, 0, 0x0f);
+ juli_ak4114_read(ice, 0);
+ juli_ak4114_read(ice, 1);
+#endif
+ err = snd_ak4114_create(ice->card,
+ juli_ak4114_read,
+ juli_ak4114_write,
+ ak4114_init_vals, ak4114_init_txcsb,
+ ice, &ice->spec.juli.ak4114);
+ if (err < 0)
+ return err;
+
+ ice->spec.juli.analog = ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT;
+
+ if (ice->spec.juli.analog) {
+ printk(KERN_INFO "juli@: analog I/O detected\n");
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 2;
+
+ ak = ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ak)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+ if ((err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice)) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Juli@ boards don't provide the EEPROM data except for the vendor IDs.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char juli_eeprom[] __devinitdata = {
+ 0x20, /* SYSCONF: clock 512, mpu401, 1xADC, 1xDACs */
+ 0x80, /* ACLINK: I2S */
+ 0xf8, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0x9f, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x7f, /* GPIO_DIR2 */
+ 0x9f, /* GPIO_MASK */
+ 0xff, /* GPIO_MASK1 */
+ 0x7f, /* GPIO_MASK2 */
+ 0x16, /* GPIO_STATE: internal clock, multiple 1x, 48kHz */
+ 0x80, /* GPIO_STATE1: mute */
+ 0x00, /* GPIO_STATE2 */
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_JULI,
+ .name = "ESI Juli@",
+ .model = "juli",
+ .chip_init = juli_init,
+ .build_controls = juli_add_controls,
+ .eeprom_size = sizeof(juli_eeprom),
+ .eeprom_data = juli_eeprom,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/juli.h b/sound/pci/ice1712/juli.h
new file mode 100644
index 00000000000..d9f8534fd92
--- /dev/null
+++ b/sound/pci/ice1712/juli.h
@@ -0,0 +1,10 @@
+#ifndef __SOUND_JULI_H
+#define __SOUND_JULI_H
+
+#define JULI_DEVICE_DESC "{ESI,Juli@},"
+
+#define VT1724_SUBDEVICE_JULI 0x31305345 /* Juli@ */
+
+extern struct snd_ice1712_card_info snd_vt1724_juli_cards[];
+
+#endif /* __SOUND_JULI_H */
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
new file mode 100644
index 00000000000..d1f90832443
--- /dev/null
+++ b/sound/pci/ice1712/phase.c
@@ -0,0 +1,138 @@
+/*
+ * ALSA driver for ICEnsemble ICE1724 (Envy24)
+ *
+ * Lowlevel functions for Terratec PHASE 22
+ *
+ * Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* PHASE 22 overview:
+ * Audio controller: VIA Envy24HT-S (slightly trimmed down version of Envy24HT)
+ * Analog chip: AK4524 (partially via Philip's 74HCT125)
+ * Digital receiver: CS8414-CS (not supported in this release)
+ *
+ * Envy connects to AK4524
+ * - CS directly from GPIO 10
+ * - CCLK via 74HCT125's gate #4 from GPIO 4
+ * - CDTI via 74HCT125's gate #2 from GPIO 5
+ * CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "phase.h"
+
+static akm4xxx_t akm_phase22 __devinitdata = {
+ .type = SND_AK4524,
+ .num_dacs = 2,
+ .num_adcs = 2,
+};
+
+static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 1,
+ .data_mask = 1 << 4,
+ .clk_mask = 1 << 5,
+ .cs_mask = 1 << 10,
+ .cs_addr = 1 << 10,
+ .cs_none = 0,
+ .add_flags = 1 << 3,
+ .mask_flags = 0,
+};
+
+static int __devinit phase22_init(ice1712_t *ice)
+{
+ akm4xxx_t *ak;
+ int err;
+
+ // Configure DAC/ADC description for generic part of ice1724
+ switch (ice->eeprom.subvendor) {
+ case VT1724_SUBDEVICE_PHASE22:
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 2;
+ ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+
+ // Initialize analog chips
+ ak = ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ak)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+ switch (ice->eeprom.subvendor) {
+ case VT1724_SUBDEVICE_PHASE22:
+ if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0)
+ return err;
+ break;
+ }
+
+ return 0;
+}
+
+static int __devinit phase22_add_controls(ice1712_t *ice)
+{
+ int err = 0;
+
+ switch (ice->eeprom.subvendor) {
+ case VT1724_SUBDEVICE_PHASE22:
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static unsigned char phase22_eeprom[] __devinitdata = {
+ 0x00, /* SYSCONF: 1xADC, 1xDACs */
+ 0x80, /* ACLINK: I2S */
+ 0xf8, /* I2S: vol, 96k, 24bit*/
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xFF, /* GPIO_DIR */
+ 0xFF, /* GPIO_DIR1 */
+ 0xFF, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE: */
+ 0x00, /* GPIO_STATE1: */
+ 0x00, /* GPIO_STATE2 */
+};
+
+struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_PHASE22,
+ .name = "Terratec PHASE 22",
+ .model = "phase22",
+ .chip_init = phase22_init,
+ .build_controls = phase22_add_controls,
+ .eeprom_size = sizeof(phase22_eeprom),
+ .eeprom_data = phase22_eeprom,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h
new file mode 100644
index 00000000000..6230cf16989
--- /dev/null
+++ b/sound/pci/ice1712/phase.h
@@ -0,0 +1,34 @@
+#ifndef __SOUND_PHASE_H
+#define __SOUND_PHASE_H
+
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for Terratec PHASE 22
+ *
+ * Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"
+
+#define VT1724_SUBDEVICE_PHASE22 0x3b155011
+
+/* entry point */
+extern struct snd_ice1712_card_info snd_vt1724_phase_cards[];
+
+#endif /* __SOUND_PHASE */
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
new file mode 100644
index 00000000000..25f827d8fbd
--- /dev/null
+++ b/sound/pci/ice1712/pontis.c
@@ -0,0 +1,849 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Pontis MS300
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "pontis.h"
+
+/* I2C addresses */
+#define WM_DEV 0x34
+#define CS_DEV 0x20
+
+/* WM8776 registers */
+#define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */
+#define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */
+#define WM_HP_MASTER 0x02 /* headphone master (both channels), override LLR */
+#define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */
+#define WM_DAC_ATTEN_R 0x04
+#define WM_DAC_MASTER 0x05
+#define WM_PHASE_SWAP 0x06 /* DAC phase swap */
+#define WM_DAC_CTRL1 0x07
+#define WM_DAC_MUTE 0x08
+#define WM_DAC_CTRL2 0x09
+#define WM_DAC_INT 0x0a
+#define WM_ADC_INT 0x0b
+#define WM_MASTER_CTRL 0x0c
+#define WM_POWERDOWN 0x0d
+#define WM_ADC_ATTEN_L 0x0e
+#define WM_ADC_ATTEN_R 0x0f
+#define WM_ALC_CTRL1 0x10
+#define WM_ALC_CTRL2 0x11
+#define WM_ALC_CTRL3 0x12
+#define WM_NOISE_GATE 0x13
+#define WM_LIMITER 0x14
+#define WM_ADC_MUX 0x15
+#define WM_OUT_MUX 0x16
+#define WM_RESET 0x17
+
+/*
+ * GPIO
+ */
+#define PONTIS_CS_CS (1<<4) /* CS */
+#define PONTIS_CS_CLK (1<<5) /* CLK */
+#define PONTIS_CS_RDATA (1<<6) /* CS8416 -> VT1720 */
+#define PONTIS_CS_WDATA (1<<7) /* VT1720 -> CS8416 */
+
+
+/*
+ * get the current register value of WM codec
+ */
+static unsigned short wm_get(ice1712_t *ice, int reg)
+{
+ reg <<= 1;
+ return ((unsigned short)ice->akm[0].images[reg] << 8) |
+ ice->akm[0].images[reg + 1];
+}
+
+/*
+ * set the register value of WM codec and remember it
+ */
+static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val)
+{
+ unsigned short cval;
+ cval = (reg << 9) | val;
+ snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
+}
+
+static void wm_put(ice1712_t *ice, int reg, unsigned short val)
+{
+ wm_put_nocache(ice, reg, val);
+ reg <<= 1;
+ ice->akm[0].images[reg] = val >> 8;
+ ice->akm[0].images[reg + 1] = val;
+}
+
+/*
+ * DAC volume attenuation mixer control (-64dB to 0dB)
+ */
+
+#define DAC_0dB 0xff
+#define DAC_RES 128
+#define DAC_MIN (DAC_0dB - DAC_RES)
+
+static int wm_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* mute */
+ uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */
+ return 0;
+}
+
+static int wm_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+ int i;
+
+ down(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ val = wm_get(ice, WM_DAC_ATTEN_L + i) & 0xff;
+ val = val > DAC_MIN ? (val - DAC_MIN) : 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short oval, nval;
+ int i, idx, change = 0;
+
+ down(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ nval = ucontrol->value.integer.value[i];
+ nval = (nval ? (nval + DAC_MIN) : 0) & 0xff;
+ idx = WM_DAC_ATTEN_L + i;
+ oval = wm_get(ice, idx) & 0xff;
+ if (oval != nval) {
+ wm_put(ice, idx, nval);
+ wm_put_nocache(ice, idx, nval | 0x100);
+ change = 1;
+ }
+ }
+ up(&ice->gpio_mutex);
+ return change;
+}
+
+/*
+ * ADC gain mixer control (-64dB to 0dB)
+ */
+
+#define ADC_0dB 0xcf
+#define ADC_RES 128
+#define ADC_MIN (ADC_0dB - ADC_RES)
+
+static int wm_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* mute (-64dB) */
+ uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */
+ return 0;
+}
+
+static int wm_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+ int i;
+
+ down(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
+ val = val > ADC_MIN ? (val - ADC_MIN) : 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short ovol, nvol;
+ int i, idx, change = 0;
+
+ down(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ nvol = ucontrol->value.integer.value[i];
+ nvol = nvol ? (nvol + ADC_MIN) : 0;
+ idx = WM_ADC_ATTEN_L + i;
+ ovol = wm_get(ice, idx) & 0xff;
+ if (ovol != nvol) {
+ wm_put(ice, idx, nvol);
+ change = 1;
+ }
+ }
+ up(&ice->gpio_mutex);
+ return change;
+}
+
+/*
+ * ADC input mux mixer control
+ */
+static int wm_adc_mux_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm_adc_mux_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int bit = kcontrol->private_value;
+
+ down(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_mux_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int bit = kcontrol->private_value;
+ unsigned short oval, nval;
+ int change;
+
+ down(&ice->gpio_mutex);
+ nval = oval = wm_get(ice, WM_ADC_MUX);
+ if (ucontrol->value.integer.value[0])
+ nval |= (1 << bit);
+ else
+ nval &= ~(1 << bit);
+ change = nval != oval;
+ if (change) {
+ wm_put(ice, WM_ADC_MUX, nval);
+ }
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+/*
+ * Analog bypass (In -> Out)
+ */
+static int wm_bypass_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm_bypass_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ down(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_bypass_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val, oval;
+ int change = 0;
+
+ down(&ice->gpio_mutex);
+ val = oval = wm_get(ice, WM_OUT_MUX);
+ if (ucontrol->value.integer.value[0])
+ val |= 0x04;
+ else
+ val &= ~0x04;
+ if (val != oval) {
+ wm_put(ice, WM_OUT_MUX, val);
+ change = 1;
+ }
+ up(&ice->gpio_mutex);
+ return change;
+}
+
+/*
+ * Left/Right swap
+ */
+static int wm_chswap_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm_chswap_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ down(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_chswap_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val, oval;
+ int change = 0;
+
+ down(&ice->gpio_mutex);
+ oval = wm_get(ice, WM_DAC_CTRL1);
+ val = oval & 0x0f;
+ if (ucontrol->value.integer.value[0])
+ val |= 0x60;
+ else
+ val |= 0x90;
+ if (val != oval) {
+ wm_put(ice, WM_DAC_CTRL1, val);
+ wm_put_nocache(ice, WM_DAC_CTRL1, val);
+ change = 1;
+ }
+ up(&ice->gpio_mutex);
+ return change;
+}
+
+/*
+ * write data in the SPI mode
+ */
+static void set_gpio_bit(ice1712_t *ice, unsigned int bit, int val)
+{
+ unsigned int tmp = snd_ice1712_gpio_read(ice);
+ if (val)
+ tmp |= bit;
+ else
+ tmp &= ~bit;
+ snd_ice1712_gpio_write(ice, tmp);
+}
+
+static void spi_send_byte(ice1712_t *ice, unsigned char data)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ set_gpio_bit(ice, PONTIS_CS_CLK, 0);
+ udelay(1);
+ set_gpio_bit(ice, PONTIS_CS_WDATA, data & 0x80);
+ udelay(1);
+ set_gpio_bit(ice, PONTIS_CS_CLK, 1);
+ udelay(1);
+ data <<= 1;
+ }
+}
+
+static unsigned int spi_read_byte(ice1712_t *ice)
+{
+ int i;
+ unsigned int val = 0;
+
+ for (i = 0; i < 8; i++) {
+ val <<= 1;
+ set_gpio_bit(ice, PONTIS_CS_CLK, 0);
+ udelay(1);
+ if (snd_ice1712_gpio_read(ice) & PONTIS_CS_RDATA)
+ val |= 1;
+ udelay(1);
+ set_gpio_bit(ice, PONTIS_CS_CLK, 1);
+ udelay(1);
+ }
+ return val;
+}
+
+
+static void spi_write(ice1712_t *ice, unsigned int dev, unsigned int reg, unsigned int data)
+{
+ snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK);
+ snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK));
+ set_gpio_bit(ice, PONTIS_CS_CS, 0);
+ spi_send_byte(ice, dev & ~1); /* WRITE */
+ spi_send_byte(ice, reg); /* MAP */
+ spi_send_byte(ice, data); /* DATA */
+ /* trigger */
+ set_gpio_bit(ice, PONTIS_CS_CS, 1);
+ udelay(1);
+ /* restore */
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+}
+
+static unsigned int spi_read(ice1712_t *ice, unsigned int dev, unsigned int reg)
+{
+ unsigned int val;
+ snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK);
+ snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK));
+ set_gpio_bit(ice, PONTIS_CS_CS, 0);
+ spi_send_byte(ice, dev & ~1); /* WRITE */
+ spi_send_byte(ice, reg); /* MAP */
+ /* trigger */
+ set_gpio_bit(ice, PONTIS_CS_CS, 1);
+ udelay(1);
+ set_gpio_bit(ice, PONTIS_CS_CS, 0);
+ spi_send_byte(ice, dev | 1); /* READ */
+ val = spi_read_byte(ice);
+ /* trigger */
+ set_gpio_bit(ice, PONTIS_CS_CS, 1);
+ udelay(1);
+ /* restore */
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+ return val;
+}
+
+
+/*
+ * SPDIF input source
+ */
+static int cs_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[] = {
+ "Coax", /* RXP0 */
+ "Optical", /* RXP1 */
+ "CD", /* RXP2 */
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int cs_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ down(&ice->gpio_mutex);
+ ucontrol->value.enumerated.item[0] = ice->gpio.saved[0];
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int cs_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int change = 0;
+
+ down(&ice->gpio_mutex);
+ if (ucontrol->value.enumerated.item[0] != ice->gpio.saved[0]) {
+ ice->gpio.saved[0] = ucontrol->value.enumerated.item[0] & 3;
+ val = 0x80 | (ice->gpio.saved[0] << 3);
+ spi_write(ice, CS_DEV, 0x04, val);
+ change = 1;
+ }
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+
+/*
+ * GPIO controls
+ */
+static int pontis_gpio_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff; /* 16bit */
+ return 0;
+}
+
+static int pontis_gpio_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ down(&ice->gpio_mutex);
+ /* 4-7 reserved */
+ ucontrol->value.integer.value[0] = (~ice->gpio.write_mask & 0xffff) | 0x00f0;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int pontis_gpio_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int changed;
+ down(&ice->gpio_mutex);
+ /* 4-7 reserved */
+ val = (~ucontrol->value.integer.value[0] & 0xffff) | 0x00f0;
+ changed = val != ice->gpio.write_mask;
+ ice->gpio.write_mask = val;
+ up(&ice->gpio_mutex);
+ return changed;
+}
+
+static int pontis_gpio_dir_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ down(&ice->gpio_mutex);
+ /* 4-7 reserved */
+ ucontrol->value.integer.value[0] = ice->gpio.direction & 0xff0f;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int pontis_gpio_dir_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int changed;
+ down(&ice->gpio_mutex);
+ /* 4-7 reserved */
+ val = ucontrol->value.integer.value[0] & 0xff0f;
+ changed = (val != ice->gpio.direction);
+ ice->gpio.direction = val;
+ up(&ice->gpio_mutex);
+ return changed;
+}
+
+static int pontis_gpio_data_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ down(&ice->gpio_mutex);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+ ucontrol->value.integer.value[0] = snd_ice1712_gpio_read(ice) & 0xffff;
+ up(&ice->gpio_mutex);
+ return 0;
+}
+
+static int pontis_gpio_data_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val, nval;
+ int changed = 0;
+ down(&ice->gpio_mutex);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+ val = snd_ice1712_gpio_read(ice) & 0xffff;
+ nval = ucontrol->value.integer.value[0] & 0xffff;
+ if (val != nval) {
+ snd_ice1712_gpio_write(ice, nval);
+ changed = 1;
+ }
+ up(&ice->gpio_mutex);
+ return changed;
+}
+
+/*
+ * mixers
+ */
+
+static snd_kcontrol_new_t pontis_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .info = wm_dac_vol_info,
+ .get = wm_dac_vol_get,
+ .put = wm_dac_vol_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .info = wm_adc_vol_info,
+ .get = wm_adc_vol_get,
+ .put = wm_adc_vol_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Capture Switch",
+ .info = wm_adc_mux_info,
+ .get = wm_adc_mux_get,
+ .put = wm_adc_mux_put,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Capture Switch",
+ .info = wm_adc_mux_info,
+ .get = wm_adc_mux_get,
+ .put = wm_adc_mux_put,
+ .private_value = 1,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Bypass Switch",
+ .info = wm_bypass_info,
+ .get = wm_bypass_get,
+ .put = wm_bypass_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Swap Output Channels",
+ .info = wm_chswap_info,
+ .get = wm_chswap_get,
+ .put = wm_chswap_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Input Source",
+ .info = cs_source_info,
+ .get = cs_source_get,
+ .put = cs_source_put,
+ },
+ /* FIXME: which interface? */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "GPIO Mask",
+ .info = pontis_gpio_mask_info,
+ .get = pontis_gpio_mask_get,
+ .put = pontis_gpio_mask_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "GPIO Direction",
+ .info = pontis_gpio_mask_info,
+ .get = pontis_gpio_dir_get,
+ .put = pontis_gpio_dir_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "GPIO Data",
+ .info = pontis_gpio_mask_info,
+ .get = pontis_gpio_data_get,
+ .put = pontis_gpio_data_put,
+ },
+};
+
+
+/*
+ * WM codec registers
+ */
+static void wm_proc_regs_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ ice1712_t *ice = (ice1712_t *)entry->private_data;
+ char line[64];
+ unsigned int reg, val;
+ down(&ice->gpio_mutex);
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x", &reg, &val) != 2)
+ continue;
+ if (reg <= 0x17 && val <= 0xffff)
+ wm_put(ice, reg, val);
+ }
+ up(&ice->gpio_mutex);
+}
+
+static void wm_proc_regs_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ ice1712_t *ice = (ice1712_t *)entry->private_data;
+ int reg, val;
+
+ down(&ice->gpio_mutex);
+ for (reg = 0; reg <= 0x17; reg++) {
+ val = wm_get(ice, reg);
+ snd_iprintf(buffer, "%02x = %04x\n", reg, val);
+ }
+ up(&ice->gpio_mutex);
+}
+
+static void wm_proc_init(ice1712_t *ice)
+{
+ snd_info_entry_t *entry;
+ if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
+ snd_info_set_text_ops(entry, ice, 1024, wm_proc_regs_read);
+ entry->mode |= S_IWUSR;
+ entry->c.text.write_size = 1024;
+ entry->c.text.write = wm_proc_regs_write;
+ }
+}
+
+static void cs_proc_regs_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ ice1712_t *ice = (ice1712_t *)entry->private_data;
+ int reg, val;
+
+ down(&ice->gpio_mutex);
+ for (reg = 0; reg <= 0x26; reg++) {
+ val = spi_read(ice, CS_DEV, reg);
+ snd_iprintf(buffer, "%02x = %02x\n", reg, val);
+ }
+ val = spi_read(ice, CS_DEV, 0x7f);
+ snd_iprintf(buffer, "%02x = %02x\n", 0x7f, val);
+ up(&ice->gpio_mutex);
+}
+
+static void cs_proc_init(ice1712_t *ice)
+{
+ snd_info_entry_t *entry;
+ if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) {
+ snd_info_set_text_ops(entry, ice, 1024, cs_proc_regs_read);
+ }
+}
+
+
+static int __devinit pontis_add_controls(ice1712_t *ice)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(pontis_controls); i++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&pontis_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+
+ wm_proc_init(ice);
+ cs_proc_init(ice);
+
+ return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit pontis_init(ice1712_t *ice)
+{
+ static unsigned short wm_inits[] = {
+ /* These come first to reduce init pop noise */
+ WM_ADC_MUX, 0x00c0, /* ADC mute */
+ WM_DAC_MUTE, 0x0001, /* DAC softmute */
+ WM_DAC_CTRL1, 0x0000, /* DAC mute */
+
+ WM_POWERDOWN, 0x0008, /* All power-up except HP */
+ WM_RESET, 0x0000, /* reset */
+ };
+ static unsigned short wm_inits2[] = {
+ WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */
+ WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */
+ WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */
+ WM_DAC_CTRL1, 0x0090, /* DAC L/R */
+ WM_OUT_MUX, 0x0001, /* OUT DAC */
+ WM_HP_ATTEN_L, 0x0179, /* HP 0dB */
+ WM_HP_ATTEN_R, 0x0179, /* HP 0dB */
+ WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */
+ WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */
+ WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */
+ WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */
+ // WM_DAC_MASTER, 0x0100, /* DAC master muted */
+ WM_PHASE_SWAP, 0x0000, /* phase normal */
+ WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */
+ WM_ADC_ATTEN_L, 0x0000, /* ADC muted */
+ WM_ADC_ATTEN_R, 0x0000, /* ADC muted */
+#if 0
+ WM_ALC_CTRL1, 0x007b, /* */
+ WM_ALC_CTRL2, 0x0000, /* */
+ WM_ALC_CTRL3, 0x0000, /* */
+ WM_NOISE_GATE, 0x0000, /* */
+#endif
+ WM_DAC_MUTE, 0x0000, /* DAC unmute */
+ WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */
+ };
+ static unsigned char cs_inits[] = {
+ 0x04, 0x80, /* RUN, RXP0 */
+ 0x05, 0x05, /* slave, 24bit */
+ 0x01, 0x00,
+ 0x02, 0x00,
+ 0x03, 0x00,
+ };
+ unsigned int i;
+
+ ice->vt1720 = 1;
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 2;
+
+ /* to remeber the register values */
+ ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ice->akm)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+
+ /* HACK - use this as the SPDIF source.
+ * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
+ */
+ ice->gpio.saved[0] = 0;
+
+ /* initialize WM8776 codec */
+ for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
+ wm_put(ice, wm_inits[i], wm_inits[i+1]);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
+ wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
+
+ /* initialize CS8416 codec */
+ /* assert PRST#; MT05 bit 7 */
+ outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD));
+ mdelay(5);
+ /* deassert PRST# */
+ outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
+
+ for (i = 0; i < ARRAY_SIZE(cs_inits); i += 2)
+ spi_write(ice, CS_DEV, cs_inits[i], cs_inits[i+1]);
+
+ return 0;
+}
+
+
+/*
+ * Pontis boards don't provide the EEPROM data at all.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char pontis_eeprom[] __devinitdata = {
+ 0x08, /* SYSCONF: clock 256, mpu401, spdif-in/ADC, 1DAC */
+ 0x80, /* ACLINK: I2S */
+ 0xf8, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0x07, /* GPIO_DIR */
+ 0x00, /* GPIO_DIR1 */
+ 0x00, /* GPIO_DIR2 (ignored) */
+ 0x0f, /* GPIO_MASK (4-7 reserved for CS8416) */
+ 0xff, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 (ignored) */
+ 0x06, /* GPIO_STATE (0-low, 1-high, 2-high) */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 (ignored) */
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = {
+ {
+ .subvendor = VT1720_SUBDEVICE_PONTIS_MS300,
+ .name = "Pontis MS300",
+ .model = "ms300",
+ .chip_init = pontis_init,
+ .build_controls = pontis_add_controls,
+ .eeprom_size = sizeof(pontis_eeprom),
+ .eeprom_data = pontis_eeprom,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/pontis.h b/sound/pci/ice1712/pontis.h
new file mode 100644
index 00000000000..d0d1378b935
--- /dev/null
+++ b/sound/pci/ice1712/pontis.h
@@ -0,0 +1,33 @@
+#ifndef __SOUND_PONTIS_H
+#define __SOUND_PONTIS_H
+
+/*
+ * ALSA driver for VIA VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Pontis MS300 boards
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define PONTIS_DEVICE_DESC "{Pontis,MS300},"
+
+#define VT1720_SUBDEVICE_PONTIS_MS300 0x00020002 /* a dummy id for MS300 */
+
+extern struct snd_ice1712_card_info snd_vt1720_pontis_cards[];
+
+#endif /* __SOUND_PONTIS_H */
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
new file mode 100644
index 00000000000..d2c5963795d
--- /dev/null
+++ b/sound/pci/ice1712/prodigy192.c
@@ -0,0 +1,524 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for AudioTrak Prodigy 192 cards
+ *
+ * Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
+ * Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
+ * Copyright (c) 2004 Kouichi ONO <co2b@ceres.dti.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "prodigy192.h"
+#include "stac946x.h"
+
+static inline void stac9460_put(ice1712_t *ice, int reg, unsigned char val)
+{
+ snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val);
+}
+
+static inline unsigned char stac9460_get(ice1712_t *ice, int reg)
+{
+ return snd_vt1724_read_i2c(ice, PRODIGY192_STAC9460_ADDR, reg);
+}
+
+/*
+ * DAC mute control
+ */
+static int stac9460_dac_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int stac9460_dac_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int idx;
+
+ if (kcontrol->private_value)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+ val = stac9460_get(ice, idx);
+ ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
+ return 0;
+}
+
+static int stac9460_dac_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char new, old;
+ int idx;
+ int change;
+
+ if (kcontrol->private_value)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+ old = stac9460_get(ice, idx);
+ new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change)
+ stac9460_put(ice, idx, new);
+
+ return change;
+}
+
+/*
+ * DAC volume attenuation mixer control
+ */
+static int stac9460_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0; /* mute */
+ uinfo->value.integer.max = 0x7f; /* 0dB */
+ return 0;
+}
+
+static int stac9460_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx;
+ unsigned char vol;
+
+ if (kcontrol->private_value)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+ vol = stac9460_get(ice, idx) & 0x7f;
+ ucontrol->value.integer.value[0] = 0x7f - vol;
+
+ return 0;
+}
+
+static int stac9460_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int idx;
+ unsigned char tmp, ovol, nvol;
+ int change;
+
+ if (kcontrol->private_value)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+ nvol = ucontrol->value.integer.value[0];
+ tmp = stac9460_get(ice, idx);
+ ovol = 0x7f - (tmp & 0x7f);
+ change = (ovol != nvol);
+ if (change) {
+ stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+ }
+ return change;
+}
+
+/*
+ * ADC mute control
+ */
+static int stac9460_adc_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int stac9460_adc_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ val = stac9460_get(ice, STAC946X_MIC_L_VOLUME + i);
+ ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
+ }
+
+ return 0;
+}
+
+static int stac9460_adc_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char new, old;
+ int i, reg;
+ int change;
+
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ old = stac9460_get(ice, reg);
+ new = (~ucontrol->value.integer.value[i]<<7&0x80) | (old&~0x80);
+ change = (new != old);
+ if (change)
+ stac9460_put(ice, reg, new);
+ }
+
+ return change;
+}
+
+/*
+ * ADC gain mixer control
+ */
+static int stac9460_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* 0dB */
+ uinfo->value.integer.max = 0x0f; /* 22.5dB */
+ return 0;
+}
+
+static int stac9460_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int i, reg;
+ unsigned char vol;
+
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ vol = stac9460_get(ice, reg) & 0x0f;
+ ucontrol->value.integer.value[i] = 0x0f - vol;
+ }
+
+ return 0;
+}
+
+static int stac9460_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int i, reg;
+ unsigned char ovol, nvol;
+ int change;
+
+ for (i = 0; i < 2; ++i) {
+ reg = STAC946X_MIC_L_VOLUME + i;
+ nvol = ucontrol->value.integer.value[i];
+ ovol = 0x0f - stac9460_get(ice, reg);
+ change = ((ovol & 0x0f) != nvol);
+ if (change)
+ stac9460_put(ice, reg, (0x0f - nvol) | (ovol & ~0x0f));
+ }
+
+ return change;
+}
+
+#if 0
+/*
+ * Headphone Amplifier
+ */
+static int aureon_set_headphone_amp(ice1712_t *ice, int enable)
+{
+ unsigned int tmp, tmp2;
+
+ tmp2 = tmp = snd_ice1712_gpio_read(ice);
+ if (enable)
+ tmp |= AUREON_HP_SEL;
+ else
+ tmp &= ~ AUREON_HP_SEL;
+ if (tmp != tmp2) {
+ snd_ice1712_gpio_write(ice, tmp);
+ return 1;
+ }
+ return 0;
+}
+
+static int aureon_get_headphone_amp(ice1712_t *ice)
+{
+ unsigned int tmp = snd_ice1712_gpio_read(ice);
+
+ return ( tmp & AUREON_HP_SEL )!= 0;
+}
+
+static int aureon_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int aureon_hpamp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
+ return 0;
+}
+
+
+static int aureon_hpamp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
+}
+
+/*
+ * Deemphasis
+ */
+static int aureon_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
+ return 0;
+}
+
+static int aureon_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ int temp, temp2;
+ temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
+ if (ucontrol->value.integer.value[0])
+ temp |= 0xf;
+ else
+ temp &= ~0xf;
+ if (temp != temp2) {
+ wm_put(ice, WM_DAC_CTRL2, temp);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * ADC Oversampling
+ */
+static int aureon_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[2] = { "128x", "64x" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int aureon_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
+ return 0;
+}
+
+static int aureon_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ int temp, temp2;
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ temp2 = temp = wm_get(ice, WM_MASTER);
+
+ if (ucontrol->value.enumerated.item[0])
+ temp |= 0x8;
+ else
+ temp &= ~0x8;
+
+ if (temp != temp2) {
+ wm_put(ice, WM_MASTER, temp);
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * mixers
+ */
+
+static snd_kcontrol_new_t stac_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = stac9460_dac_mute_info,
+ .get = stac9460_dac_mute_get,
+ .put = stac9460_dac_mute_put,
+ .private_value = 1,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .info = stac9460_dac_vol_info,
+ .get = stac9460_dac_vol_get,
+ .put = stac9460_dac_vol_put,
+ .private_value = 1,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Switch",
+ .count = 6,
+ .info = stac9460_dac_mute_info,
+ .get = stac9460_dac_mute_get,
+ .put = stac9460_dac_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Volume",
+ .count = 6,
+ .info = stac9460_dac_vol_info,
+ .get = stac9460_dac_vol_get,
+ .put = stac9460_dac_vol_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Switch",
+ .count = 1,
+ .info = stac9460_adc_mute_info,
+ .get = stac9460_adc_mute_get,
+ .put = stac9460_adc_mute_put,
+
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Volume",
+ .count = 1,
+ .info = stac9460_adc_vol_info,
+ .get = stac9460_adc_vol_get,
+ .put = stac9460_adc_vol_put,
+ },
+#if 0
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Route",
+ .info = wm_adc_mux_info,
+ .get = wm_adc_mux_get,
+ .put = wm_adc_mux_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Amplifier Switch",
+ .info = aureon_bool_info,
+ .get = aureon_hpamp_get,
+ .put = aureon_hpamp_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Deemphasis Switch",
+ .info = aureon_bool_info,
+ .get = aureon_deemp_get,
+ .put = aureon_deemp_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Oversampling",
+ .info = aureon_oversampling_info,
+ .get = aureon_oversampling_get,
+ .put = aureon_oversampling_put
+ },
+#endif
+};
+
+static int __devinit prodigy192_add_controls(ice1712_t *ice)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(stac_controls); i++) {
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&stac_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit prodigy192_init(ice1712_t *ice)
+{
+ static unsigned short stac_inits_prodigy[] = {
+ STAC946X_RESET, 0,
+/* STAC946X_MASTER_VOLUME, 0,
+ STAC946X_LF_VOLUME, 0,
+ STAC946X_RF_VOLUME, 0,
+ STAC946X_LR_VOLUME, 0,
+ STAC946X_RR_VOLUME, 0,
+ STAC946X_CENTER_VOLUME, 0,
+ STAC946X_LFE_VOLUME, 0,*/
+ (unsigned short)-1
+ };
+ unsigned short *p;
+
+ /* prodigy 192 */
+ ice->num_total_dacs = 6;
+ ice->num_total_adcs = 2;
+
+ /* initialize codec */
+ p = stac_inits_prodigy;
+ for (; *p != (unsigned short)-1; p += 2)
+ stac9460_put(ice, p[0], p[1]);
+
+ return 0;
+}
+
+
+/*
+ * Aureon boards don't provide the EEPROM data except for the vendor IDs.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char prodigy71_eeprom[] __devinitdata = {
+ 0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
+ 0x80, /* ACLINK: I2S */
+ 0xf8, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0xbf, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_PRODIGY192VE,
+ .name = "Audiotrak Prodigy 192",
+ .model = "prodigy192",
+ .chip_init = prodigy192_init,
+ .build_controls = prodigy192_add_controls,
+ .eeprom_size = sizeof(prodigy71_eeprom),
+ .eeprom_data = prodigy71_eeprom,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/prodigy192.h b/sound/pci/ice1712/prodigy192.h
new file mode 100644
index 00000000000..94c824e24e0
--- /dev/null
+++ b/sound/pci/ice1712/prodigy192.h
@@ -0,0 +1,11 @@
+#ifndef __SOUND_PRODIGY192_H
+#define __SOUND_PRODIGY192_H
+
+#define PRODIGY192_DEVICE_DESC "{AudioTrak,Prodigy 192},"
+#define PRODIGY192_STAC9460_ADDR 0x54
+
+#define VT1724_SUBDEVICE_PRODIGY192VE 0x34495345 /* PRODIGY 192 VE */
+
+extern struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[];
+
+#endif /* __SOUND_PRODIGY192_H */
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
new file mode 100644
index 00000000000..d48d42524ac
--- /dev/null
+++ b/sound/pci/ice1712/revo.c
@@ -0,0 +1,205 @@
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for M-Audio Revolution 7.1
+ *
+ * Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "revo.h"
+
+static void revo_i2s_mclk_changed(ice1712_t *ice)
+{
+ /* assert PRST# to converters; MT05 bit 7 */
+ outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD));
+ mdelay(5);
+ /* deassert PRST# */
+ outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
+}
+
+/*
+ * change the rate of envy24HT, AK4355 and AK4381
+ */
+static void revo_set_rate_val(akm4xxx_t *ak, unsigned int rate)
+{
+ unsigned char old, tmp, dfs;
+ int reg, shift;
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+
+ /* adjust DFS on codecs */
+ if (rate > 96000)
+ dfs = 2;
+ else if (rate > 48000)
+ dfs = 1;
+ else
+ dfs = 0;
+
+ if (ak->type == SND_AK4355) {
+ reg = 2;
+ shift = 4;
+ } else {
+ reg = 1;
+ shift = 3;
+ }
+ tmp = snd_akm4xxx_get(ak, 0, reg);
+ old = (tmp >> shift) & 0x03;
+ if (old == dfs)
+ return;
+
+ /* reset DFS */
+ snd_akm4xxx_reset(ak, 1);
+ tmp = snd_akm4xxx_get(ak, 0, reg);
+ tmp &= ~(0x03 << shift);
+ tmp |= dfs << shift;
+ // snd_akm4xxx_write(ak, 0, reg, tmp);
+ snd_akm4xxx_set(ak, 0, reg, tmp); /* the value is written in reset(0) */
+ snd_akm4xxx_reset(ak, 0);
+}
+
+/*
+ * initialize the chips on M-Audio Revolution cards
+ */
+
+static akm4xxx_t akm_revo_front __devinitdata = {
+ .type = SND_AK4381,
+ .num_dacs = 2,
+ .ops = {
+ .set_rate_val = revo_set_rate_val
+ }
+};
+
+static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
+ .caddr = 1,
+ .cif = 0,
+ .data_mask = VT1724_REVO_CDOUT,
+ .clk_mask = VT1724_REVO_CCLK,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .add_flags = VT1724_REVO_CCLK, /* high at init */
+ .mask_flags = 0,
+};
+
+static akm4xxx_t akm_revo_surround __devinitdata = {
+ .type = SND_AK4355,
+ .idx_offset = 1,
+ .num_dacs = 6,
+ .ops = {
+ .set_rate_val = revo_set_rate_val
+ }
+};
+
+static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
+ .caddr = 3,
+ .cif = 0,
+ .data_mask = VT1724_REVO_CDOUT,
+ .clk_mask = VT1724_REVO_CCLK,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS1,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .add_flags = VT1724_REVO_CCLK, /* high at init */
+ .mask_flags = 0,
+};
+
+static unsigned int rates[] = {
+ 32000, 44100, 48000, 64000, 88200, 96000,
+ 176400, 192000,
+};
+
+static snd_pcm_hw_constraint_list_t revo_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static int __devinit revo_init(ice1712_t *ice)
+{
+ akm4xxx_t *ak;
+ int err;
+
+ /* determine I2C, DACs and ADCs */
+ switch (ice->eeprom.subvendor) {
+ case VT1724_SUBDEVICE_REVOLUTION71:
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 2;
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+
+ ice->gpio.i2s_mclk_changed = revo_i2s_mclk_changed;
+
+ /* second stage of initialization, analog parts and others */
+ ak = ice->akm = kcalloc(2, sizeof(akm4xxx_t), GFP_KERNEL);
+ if (! ak)
+ return -ENOMEM;
+ ice->akm_codecs = 2;
+ switch (ice->eeprom.subvendor) {
+ case VT1724_SUBDEVICE_REVOLUTION71:
+ if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front, &akm_revo_front_priv, ice)) < 0)
+ return err;
+ if ((err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo_surround, &akm_revo_surround_priv, ice)) < 0)
+ return err;
+ /* unmute all codecs */
+ snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
+ break;
+ }
+
+ ice->hw_rates = &revo_rates; /* AK codecs don't support lower than 32k */
+
+ return 0;
+}
+
+
+static int __devinit revo_add_controls(ice1712_t *ice)
+{
+ int err;
+
+ switch (ice->eeprom.subvendor) {
+ case VT1724_SUBDEVICE_REVOLUTION71:
+ err = snd_ice1712_akm4xxx_build_controls(ice);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_REVOLUTION71,
+ .name = "M Audio Revolution-7.1",
+ .model = "revo71",
+ .chip_init = revo_init,
+ .build_controls = revo_add_controls,
+ },
+ { } /* terminator */
+};
diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h
new file mode 100644
index 00000000000..ca4420b5e3e
--- /dev/null
+++ b/sound/pci/ice1712/revo.h
@@ -0,0 +1,48 @@
+#ifndef __SOUND_REVO_H
+#define __SOUND_REVO_H
+
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Lowlevel functions for M-Audio Revolution 7.1
+ *
+ * Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define REVO_DEVICE_DESC \
+ "{MidiMan M Audio,Revolution 7.1},"
+
+#define VT1724_SUBDEVICE_REVOLUTION71 0x12143036
+
+/* entry point */
+extern struct snd_ice1712_card_info snd_vt1724_revo_cards[];
+
+
+/*
+ * MidiMan M-Audio Revolution GPIO definitions
+ */
+
+#define VT1724_REVO_CCLK 0x02
+#define VT1724_REVO_CDIN 0x04 /* not used */
+#define VT1724_REVO_CDOUT 0x08
+#define VT1724_REVO_CS0 0x10 /* not used */
+#define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */
+#define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */
+#define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */
+
+#endif /* __SOUND_REVO_H */
diff --git a/sound/pci/ice1712/stac946x.h b/sound/pci/ice1712/stac946x.h
new file mode 100644
index 00000000000..5b390952d0e
--- /dev/null
+++ b/sound/pci/ice1712/stac946x.h
@@ -0,0 +1,25 @@
+#ifndef __SOUND_STAC946X_H
+#define __SOUND_STAC946X_H
+
+#define STAC946X_RESET 0x00
+#define STAC946X_STATUS 0x01
+#define STAC946X_MASTER_VOLUME 0x02
+#define STAC946X_LF_VOLUME 0x03
+#define STAC946X_RF_VOLUME 0x04
+#define STAC946X_LR_VOLUME 0x05
+#define STAC946X_RR_VOLUME 0x06
+#define STAC946X_CENTER_VOLUME 0x07
+#define STAC946X_LFE_VOLUME 0x08
+#define STAC946X_MIC_L_VOLUME 0x09
+#define STAC946X_MIC_R_VOLUME 0x0a
+#define STAC946X_DEEMPHASIS 0x0c
+#define STAC946X_GENERAL_PURPOSE 0x0d
+#define STAC946X_AUDIO_PORT_CONTROL 0x0e
+#define STAC946X_MASTER_CLOCKING 0x0f
+#define STAC946X_POWERDOWN_CTRL1 0x10
+#define STAC946X_POWERDOWN_CTRL2 0x11
+#define STAC946X_REVISION_CODE 0x12
+#define STAC946X_ADDRESS_CONTROL 0x13
+#define STAC946X_ADDRESS 0x14
+
+#endif /* __SOUND_STAC946X_H */
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
new file mode 100644
index 00000000000..3bd92627231
--- /dev/null
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -0,0 +1,115 @@
+/*
+ * ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT)
+ *
+ * Lowlevel functions for VT1720-based motherboards
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "vt1720_mobo.h"
+
+
+static int __devinit k8x800_init(ice1712_t *ice)
+{
+ ice->vt1720 = 1;
+
+ /* VT1616 codec */
+ ice->num_total_dacs = 6;
+ ice->num_total_adcs = 2;
+
+ /* WM8728 codec */
+ /* FIXME: TODO */
+
+ return 0;
+}
+
+static int __devinit k8x800_add_controls(ice1712_t *ice)
+{
+ /* FIXME: needs some quirks for VT1616? */
+ return 0;
+}
+
+/* EEPROM image */
+
+static unsigned char k8x800_eeprom[] __devinitdata = {
+ 0x01, /* SYSCONF: clock 256, 1ADC, 2DACs */
+ 0x02, /* ACLINK: ACLINK, packed */
+ 0x00, /* I2S: - */
+ 0x00, /* SPDIF: - */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x00, /* - */
+ 0xff, /* GPIO_MASK */
+ 0xff, /* GPIO_MASK1 */
+ 0x00, /* - */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* - */
+};
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = {
+ {
+ .subvendor = VT1720_SUBDEVICE_K8X800,
+ .name = "Albatron K8X800 Pro II",
+ .model = "k8x800",
+ .chip_init = k8x800_init,
+ .build_controls = k8x800_add_controls,
+ .eeprom_size = sizeof(k8x800_eeprom),
+ .eeprom_data = k8x800_eeprom,
+ },
+ {
+ .subvendor = VT1720_SUBDEVICE_ZNF3_150,
+ .name = "Chaintech ZNF3-150",
+ /* identical with k8x800 */
+ .chip_init = k8x800_init,
+ .build_controls = k8x800_add_controls,
+ .eeprom_size = sizeof(k8x800_eeprom),
+ .eeprom_data = k8x800_eeprom,
+ },
+ {
+ .subvendor = VT1720_SUBDEVICE_ZNF3_250,
+ .name = "Chaintech ZNF3-250",
+ /* identical with k8x800 */
+ .chip_init = k8x800_init,
+ .build_controls = k8x800_add_controls,
+ .eeprom_size = sizeof(k8x800_eeprom),
+ .eeprom_data = k8x800_eeprom,
+ },
+ {
+ .subvendor = VT1720_SUBDEVICE_9CJS,
+ .name = "Chaintech 9CJS",
+ /* identical with k8x800 */
+ .chip_init = k8x800_init,
+ .build_controls = k8x800_add_controls,
+ .eeprom_size = sizeof(k8x800_eeprom),
+ .eeprom_data = k8x800_eeprom,
+ },
+ { } /* terminator */
+};
+
diff --git a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h
new file mode 100644
index 00000000000..f949eb804ca
--- /dev/null
+++ b/sound/pci/ice1712/vt1720_mobo.h
@@ -0,0 +1,39 @@
+#ifndef __SOUND_VT1720_MOBO_H
+#define __SOUND_VT1720_MOBO_H
+
+/*
+ * ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT)
+ *
+ * Lowlevel functions for VT1720-based motherboards
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define VT1720_MOBO_DEVICE_DESC "{Albatron,K8X800 Pro II},"\
+ "{Chaintech,ZNF3-150},"\
+ "{Chaintech,ZNF3-250},"\
+ "{Chaintech,9CJS},"
+
+#define VT1720_SUBDEVICE_K8X800 0xf217052c
+#define VT1720_SUBDEVICE_ZNF3_150 0x0f2741f6
+#define VT1720_SUBDEVICE_ZNF3_250 0x0f2745f6
+#define VT1720_SUBDEVICE_9CJS 0x0f272327
+
+extern struct snd_ice1712_card_info snd_vt1720_mobo_cards[];
+
+#endif /* __SOUND_VT1720_MOBO_H */
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
new file mode 100644
index 00000000000..0eb940da9d1
--- /dev/null
+++ b/sound/pci/intel8x0.c
@@ -0,0 +1,2855 @@
+/*
+ * ALSA driver for Intel ICH (i8x0) chipsets
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This code also contains alpha support for SiS 735 chipsets provided
+ * by Mike Pieper <mptei@users.sourceforge.net>. We have no datasheet
+ * for SiS735, so the code is not fully functional.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+/* for 440MX workaround */
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"
+ "{Intel,82901AB-ICH0},"
+ "{Intel,82801BA-ICH2},"
+ "{Intel,82801CA-ICH3},"
+ "{Intel,82801DB-ICH4},"
+ "{Intel,ICH5},"
+ "{Intel,ICH6},"
+ "{Intel,ICH7},"
+ "{Intel,6300ESB},"
+ "{Intel,MX440},"
+ "{SiS,SI7012},"
+ "{NVidia,nForce Audio},"
+ "{NVidia,nForce2 Audio},"
+ "{AMD,AMD768},"
+ "{AMD,AMD8111},"
+ "{ALI,M5455}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static char *ac97_quirk[SNDRV_CARDS];
+static int buggy_irq[SNDRV_CARDS];
+static int xbox[SNDRV_CARDS];
+
+#ifdef SUPPORT_MIDI
+static int mpu_port[SNDRV_CARDS]; /* disabled */
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel i8x0 soundcard.");
+module_param_array(ac97_clock, int, NULL, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");
+module_param_array(ac97_quirk, charp, NULL, 0444);
+MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
+module_param_array(buggy_irq, bool, NULL, 0444);
+MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards.");
+module_param_array(xbox, bool, NULL, 0444);
+MODULE_PARM_DESC(xbox, "Set to 1 for Xbox, if you have problems with the AC'97 codec detection.");
+
+/*
+ * Direct registers
+ */
+
+#ifndef PCI_DEVICE_ID_INTEL_82801
+#define PCI_DEVICE_ID_INTEL_82801 0x2415
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82901
+#define PCI_DEVICE_ID_INTEL_82901 0x2425
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BA
+#define PCI_DEVICE_ID_INTEL_82801BA 0x2445
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_440MX
+#define PCI_DEVICE_ID_INTEL_440MX 0x7195
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH3
+#define PCI_DEVICE_ID_INTEL_ICH3 0x2485
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH4
+#define PCI_DEVICE_ID_INTEL_ICH4 0x24c5
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH5
+#define PCI_DEVICE_ID_INTEL_ICH5 0x24d5
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ESB_5
+#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH6_18
+#define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH7_20
+#define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de
+#endif
+#ifndef PCI_DEVICE_ID_SI_7012
+#define PCI_DEVICE_ID_SI_7012 0x7012
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_CK804_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_CK8_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea
+#endif
+
+enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };
+
+#define ICHREG(x) ICH_REG_##x
+
+#define DEFINE_REGSET(name,base) \
+enum { \
+ ICH_REG_##name##_BDBAR = base + 0x0, /* dword - buffer descriptor list base address */ \
+ ICH_REG_##name##_CIV = base + 0x04, /* byte - current index value */ \
+ ICH_REG_##name##_LVI = base + 0x05, /* byte - last valid index */ \
+ ICH_REG_##name##_SR = base + 0x06, /* byte - status register */ \
+ ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
+ ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
+ ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
+};
+
+/* busmaster blocks */
+DEFINE_REGSET(OFF, 0); /* offset */
+DEFINE_REGSET(PI, 0x00); /* PCM in */
+DEFINE_REGSET(PO, 0x10); /* PCM out */
+DEFINE_REGSET(MC, 0x20); /* Mic in */
+
+/* ICH4 busmaster blocks */
+DEFINE_REGSET(MC2, 0x40); /* Mic in 2 */
+DEFINE_REGSET(PI2, 0x50); /* PCM in 2 */
+DEFINE_REGSET(SP, 0x60); /* SPDIF out */
+
+/* values for each busmaster block */
+
+/* LVI */
+#define ICH_REG_LVI_MASK 0x1f
+
+/* SR */
+#define ICH_FIFOE 0x10 /* FIFO error */
+#define ICH_BCIS 0x08 /* buffer completion interrupt status */
+#define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */
+#define ICH_CELV 0x02 /* current equals last valid */
+#define ICH_DCH 0x01 /* DMA controller halted */
+
+/* PIV */
+#define ICH_REG_PIV_MASK 0x1f /* mask */
+
+/* CR */
+#define ICH_IOCE 0x10 /* interrupt on completion enable */
+#define ICH_FEIE 0x08 /* fifo error interrupt enable */
+#define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */
+#define ICH_RESETREGS 0x02 /* reset busmaster registers */
+#define ICH_STARTBM 0x01 /* start busmaster operation */
+
+
+/* global block */
+#define ICH_REG_GLOB_CNT 0x2c /* dword - global control */
+#define ICH_PCM_SPDIF_MASK 0xc0000000 /* s/pdif pcm slot mask (ICH4) */
+#define ICH_PCM_SPDIF_NONE 0x00000000 /* reserved - undefined */
+#define ICH_PCM_SPDIF_78 0x40000000 /* s/pdif pcm on slots 7&8 */
+#define ICH_PCM_SPDIF_69 0x80000000 /* s/pdif pcm on slots 6&9 */
+#define ICH_PCM_SPDIF_1011 0xc0000000 /* s/pdif pcm on slots 10&11 */
+#define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */
+#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */
+#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */
+#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */
+#define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */
+#define ICH_SIS_PCM_246_MASK 0x000000c0 /* 6 channels (SIS7012) */
+#define ICH_SIS_PCM_6 0x00000080 /* 6 channels (SIS7012) */
+#define ICH_SIS_PCM_4 0x00000040 /* 4 channels (SIS7012) */
+#define ICH_SIS_PCM_2 0x00000000 /* 2 channels (SIS7012) */
+#define ICH_TRIE 0x00000040 /* tertiary resume interrupt enable */
+#define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */
+#define ICH_PRIE 0x00000010 /* primary resume interrupt enable */
+#define ICH_ACLINK 0x00000008 /* AClink shut off */
+#define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */
+#define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */
+#define ICH_GIE 0x00000001 /* GPI interrupt enable */
+#define ICH_REG_GLOB_STA 0x30 /* dword - global status */
+#define ICH_TRI 0x20000000 /* ICH4: tertiary (AC_SDIN2) resume interrupt */
+#define ICH_TCR 0x10000000 /* ICH4: tertiary (AC_SDIN2) codec ready */
+#define ICH_BCS 0x08000000 /* ICH4: bit clock stopped */
+#define ICH_SPINT 0x04000000 /* ICH4: S/PDIF interrupt */
+#define ICH_P2INT 0x02000000 /* ICH4: PCM2-In interrupt */
+#define ICH_M2INT 0x01000000 /* ICH4: Mic2-In interrupt */
+#define ICH_SAMPLE_CAP 0x00c00000 /* ICH4: sample capability bits (RO) */
+#define ICH_SAMPLE_16_20 0x00400000 /* ICH4: 16- and 20-bit samples */
+#define ICH_MULTICHAN_CAP 0x00300000 /* ICH4: multi-channel capability bits (RO) */
+#define ICH_MD3 0x00020000 /* modem power down semaphore */
+#define ICH_AD3 0x00010000 /* audio power down semaphore */
+#define ICH_RCS 0x00008000 /* read completion status */
+#define ICH_BIT3 0x00004000 /* bit 3 slot 12 */
+#define ICH_BIT2 0x00002000 /* bit 2 slot 12 */
+#define ICH_BIT1 0x00001000 /* bit 1 slot 12 */
+#define ICH_SRI 0x00000800 /* secondary (AC_SDIN1) resume interrupt */
+#define ICH_PRI 0x00000400 /* primary (AC_SDIN0) resume interrupt */
+#define ICH_SCR 0x00000200 /* secondary (AC_SDIN1) codec ready */
+#define ICH_PCR 0x00000100 /* primary (AC_SDIN0) codec ready */
+#define ICH_MCINT 0x00000080 /* MIC capture interrupt */
+#define ICH_POINT 0x00000040 /* playback interrupt */
+#define ICH_PIINT 0x00000020 /* capture interrupt */
+#define ICH_NVSPINT 0x00000010 /* nforce spdif interrupt */
+#define ICH_MOINT 0x00000004 /* modem playback interrupt */
+#define ICH_MIINT 0x00000002 /* modem capture interrupt */
+#define ICH_GSCI 0x00000001 /* GPI status change interrupt */
+#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */
+#define ICH_CAS 0x01 /* codec access semaphore */
+#define ICH_REG_SDM 0x80
+#define ICH_DI2L_MASK 0x000000c0 /* PCM In 2, Mic In 2 data in line */
+#define ICH_DI2L_SHIFT 6
+#define ICH_DI1L_MASK 0x00000030 /* PCM In 1, Mic In 1 data in line */
+#define ICH_DI1L_SHIFT 4
+#define ICH_SE 0x00000008 /* steer enable */
+#define ICH_LDI_MASK 0x00000003 /* last codec read data input */
+
+#define ICH_MAX_FRAGS 32 /* max hw frags */
+
+
+/*
+ * registers for Ali5455
+ */
+
+/* ALi 5455 busmaster blocks */
+DEFINE_REGSET(AL_PI, 0x40); /* ALi PCM in */
+DEFINE_REGSET(AL_PO, 0x50); /* Ali PCM out */
+DEFINE_REGSET(AL_MC, 0x60); /* Ali Mic in */
+DEFINE_REGSET(AL_CDC_SPO, 0x70); /* Ali Codec SPDIF out */
+DEFINE_REGSET(AL_CENTER, 0x80); /* Ali center out */
+DEFINE_REGSET(AL_LFE, 0x90); /* Ali center out */
+DEFINE_REGSET(AL_CLR_SPI, 0xa0); /* Ali Controller SPDIF in */
+DEFINE_REGSET(AL_CLR_SPO, 0xb0); /* Ali Controller SPDIF out */
+DEFINE_REGSET(AL_I2S, 0xc0); /* Ali I2S in */
+DEFINE_REGSET(AL_PI2, 0xd0); /* Ali PCM2 in */
+DEFINE_REGSET(AL_MC2, 0xe0); /* Ali Mic2 in */
+
+enum {
+ ICH_REG_ALI_SCR = 0x00, /* System Control Register */
+ ICH_REG_ALI_SSR = 0x04, /* System Status Register */
+ ICH_REG_ALI_DMACR = 0x08, /* DMA Control Register */
+ ICH_REG_ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */
+ ICH_REG_ALI_INTERFACECR = 0x10, /* Interface Control Register */
+ ICH_REG_ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */
+ ICH_REG_ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */
+ ICH_REG_ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */
+ ICH_REG_ALI_CPR = 0x20, /* Command Port Register */
+ ICH_REG_ALI_CPR_ADDR = 0x22, /* ac97 addr write */
+ ICH_REG_ALI_SPR = 0x24, /* Status Port Register */
+ ICH_REG_ALI_SPR_ADDR = 0x26, /* ac97 addr read */
+ ICH_REG_ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */
+ ICH_REG_ALI_TTSR = 0x30, /* Transmit Tag Slot Register */
+ ICH_REG_ALI_RTSR = 0x34, /* Receive Tag Slot Register */
+ ICH_REG_ALI_CSPSR = 0x38, /* Command/Status Port Status Register */
+ ICH_REG_ALI_CAS = 0x3c, /* Codec Write Semaphore Register */
+ ICH_REG_ALI_HWVOL = 0xf0, /* hardware volume control/status */
+ ICH_REG_ALI_I2SCR = 0xf4, /* I2S control/status */
+ ICH_REG_ALI_SPDIFCSR = 0xf8, /* spdif channel status register */
+ ICH_REG_ALI_SPDIFICS = 0xfc, /* spdif interface control/status */
+};
+
+#define ALI_CAS_SEM_BUSY 0x80000000
+#define ALI_CPR_ADDR_SECONDARY 0x100
+#define ALI_CPR_ADDR_READ 0x80
+#define ALI_CSPSR_CODEC_READY 0x08
+#define ALI_CSPSR_READ_OK 0x02
+#define ALI_CSPSR_WRITE_OK 0x01
+
+/* interrupts for the whole chip by interrupt status register finish */
+
+#define ALI_INT_MICIN2 (1<<26)
+#define ALI_INT_PCMIN2 (1<<25)
+#define ALI_INT_I2SIN (1<<24)
+#define ALI_INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */
+#define ALI_INT_SPDIFIN (1<<22)
+#define ALI_INT_LFEOUT (1<<21)
+#define ALI_INT_CENTEROUT (1<<20)
+#define ALI_INT_CODECSPDIFOUT (1<<19)
+#define ALI_INT_MICIN (1<<18)
+#define ALI_INT_PCMOUT (1<<17)
+#define ALI_INT_PCMIN (1<<16)
+#define ALI_INT_CPRAIS (1<<7) /* command port available */
+#define ALI_INT_SPRAIS (1<<5) /* status port available */
+#define ALI_INT_GPIO (1<<1)
+#define ALI_INT_MASK (ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN)
+
+#define ICH_ALI_SC_RESET (1<<31) /* master reset */
+#define ICH_ALI_SC_AC97_DBL (1<<30)
+#define ICH_ALI_SC_CODEC_SPDF (3<<20) /* 1=7/8, 2=6/9, 3=10/11 */
+#define ICH_ALI_SC_IN_BITS (3<<18)
+#define ICH_ALI_SC_OUT_BITS (3<<16)
+#define ICH_ALI_SC_6CH_CFG (3<<14)
+#define ICH_ALI_SC_PCM_4 (1<<8)
+#define ICH_ALI_SC_PCM_6 (2<<8)
+#define ICH_ALI_SC_PCM_246_MASK (3<<8)
+
+#define ICH_ALI_SS_SEC_ID (3<<5)
+#define ICH_ALI_SS_PRI_ID (3<<3)
+
+#define ICH_ALI_IF_AC97SP (1<<21)
+#define ICH_ALI_IF_MC (1<<20)
+#define ICH_ALI_IF_PI (1<<19)
+#define ICH_ALI_IF_MC2 (1<<18)
+#define ICH_ALI_IF_PI2 (1<<17)
+#define ICH_ALI_IF_LINE_SRC (1<<15) /* 0/1 = slot 3/6 */
+#define ICH_ALI_IF_MIC_SRC (1<<14) /* 0/1 = slot 3/6 */
+#define ICH_ALI_IF_SPDF_SRC (3<<12) /* 00 = PCM, 01 = AC97-in, 10 = spdif-in, 11 = i2s */
+#define ICH_ALI_IF_AC97_OUT (3<<8) /* 00 = PCM, 10 = spdif-in, 11 = i2s */
+#define ICH_ALI_IF_PO_SPDF (1<<3)
+#define ICH_ALI_IF_PO (1<<1)
+
+/*
+ *
+ */
+
+enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR };
+enum { NVD_PCMIN, NVD_PCMOUT, NVD_MIC, NVD_SPBAR, NVD_LAST = NVD_SPBAR };
+enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT };
+
+#define get_ichdev(substream) (ichdev_t *)(substream->runtime->private_data)
+
+typedef struct {
+ unsigned int ichd; /* ich device number */
+ unsigned long reg_offset; /* offset to bmaddr */
+ u32 *bdbar; /* CPU address (32bit) */
+ unsigned int bdbar_addr; /* PCI bus address (32bit) */
+ snd_pcm_substream_t *substream;
+ unsigned int physbuf; /* physical address (32bit) */
+ unsigned int size;
+ unsigned int fragsize;
+ unsigned int fragsize1;
+ unsigned int position;
+ unsigned int pos_shift;
+ int frags;
+ int lvi;
+ int lvi_frag;
+ int civ;
+ int ack;
+ int ack_reload;
+ unsigned int ack_bit;
+ unsigned int roff_sr;
+ unsigned int roff_picb;
+ unsigned int int_sta_mask; /* interrupt status mask */
+ unsigned int ali_slot; /* ALI DMA slot */
+ struct ac97_pcm *pcm;
+ int pcm_open_flag;
+ unsigned int page_attr_changed: 1;
+} ichdev_t;
+
+typedef struct _snd_intel8x0 intel8x0_t;
+
+struct _snd_intel8x0 {
+ unsigned int device_type;
+
+ int irq;
+
+ unsigned int mmio;
+ unsigned long addr;
+ void __iomem *remap_addr;
+ unsigned int bm_mmio;
+ unsigned long bmaddr;
+ void __iomem *remap_bmaddr;
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+
+ int pcm_devs;
+ snd_pcm_t *pcm[6];
+ ichdev_t ichd[6];
+
+ unsigned multi4: 1,
+ multi6: 1,
+ dra: 1,
+ smp20bit: 1;
+ unsigned in_ac97_init: 1,
+ in_sdin_init: 1;
+ unsigned in_measurement: 1; /* during ac97 clock measurement */
+ unsigned fix_nocache: 1; /* workaround for 440MX */
+ unsigned buggy_irq: 1; /* workaround for buggy mobos */
+ unsigned xbox: 1; /* workaround for Xbox AC'97 detection */
+
+ int spdif_idx; /* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97[3];
+ unsigned int ac97_sdin[3];
+
+ spinlock_t reg_lock;
+
+ u32 bdbars_count;
+ struct snd_dma_buffer bdbars;
+ u32 int_sta_reg; /* interrupt status register */
+ u32 int_sta_mask; /* interrupt status mask */
+};
+
+static struct pci_device_id snd_intel8x0_ids[] = {
+ { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */
+ { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */
+ { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */
+ { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */
+ { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH4 */
+ { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH5 */
+ { 0x8086, 0x25a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ESB */
+ { 0x8086, 0x266e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH6 */
+ { 0x8086, 0x27de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH7 */
+ { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */
+ { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */
+ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */
+ { 0x10de, 0x003a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* MCP04 */
+ { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */
+ { 0x10de, 0x0059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK804 */
+ { 0x10de, 0x008a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8 */
+ { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */
+ { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8S */
+ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */
+ { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */
+ { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
+
+/*
+ * Lowlevel I/O - busmaster
+ */
+
+static u8 igetbyte(intel8x0_t *chip, u32 offset)
+{
+ if (chip->bm_mmio)
+ return readb(chip->remap_bmaddr + offset);
+ else
+ return inb(chip->bmaddr + offset);
+}
+
+static u16 igetword(intel8x0_t *chip, u32 offset)
+{
+ if (chip->bm_mmio)
+ return readw(chip->remap_bmaddr + offset);
+ else
+ return inw(chip->bmaddr + offset);
+}
+
+static u32 igetdword(intel8x0_t *chip, u32 offset)
+{
+ if (chip->bm_mmio)
+ return readl(chip->remap_bmaddr + offset);
+ else
+ return inl(chip->bmaddr + offset);
+}
+
+static void iputbyte(intel8x0_t *chip, u32 offset, u8 val)
+{
+ if (chip->bm_mmio)
+ writeb(val, chip->remap_bmaddr + offset);
+ else
+ outb(val, chip->bmaddr + offset);
+}
+
+static void iputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+ if (chip->bm_mmio)
+ writew(val, chip->remap_bmaddr + offset);
+ else
+ outw(val, chip->bmaddr + offset);
+}
+
+static void iputdword(intel8x0_t *chip, u32 offset, u32 val)
+{
+ if (chip->bm_mmio)
+ writel(val, chip->remap_bmaddr + offset);
+ else
+ outl(val, chip->bmaddr + offset);
+}
+
+/*
+ * Lowlevel I/O - AC'97 registers
+ */
+
+static u16 iagetword(intel8x0_t *chip, u32 offset)
+{
+ if (chip->mmio)
+ return readw(chip->remap_addr + offset);
+ else
+ return inw(chip->addr + offset);
+}
+
+static void iaputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+ if (chip->mmio)
+ writew(val, chip->remap_addr + offset);
+ else
+ outw(val, chip->addr + offset);
+}
+
+/*
+ * Basic I/O
+ */
+
+/*
+ * access to AC97 codec via normal i/o (for ICH and SIS7012)
+ */
+
+/* return the GLOB_STA bit for the corresponding codec */
+static unsigned int get_ich_codec_bit(intel8x0_t *chip, unsigned int codec)
+{
+ static unsigned int codec_bit[3] = {
+ ICH_PCR, ICH_SCR, ICH_TCR
+ };
+ snd_assert(codec < 3, return ICH_PCR);
+ if (chip->device_type == DEVICE_INTEL_ICH4)
+ codec = chip->ac97_sdin[codec];
+ return codec_bit[codec];
+}
+
+static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec)
+{
+ int time;
+
+ if (codec > 2)
+ return -EIO;
+ if (chip->in_sdin_init) {
+ /* we don't know the ready bit assignment at the moment */
+ /* so we check any */
+ codec = ICH_PCR | ICH_SCR | ICH_TCR;
+ } else {
+ codec = get_ich_codec_bit(chip, codec);
+ }
+
+ /* codec ready ? */
+ if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0)
+ return -EIO;
+
+ /* Anyone holding a semaphore for 1 msec should be shot... */
+ time = 100;
+ do {
+ if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS))
+ return 0;
+ udelay(10);
+ } while (time--);
+
+ /* access to some forbidden (non existant) ac97 registers will not
+ * reset the semaphore. So even if you don't get the semaphore, still
+ * continue the access. We don't need the semaphore anyway. */
+ snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+ igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
+ iagetword(chip, 0); /* clear semaphore flag */
+ /* I don't care about the semaphore */
+ return -EBUSY;
+}
+
+static void snd_intel8x0_codec_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ intel8x0_t *chip = ac97->private_data;
+
+ if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
+ if (! chip->in_ac97_init)
+ snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ }
+ iaputword(chip, reg + ac97->num * 0x80, val);
+}
+
+static unsigned short snd_intel8x0_codec_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ intel8x0_t *chip = ac97->private_data;
+ unsigned short res;
+ unsigned int tmp;
+
+ if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
+ if (! chip->in_ac97_init)
+ snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ res = 0xffff;
+ } else {
+ res = iagetword(chip, reg + ac97->num * 0x80);
+ if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ /* reset RCS and preserve other R/WC bits */
+ iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
+ if (! chip->in_ac97_init)
+ snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+ res = 0xffff;
+ }
+ }
+ return res;
+}
+
+static void snd_intel8x0_codec_read_test(intel8x0_t *chip, unsigned int codec)
+{
+ unsigned int tmp;
+
+ if (snd_intel8x0_codec_semaphore(chip, codec) >= 0) {
+ iagetword(chip, codec * 0x80);
+ if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ /* reset RCS and preserve other R/WC bits */
+ iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
+ }
+ }
+}
+
+/*
+ * access to AC97 for Ali5455
+ */
+static int snd_intel8x0_ali_codec_ready(intel8x0_t *chip, int mask)
+{
+ int count = 0;
+ for (count = 0; count < 0x7f; count++) {
+ int val = igetbyte(chip, ICHREG(ALI_CSPSR));
+ if (val & mask)
+ return 0;
+ }
+ snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n");
+ return -EBUSY;
+}
+
+static int snd_intel8x0_ali_codec_semaphore(intel8x0_t *chip)
+{
+ int time = 100;
+ while (time-- && (igetdword(chip, ICHREG(ALI_CAS)) & ALI_CAS_SEM_BUSY))
+ udelay(1);
+ if (! time)
+ snd_printk(KERN_WARNING "ali_codec_semaphore timeout\n");
+ return snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_CODEC_READY);
+}
+
+static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg)
+{
+ intel8x0_t *chip = ac97->private_data;
+ unsigned short data = 0xffff;
+
+ if (snd_intel8x0_ali_codec_semaphore(chip))
+ goto __err;
+ reg |= ALI_CPR_ADDR_READ;
+ if (ac97->num)
+ reg |= ALI_CPR_ADDR_SECONDARY;
+ iputword(chip, ICHREG(ALI_CPR_ADDR), reg);
+ if (snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_READ_OK))
+ goto __err;
+ data = igetword(chip, ICHREG(ALI_SPR));
+ __err:
+ return data;
+}
+
+static void snd_intel8x0_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+ intel8x0_t *chip = ac97->private_data;
+
+ if (snd_intel8x0_ali_codec_semaphore(chip))
+ return;
+ iputword(chip, ICHREG(ALI_CPR), val);
+ if (ac97->num)
+ reg |= ALI_CPR_ADDR_SECONDARY;
+ iputword(chip, ICHREG(ALI_CPR_ADDR), reg);
+ snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_WRITE_OK);
+}
+
+
+/*
+ * DMA I/O
+ */
+static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev)
+{
+ int idx;
+ u32 *bdbar = ichdev->bdbar;
+ unsigned long port = ichdev->reg_offset;
+
+ iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
+ if (ichdev->size == ichdev->fragsize) {
+ ichdev->ack_reload = ichdev->ack = 2;
+ ichdev->fragsize1 = ichdev->fragsize >> 1;
+ for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) {
+ bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf);
+ bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+ ichdev->fragsize1 >> ichdev->pos_shift);
+ bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1));
+ bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+ ichdev->fragsize1 >> ichdev->pos_shift);
+ }
+ ichdev->frags = 2;
+ } else {
+ ichdev->ack_reload = ichdev->ack = 1;
+ ichdev->fragsize1 = ichdev->fragsize;
+ for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) {
+ bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size));
+ bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+ ichdev->fragsize >> ichdev->pos_shift);
+ // printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
+ }
+ ichdev->frags = ichdev->size / ichdev->fragsize;
+ }
+ iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
+ ichdev->civ = 0;
+ iputbyte(chip, port + ICH_REG_OFF_CIV, 0);
+ ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
+ ichdev->position = 0;
+#if 0
+ printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
+ ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
+#endif
+ /* clear interrupts */
+ iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
+}
+
+#ifdef __i386__
+/*
+ * Intel 82443MX running a 100MHz processor system bus has a hardware bug,
+ * which aborts PCI busmaster for audio transfer. A workaround is to set
+ * the pages as non-cached. For details, see the errata in
+ * http://www.intel.com/design/chipsets/specupdt/245051.htm
+ */
+static void fill_nocache(void *buf, int size, int nocache)
+{
+ size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ change_page_attr(virt_to_page(buf), size, nocache ? PAGE_KERNEL_NOCACHE : PAGE_KERNEL);
+ global_flush_tlb();
+}
+#else
+#define fill_nocache(buf,size,nocache)
+#endif
+
+/*
+ * Interrupt handler
+ */
+
+static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev)
+{
+ unsigned long port = ichdev->reg_offset;
+ int status, civ, i, step;
+ int ack = 0;
+
+ spin_lock(&chip->reg_lock);
+ status = igetbyte(chip, port + ichdev->roff_sr);
+ civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
+ if (!(status & ICH_BCIS)) {
+ step = 0;
+ } else if (civ == ichdev->civ) {
+ // snd_printd("civ same %d\n", civ);
+ step = 1;
+ ichdev->civ++;
+ ichdev->civ &= ICH_REG_LVI_MASK;
+ } else {
+ step = civ - ichdev->civ;
+ if (step < 0)
+ step += ICH_REG_LVI_MASK + 1;
+ // if (step != 1)
+ // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
+ ichdev->civ = civ;
+ }
+
+ ichdev->position += step * ichdev->fragsize1;
+ if (! chip->in_measurement)
+ ichdev->position %= ichdev->size;
+ ichdev->lvi += step;
+ ichdev->lvi &= ICH_REG_LVI_MASK;
+ iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
+ for (i = 0; i < step; i++) {
+ ichdev->lvi_frag++;
+ ichdev->lvi_frag %= ichdev->frags;
+ ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
+ // printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));
+ if (--ichdev->ack == 0) {
+ ichdev->ack = ichdev->ack_reload;
+ ack = 1;
+ }
+ }
+ spin_unlock(&chip->reg_lock);
+ if (ack && ichdev->substream) {
+ snd_pcm_period_elapsed(ichdev->substream);
+ }
+ iputbyte(chip, port + ichdev->roff_sr,
+ status & (ICH_FIFOE | ICH_BCIS | ICH_LVBCI));
+}
+
+static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ intel8x0_t *chip = dev_id;
+ ichdev_t *ichdev;
+ unsigned int status;
+ unsigned int i;
+
+ status = igetdword(chip, chip->int_sta_reg);
+ if (status == 0xffffffff) /* we are not yet resumed */
+ return IRQ_NONE;
+
+ if ((status & chip->int_sta_mask) == 0) {
+ if (status) {
+ /* ack */
+ iputdword(chip, chip->int_sta_reg, status);
+ if (! chip->buggy_irq)
+ status = 0;
+ }
+ return IRQ_RETVAL(status);
+ }
+
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev = &chip->ichd[i];
+ if (status & ichdev->int_sta_mask)
+ snd_intel8x0_update(chip, ichdev);
+ }
+
+ /* ack them */
+ iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * PCM part
+ */
+
+static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ ichdev_t *ichdev = get_ichdev(substream);
+ unsigned char val = 0;
+ unsigned long port = ichdev->reg_offset;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ val = ICH_IOCE | ICH_STARTBM;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ val = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = ICH_IOCE;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = ICH_IOCE | ICH_STARTBM;
+ break;
+ default:
+ return -EINVAL;
+ }
+ iputbyte(chip, port + ICH_REG_OFF_CR, val);
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ /* wait until DMA stopped */
+ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ;
+ /* reset whole DMA things */
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+ }
+ return 0;
+}
+
+static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ ichdev_t *ichdev = get_ichdev(substream);
+ unsigned long port = ichdev->reg_offset;
+ static int fiforeg[] = { ICHREG(ALI_FIFOCR1), ICHREG(ALI_FIFOCR2), ICHREG(ALI_FIFOCR3) };
+ unsigned int val, fifo;
+
+ val = igetdword(chip, ICHREG(ALI_DMACR));
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* clear FIFO for synchronization of channels */
+ fifo = igetdword(chip, fiforeg[ichdev->ali_slot / 4]);
+ fifo &= ~(0xff << (ichdev->ali_slot % 4));
+ fifo |= 0x83 << (ichdev->ali_slot % 4);
+ iputdword(chip, fiforeg[ichdev->ali_slot / 4], fifo);
+ }
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+ val &= ~(1 << (ichdev->ali_slot + 16)); /* clear PAUSE flag */
+ iputdword(chip, ICHREG(ALI_DMACR), val | (1 << ichdev->ali_slot)); /* start DMA */
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ iputdword(chip, ICHREG(ALI_DMACR), val | (1 << (ichdev->ali_slot + 16))); /* pause */
+ iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+ while (igetbyte(chip, port + ICH_REG_OFF_CR))
+ ;
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ break;
+ /* reset whole DMA things */
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+ /* clear interrupts */
+ iputbyte(chip, port + ICH_REG_OFF_SR, igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e);
+ iputdword(chip, ICHREG(ALI_INTERRUPTSR),
+ igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ichdev->int_sta_mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ ichdev_t *ichdev = get_ichdev(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int dbl = params_rate(hw_params) > 48000;
+ int err;
+
+ if (chip->fix_nocache && ichdev->page_attr_changed) {
+ fill_nocache(runtime->dma_area, runtime->dma_bytes, 0); /* clear */
+ ichdev->page_attr_changed = 0;
+ }
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+ if (chip->fix_nocache) {
+ if (runtime->dma_area && ! ichdev->page_attr_changed) {
+ fill_nocache(runtime->dma_area, runtime->dma_bytes, 1);
+ ichdev->page_attr_changed = 1;
+ }
+ }
+ if (ichdev->pcm_open_flag) {
+ snd_ac97_pcm_close(ichdev->pcm);
+ ichdev->pcm_open_flag = 0;
+ }
+ err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
+ params_channels(hw_params),
+ ichdev->pcm->r[dbl].slots);
+ if (err >= 0) {
+ ichdev->pcm_open_flag = 1;
+ /* Force SPDIF setting */
+ if (ichdev->ichd == ICHD_PCMOUT && chip->spdif_idx < 0)
+ snd_ac97_set_rate(ichdev->pcm->r[0].codec[0], AC97_SPDIF, params_rate(hw_params));
+ }
+ return err;
+}
+
+static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ ichdev_t *ichdev = get_ichdev(substream);
+
+ if (ichdev->pcm_open_flag) {
+ snd_ac97_pcm_close(ichdev->pcm);
+ ichdev->pcm_open_flag = 0;
+ }
+ if (chip->fix_nocache && ichdev->page_attr_changed) {
+ fill_nocache(substream->runtime->dma_area, substream->runtime->dma_bytes, 0);
+ ichdev->page_attr_changed = 0;
+ }
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static void snd_intel8x0_setup_pcm_out(intel8x0_t *chip,
+ snd_pcm_runtime_t *runtime)
+{
+ unsigned int cnt;
+ int dbl = runtime->rate > 48000;
+ switch (chip->device_type) {
+ case DEVICE_ALI:
+ cnt = igetdword(chip, ICHREG(ALI_SCR));
+ cnt &= ~ICH_ALI_SC_PCM_246_MASK;
+ if (runtime->channels == 4 || dbl)
+ cnt |= ICH_ALI_SC_PCM_4;
+ else if (runtime->channels == 6)
+ cnt |= ICH_ALI_SC_PCM_6;
+ iputdword(chip, ICHREG(ALI_SCR), cnt);
+ break;
+ case DEVICE_SIS:
+ cnt = igetdword(chip, ICHREG(GLOB_CNT));
+ cnt &= ~ICH_SIS_PCM_246_MASK;
+ if (runtime->channels == 4 || dbl)
+ cnt |= ICH_SIS_PCM_4;
+ else if (runtime->channels == 6)
+ cnt |= ICH_SIS_PCM_6;
+ iputdword(chip, ICHREG(GLOB_CNT), cnt);
+ break;
+ default:
+ cnt = igetdword(chip, ICHREG(GLOB_CNT));
+ cnt &= ~(ICH_PCM_246_MASK | ICH_PCM_20BIT);
+ if (runtime->channels == 4 || dbl)
+ cnt |= ICH_PCM_4;
+ else if (runtime->channels == 6)
+ cnt |= ICH_PCM_6;
+ if (chip->device_type == DEVICE_NFORCE) {
+ /* reset to 2ch once to keep the 6 channel data in alignment,
+ * to start from Front Left always
+ */
+ if (cnt & ICH_PCM_246_MASK) {
+ iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_PCM_246_MASK);
+ spin_unlock_irq(&chip->reg_lock);
+ msleep(50); /* grrr... */
+ spin_lock_irq(&chip->reg_lock);
+ }
+ } else if (chip->device_type == DEVICE_INTEL_ICH4) {
+ if (runtime->sample_bits > 16)
+ cnt |= ICH_PCM_20BIT;
+ }
+ iputdword(chip, ICHREG(GLOB_CNT), cnt);
+ break;
+ }
+}
+
+static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ichdev_t *ichdev = get_ichdev(substream);
+
+ ichdev->physbuf = runtime->dma_addr;
+ ichdev->size = snd_pcm_lib_buffer_bytes(substream);
+ ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
+ spin_lock_irq(&chip->reg_lock);
+ if (ichdev->ichd == ICHD_PCMOUT) {
+ snd_intel8x0_setup_pcm_out(chip, runtime);
+ if (chip->device_type == DEVICE_INTEL_ICH4) {
+ ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
+ }
+ }
+ snd_intel8x0_setup_periods(chip, ichdev);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ ichdev_t *ichdev = get_ichdev(substream);
+ size_t ptr1, ptr;
+ int civ, timeout = 100;
+ unsigned int position;
+
+ spin_lock(&chip->reg_lock);
+ do {
+ civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
+ ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
+ position = ichdev->position;
+ if (ptr1 == 0) {
+ udelay(10);
+ continue;
+ }
+ if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
+ ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
+ break;
+ } while (timeout--);
+ ptr1 <<= ichdev->pos_shift;
+ ptr = ichdev->fragsize1 - ptr1;
+ ptr += position;
+ spin_unlock(&chip->reg_lock);
+ if (ptr >= ichdev->size)
+ return 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_intel8x0_stream =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static unsigned int channels4[] = {
+ 2, 4,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = {
+ .count = ARRAY_SIZE(channels4),
+ .list = channels4,
+ .mask = 0,
+};
+
+static unsigned int channels6[] = {
+ 2, 4, 6,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = {
+ .count = ARRAY_SIZE(channels6),
+ .list = channels6,
+ .mask = 0,
+};
+
+static int snd_intel8x0_pcm_open(snd_pcm_substream_t * substream, ichdev_t *ichdev)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ ichdev->substream = substream;
+ runtime->hw = snd_intel8x0_stream;
+ runtime->hw.rates = ichdev->pcm->rates;
+ snd_pcm_limit_hw_rates(runtime);
+ if (chip->device_type == DEVICE_SIS) {
+ runtime->hw.buffer_bytes_max = 64*1024;
+ runtime->hw.period_bytes_max = 64*1024;
+ }
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ runtime->private_data = ichdev;
+ return 0;
+}
+
+static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ err = snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMOUT]);
+ if (err < 0)
+ return err;
+
+ if (chip->multi6) {
+ runtime->hw.channels_max = 6;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6);
+ } else if (chip->multi4) {
+ runtime->hw.channels_max = 4;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels4);
+ }
+ if (chip->dra) {
+ snd_ac97_pcm_double_rate_rules(runtime);
+ }
+ if (chip->smp20bit) {
+ runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
+ }
+ return 0;
+}
+
+static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ICHD_PCMOUT].substream = NULL;
+ return 0;
+}
+
+static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMIN]);
+}
+
+static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ICHD_PCMIN].substream = NULL;
+ return 0;
+}
+
+static int snd_intel8x0_mic_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC]);
+}
+
+static int snd_intel8x0_mic_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ICHD_MIC].substream = NULL;
+ return 0;
+}
+
+static int snd_intel8x0_mic2_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC2]);
+}
+
+static int snd_intel8x0_mic2_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ICHD_MIC2].substream = NULL;
+ return 0;
+}
+
+static int snd_intel8x0_capture2_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCM2IN]);
+}
+
+static int snd_intel8x0_capture2_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ICHD_PCM2IN].substream = NULL;
+ return 0;
+}
+
+static int snd_intel8x0_spdif_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[idx]);
+}
+
+static int snd_intel8x0_spdif_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
+
+ chip->ichd[idx].substream = NULL;
+ return 0;
+}
+
+static int snd_intel8x0_ali_ac97spdifout_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int val;
+
+ spin_lock_irq(&chip->reg_lock);
+ val = igetdword(chip, ICHREG(ALI_INTERFACECR));
+ val |= ICH_ALI_IF_AC97SP;
+ iputdword(chip, ICHREG(ALI_INTERFACECR), val);
+ /* also needs to set ALI_SC_CODEC_SPDF correctly */
+ spin_unlock_irq(&chip->reg_lock);
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_ac97spdifout_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int val;
+
+ chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
+ spin_lock_irq(&chip->reg_lock);
+ val = igetdword(chip, ICHREG(ALI_INTERFACECR));
+ val &= ~ICH_ALI_IF_AC97SP;
+ iputdword(chip, ICHREG(ALI_INTERFACECR), val);
+ spin_unlock_irq(&chip->reg_lock);
+
+ return 0;
+}
+
+static int snd_intel8x0_ali_spdifin_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFIN]);
+}
+
+static int snd_intel8x0_ali_spdifin_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ALID_SPDIFIN].substream = NULL;
+ return 0;
+}
+
+#if 0 // NYI
+static int snd_intel8x0_ali_spdifout_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_spdifout_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ALID_SPDIFOUT].substream = NULL;
+ return 0;
+}
+#endif
+
+static snd_pcm_ops_t snd_intel8x0_playback_ops = {
+ .open = snd_intel8x0_playback_open,
+ .close = snd_intel8x0_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_ops = {
+ .open = snd_intel8x0_capture_open,
+ .close = snd_intel8x0_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
+ .open = snd_intel8x0_mic_open,
+ .close = snd_intel8x0_mic_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic2_ops = {
+ .open = snd_intel8x0_mic2_open,
+ .close = snd_intel8x0_mic2_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture2_ops = {
+ .open = snd_intel8x0_capture2_open,
+ .close = snd_intel8x0_capture2_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_spdif_ops = {
+ .open = snd_intel8x0_spdif_open,
+ .close = snd_intel8x0_spdif_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_playback_ops = {
+ .open = snd_intel8x0_playback_open,
+ .close = snd_intel8x0_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_ali_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_ops = {
+ .open = snd_intel8x0_capture_open,
+ .close = snd_intel8x0_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_ali_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_mic_ops = {
+ .open = snd_intel8x0_mic_open,
+ .close = snd_intel8x0_mic_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_ali_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_ac97spdifout_ops = {
+ .open = snd_intel8x0_ali_ac97spdifout_open,
+ .close = snd_intel8x0_ali_ac97spdifout_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_ali_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_spdifin_ops = {
+ .open = snd_intel8x0_ali_spdifin_open,
+ .close = snd_intel8x0_ali_spdifin_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+#if 0 // NYI
+static snd_pcm_ops_t snd_intel8x0_ali_spdifout_ops = {
+ .open = snd_intel8x0_ali_spdifout_open,
+ .close = snd_intel8x0_ali_spdifout_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0_pcm_prepare,
+ .trigger = snd_intel8x0_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+#endif // NYI
+
+struct ich_pcm_table {
+ char *suffix;
+ snd_pcm_ops_t *playback_ops;
+ snd_pcm_ops_t *capture_ops;
+ size_t prealloc_size;
+ size_t prealloc_max_size;
+ int ac97_idx;
+};
+
+static int __devinit snd_intel8x0_pcm1(intel8x0_t *chip, int device, struct ich_pcm_table *rec)
+{
+ snd_pcm_t *pcm;
+ int err;
+ char name[32];
+
+ if (rec->suffix)
+ sprintf(name, "Intel ICH - %s", rec->suffix);
+ else
+ strcpy(name, "Intel ICH");
+ err = snd_pcm_new(chip->card, name, device,
+ rec->playback_ops ? 1 : 0,
+ rec->capture_ops ? 1 : 0, &pcm);
+ if (err < 0)
+ return err;
+
+ if (rec->playback_ops)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, rec->playback_ops);
+ if (rec->capture_ops)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, rec->capture_ops);
+
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ if (rec->suffix)
+ sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
+ else
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcm[device] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ rec->prealloc_size, rec->prealloc_max_size);
+
+ return 0;
+}
+
+static struct ich_pcm_table intel_pcms[] __devinitdata = {
+ {
+ .playback_ops = &snd_intel8x0_playback_ops,
+ .capture_ops = &snd_intel8x0_capture_ops,
+ .prealloc_size = 64 * 1024,
+ .prealloc_max_size = 128 * 1024,
+ },
+ {
+ .suffix = "MIC ADC",
+ .capture_ops = &snd_intel8x0_capture_mic_ops,
+ .prealloc_size = 0,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = ICHD_MIC,
+ },
+ {
+ .suffix = "MIC2 ADC",
+ .capture_ops = &snd_intel8x0_capture_mic2_ops,
+ .prealloc_size = 0,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = ICHD_MIC2,
+ },
+ {
+ .suffix = "ADC2",
+ .capture_ops = &snd_intel8x0_capture2_ops,
+ .prealloc_size = 0,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = ICHD_PCM2IN,
+ },
+ {
+ .suffix = "IEC958",
+ .playback_ops = &snd_intel8x0_spdif_ops,
+ .prealloc_size = 64 * 1024,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = ICHD_SPBAR,
+ },
+};
+
+static struct ich_pcm_table nforce_pcms[] __devinitdata = {
+ {
+ .playback_ops = &snd_intel8x0_playback_ops,
+ .capture_ops = &snd_intel8x0_capture_ops,
+ .prealloc_size = 64 * 1024,
+ .prealloc_max_size = 128 * 1024,
+ },
+ {
+ .suffix = "MIC ADC",
+ .capture_ops = &snd_intel8x0_capture_mic_ops,
+ .prealloc_size = 0,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = NVD_MIC,
+ },
+ {
+ .suffix = "IEC958",
+ .playback_ops = &snd_intel8x0_spdif_ops,
+ .prealloc_size = 64 * 1024,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = NVD_SPBAR,
+ },
+};
+
+static struct ich_pcm_table ali_pcms[] __devinitdata = {
+ {
+ .playback_ops = &snd_intel8x0_ali_playback_ops,
+ .capture_ops = &snd_intel8x0_ali_capture_ops,
+ .prealloc_size = 64 * 1024,
+ .prealloc_max_size = 128 * 1024,
+ },
+ {
+ .suffix = "MIC ADC",
+ .capture_ops = &snd_intel8x0_ali_capture_mic_ops,
+ .prealloc_size = 0,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = ALID_MIC,
+ },
+ {
+ .suffix = "IEC958",
+ .playback_ops = &snd_intel8x0_ali_ac97spdifout_ops,
+ .capture_ops = &snd_intel8x0_ali_spdifin_ops,
+ .prealloc_size = 64 * 1024,
+ .prealloc_max_size = 128 * 1024,
+ .ac97_idx = ALID_AC97SPDIFOUT,
+ },
+#if 0 // NYI
+ {
+ .suffix = "HW IEC958",
+ .playback_ops = &snd_intel8x0_ali_spdifout_ops,
+ .prealloc_size = 64 * 1024,
+ .prealloc_max_size = 128 * 1024,
+ },
+#endif
+};
+
+static int __devinit snd_intel8x0_pcm(intel8x0_t *chip)
+{
+ int i, tblsize, device, err;
+ struct ich_pcm_table *tbl, *rec;
+
+ switch (chip->device_type) {
+ case DEVICE_INTEL_ICH4:
+ tbl = intel_pcms;
+ tblsize = ARRAY_SIZE(intel_pcms);
+ break;
+ case DEVICE_NFORCE:
+ tbl = nforce_pcms;
+ tblsize = ARRAY_SIZE(nforce_pcms);
+ break;
+ case DEVICE_ALI:
+ tbl = ali_pcms;
+ tblsize = ARRAY_SIZE(ali_pcms);
+ break;
+ default:
+ tbl = intel_pcms;
+ tblsize = 2;
+ break;
+ }
+
+ device = 0;
+ for (i = 0; i < tblsize; i++) {
+ rec = tbl + i;
+ if (i > 0 && rec->ac97_idx) {
+ /* activate PCM only when associated AC'97 codec */
+ if (! chip->ichd[rec->ac97_idx].pcm)
+ continue;
+ }
+ err = snd_intel8x0_pcm1(chip, device, rec);
+ if (err < 0)
+ return err;
+ device++;
+ }
+
+ chip->pcm_devs = device;
+ return 0;
+}
+
+
+/*
+ * Mixer part
+ */
+
+static void snd_intel8x0_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ intel8x0_t *chip = bus->private_data;
+ chip->ac97_bus = NULL;
+}
+
+static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97)
+{
+ intel8x0_t *chip = ac97->private_data;
+ chip->ac97[ac97->num] = NULL;
+}
+
+static struct ac97_pcm ac97_pcm_defs[] __devinitdata = {
+ /* front PCM */
+ {
+ .exclusive = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_PCM_LEFT) |
+ (1 << AC97_SLOT_PCM_RIGHT) |
+ (1 << AC97_SLOT_PCM_CENTER) |
+ (1 << AC97_SLOT_PCM_SLEFT) |
+ (1 << AC97_SLOT_PCM_SRIGHT) |
+ (1 << AC97_SLOT_LFE)
+ },
+ {
+ .slots = (1 << AC97_SLOT_PCM_LEFT) |
+ (1 << AC97_SLOT_PCM_RIGHT) |
+ (1 << AC97_SLOT_PCM_LEFT_0) |
+ (1 << AC97_SLOT_PCM_RIGHT_0)
+ }
+ }
+ },
+ /* PCM IN #1 */
+ {
+ .stream = 1,
+ .exclusive = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_PCM_LEFT) |
+ (1 << AC97_SLOT_PCM_RIGHT)
+ }
+ }
+ },
+ /* MIC IN #1 */
+ {
+ .stream = 1,
+ .exclusive = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_MIC)
+ }
+ }
+ },
+ /* S/PDIF PCM */
+ {
+ .exclusive = 1,
+ .spdif = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_SPDIF_LEFT2) |
+ (1 << AC97_SLOT_SPDIF_RIGHT2)
+ }
+ }
+ },
+ /* PCM IN #2 */
+ {
+ .stream = 1,
+ .exclusive = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_PCM_LEFT) |
+ (1 << AC97_SLOT_PCM_RIGHT)
+ }
+ }
+ },
+ /* MIC IN #2 */
+ {
+ .stream = 1,
+ .exclusive = 1,
+ .r = { {
+ .slots = (1 << AC97_SLOT_MIC)
+ }
+ }
+ },
+};
+
+static struct ac97_quirk ac97_quirks[] __devinitdata = {
+ {
+ .vendor = 0x0e11,
+ .device = 0x008a,
+ .name = "Compaq Evo W4000", /* AD1885 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x0e11,
+ .device = 0x00b8,
+ .name = "Compaq Evo D510C",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x0e11,
+ .device = 0x0860,
+ .name = "HP/Compaq nx7010",
+ .type = AC97_TUNE_MUTE_LED
+ },
+ {
+ .vendor = 0x1014,
+ .device = 0x1f00,
+ .name = "MS-9128",
+ .type = AC97_TUNE_ALC_JACK
+ },
+ {
+ .vendor = 0x1028,
+ .device = 0x00d8,
+ .name = "Dell Precision 530", /* AD1885 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1028,
+ .device = 0x010d,
+ .name = "Dell", /* which model? AD1885 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1028,
+ .device = 0x0126,
+ .name = "Dell Optiplex GX260", /* AD1981A */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1028,
+ .device = 0x012c,
+ .name = "Dell Precision 650", /* AD1981A */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1028,
+ .device = 0x012d,
+ .name = "Dell Precision 450", /* AD1981B*/
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1028,
+ .device = 0x0147,
+ .name = "Dell", /* which model? AD1981B*/
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1028,
+ .device = 0x0163,
+ .name = "Dell Unknown", /* STAC9750/51 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x103c,
+ .device = 0x006d,
+ .name = "HP zv5000",
+ .type = AC97_TUNE_MUTE_LED /*AD1981B*/
+ },
+ { /* FIXME: which codec? */
+ .vendor = 0x103c,
+ .device = 0x00c3,
+ .name = "HP xw6000",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x103c,
+ .device = 0x088c,
+ .name = "HP nc8000",
+ .type = AC97_TUNE_MUTE_LED
+ },
+ {
+ .vendor = 0x103c,
+ .device = 0x0890,
+ .name = "HP nc6000",
+ .type = AC97_TUNE_MUTE_LED
+ },
+ {
+ .vendor = 0x103c,
+ .device = 0x129d,
+ .name = "HP xw8000",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x103c,
+ .device = 0x12f1,
+ .name = "HP xw8200", /* AD1981B*/
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x103c,
+ .device = 0x12f2,
+ .name = "HP xw6200",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x103c,
+ .device = 0x3008,
+ .name = "HP xw4200", /* AD1981B*/
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x104d,
+ .device = 0x8197,
+ .name = "Sony S1XP",
+ .type = AC97_TUNE_INV_EAPD
+ },
+ {
+ .vendor = 0x1043,
+ .device = 0x80f3,
+ .name = "ASUS ICH5/AD1985",
+ .type = AC97_TUNE_AD_SHARING
+ },
+ {
+ .vendor = 0x10cf,
+ .device = 0x11c3,
+ .name = "Fujitsu-Siemens E4010",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x10cf,
+ .device = 0x1253,
+ .name = "Fujitsu S6210", /* STAC9750/51 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x10f1,
+ .device = 0x2665,
+ .name = "Fujitsu-Siemens Celsius", /* AD1981? */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x10f1,
+ .device = 0x2885,
+ .name = "AMD64 Mobo", /* ALC650 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x110a,
+ .device = 0x0056,
+ .name = "Fujitsu-Siemens Scenic", /* AD1981? */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x11d4,
+ .device = 0x5375,
+ .name = "ADI AD1985 (discrete)",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1462,
+ .device = 0x5470,
+ .name = "MSI P4 ATX 645 Ultra",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1734,
+ .device = 0x0088,
+ .name = "Fujitsu-Siemens D1522", /* AD1981 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x8086,
+ .device = 0x2000,
+ .mask = 0xfff0,
+ .name = "Intel ICH5/AD1985",
+ .type = AC97_TUNE_AD_SHARING
+ },
+ {
+ .vendor = 0x8086,
+ .device = 0x4000,
+ .mask = 0xfff0,
+ .name = "Intel ICH5/AD1985",
+ .type = AC97_TUNE_AD_SHARING
+ },
+ {
+ .vendor = 0x8086,
+ .device = 0x4856,
+ .name = "Intel D845WN (82801BA)",
+ .type = AC97_TUNE_SWAP_HP
+ },
+ {
+ .vendor = 0x8086,
+ .device = 0x4d44,
+ .name = "Intel D850EMV2", /* AD1885 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x8086,
+ .device = 0x4d56,
+ .name = "Intel ICH/AD1885",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x8086,
+ .device = 0x6000,
+ .mask = 0xfff0,
+ .name = "Intel ICH5/AD1985",
+ .type = AC97_TUNE_AD_SHARING
+ },
+ {
+ .vendor = 0x8086,
+ .device = 0xe000,
+ .mask = 0xfff0,
+ .name = "Intel ICH5/AD1985",
+ .type = AC97_TUNE_AD_SHARING
+ },
+#if 0 /* FIXME: this seems wrong on most boards */
+ {
+ .vendor = 0x8086,
+ .device = 0xa000,
+ .mask = 0xfff0,
+ .name = "Intel ICH5/AD1985",
+ .type = AC97_TUNE_HP_ONLY
+ },
+#endif
+ { } /* terminator */
+};
+
+static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock, const char *quirk_override)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int err;
+ unsigned int i, codecs;
+ unsigned int glob_sta = 0;
+ ac97_bus_ops_t *ops;
+ static ac97_bus_ops_t standard_bus_ops = {
+ .write = snd_intel8x0_codec_write,
+ .read = snd_intel8x0_codec_read,
+ };
+ static ac97_bus_ops_t ali_bus_ops = {
+ .write = snd_intel8x0_ali_codec_write,
+ .read = snd_intel8x0_ali_codec_read,
+ };
+
+ chip->spdif_idx = -1; /* use PCMOUT (or disabled) */
+ switch (chip->device_type) {
+ case DEVICE_NFORCE:
+ chip->spdif_idx = NVD_SPBAR;
+ break;
+ case DEVICE_ALI:
+ chip->spdif_idx = ALID_AC97SPDIFOUT;
+ break;
+ case DEVICE_INTEL_ICH4:
+ chip->spdif_idx = ICHD_SPBAR;
+ break;
+ };
+
+ chip->in_ac97_init = 1;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_intel8x0_mixer_free_ac97;
+ ac97.scaps = AC97_SCAP_SKIP_MODEM;
+ if (chip->xbox)
+ ac97.scaps |= AC97_SCAP_DETECT_BY_VENDOR;
+ if (chip->device_type != DEVICE_ALI) {
+ glob_sta = igetdword(chip, ICHREG(GLOB_STA));
+ ops = &standard_bus_ops;
+ if (chip->device_type == DEVICE_INTEL_ICH4) {
+ codecs = 0;
+ if (glob_sta & ICH_PCR)
+ codecs++;
+ if (glob_sta & ICH_SCR)
+ codecs++;
+ if (glob_sta & ICH_TCR)
+ codecs++;
+ chip->in_sdin_init = 1;
+ for (i = 0; i < codecs; i++) {
+ snd_intel8x0_codec_read_test(chip, i);
+ chip->ac97_sdin[i] = igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
+ }
+ chip->in_sdin_init = 0;
+ } else {
+ codecs = glob_sta & ICH_SCR ? 2 : 1;
+ }
+ } else {
+ ops = &ali_bus_ops;
+ codecs = 1;
+ /* detect the secondary codec */
+ for (i = 0; i < 100; i++) {
+ unsigned int reg = igetdword(chip, ICHREG(ALI_RTSR));
+ if (reg & 0x40) {
+ codecs = 2;
+ break;
+ }
+ iputdword(chip, ICHREG(ALI_RTSR), reg | 0x40);
+ udelay(1);
+ }
+ }
+ if ((err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus)) < 0)
+ goto __err;
+ pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
+ pbus->shared_type = AC97_SHARED_TYPE_ICH; /* shared with modem driver */
+ if (ac97_clock >= 8000 && ac97_clock <= 48000)
+ pbus->clock = ac97_clock;
+ /* FIXME: my test board doesn't work well with VRA... */
+ if (chip->device_type == DEVICE_ALI)
+ pbus->no_vra = 1;
+ else
+ pbus->dra = 1;
+ chip->ac97_bus = pbus;
+
+ ac97.pci = chip->pci;
+ for (i = 0; i < codecs; i++) {
+ ac97.num = i;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ if (err != -EACCES)
+ snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i);
+ if (i == 0)
+ goto __err;
+ continue;
+ }
+ }
+ /* tune up the primary codec */
+ snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, quirk_override);
+ /* enable separate SDINs for ICH4 */
+ if (chip->device_type == DEVICE_INTEL_ICH4)
+ pbus->isdin = 1;
+ /* find the available PCM streams */
+ i = ARRAY_SIZE(ac97_pcm_defs);
+ if (chip->device_type != DEVICE_INTEL_ICH4)
+ i -= 2; /* do not allocate PCM2IN and MIC2 */
+ if (chip->spdif_idx < 0)
+ i--; /* do not allocate S/PDIF */
+ err = snd_ac97_pcm_assign(pbus, i, ac97_pcm_defs);
+ if (err < 0)
+ goto __err;
+ chip->ichd[ICHD_PCMOUT].pcm = &pbus->pcms[0];
+ chip->ichd[ICHD_PCMIN].pcm = &pbus->pcms[1];
+ chip->ichd[ICHD_MIC].pcm = &pbus->pcms[2];
+ if (chip->spdif_idx >= 0)
+ chip->ichd[chip->spdif_idx].pcm = &pbus->pcms[3];
+ if (chip->device_type == DEVICE_INTEL_ICH4) {
+ chip->ichd[ICHD_PCM2IN].pcm = &pbus->pcms[4];
+ chip->ichd[ICHD_MIC2].pcm = &pbus->pcms[5];
+ }
+ /* enable separate SDINs for ICH4 */
+ if (chip->device_type == DEVICE_INTEL_ICH4) {
+ struct ac97_pcm *pcm = chip->ichd[ICHD_PCM2IN].pcm;
+ u8 tmp = igetbyte(chip, ICHREG(SDM));
+ tmp &= ~(ICH_DI2L_MASK|ICH_DI1L_MASK);
+ if (pcm) {
+ tmp |= ICH_SE; /* steer enable for multiple SDINs */
+ tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
+ for (i = 1; i < 4; i++) {
+ if (pcm->r[0].codec[i]) {
+ tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT;
+ break;
+ }
+ }
+ } else {
+ tmp &= ~ICH_SE; /* steer disable */
+ }
+ iputbyte(chip, ICHREG(SDM), tmp);
+ }
+ if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+ chip->multi4 = 1;
+ if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE))
+ chip->multi6 = 1;
+ }
+ if (pbus->pcms[0].r[1].rslots[0]) {
+ chip->dra = 1;
+ }
+ if (chip->device_type == DEVICE_INTEL_ICH4) {
+ if ((igetdword(chip, ICHREG(GLOB_STA)) & ICH_SAMPLE_CAP) == ICH_SAMPLE_16_20)
+ chip->smp20bit = 1;
+ }
+ if (chip->device_type == DEVICE_NFORCE) {
+ /* 48kHz only */
+ chip->ichd[chip->spdif_idx].pcm->rates = SNDRV_PCM_RATE_48000;
+ }
+ if (chip->device_type == DEVICE_INTEL_ICH4) {
+ /* use slot 10/11 for SPDIF */
+ u32 val;
+ val = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK;
+ val |= ICH_PCM_SPDIF_1011;
+ iputdword(chip, ICHREG(GLOB_CNT), val);
+ snd_ac97_update_bits(chip->ac97[0], AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4);
+ }
+ chip->in_ac97_init = 0;
+ return 0;
+
+ __err:
+ /* clear the cold-reset bit for the next chance */
+ if (chip->device_type != DEVICE_ALI)
+ iputdword(chip, ICHREG(GLOB_CNT), igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_AC97COLD);
+ return err;
+}
+
+
+/*
+ *
+ */
+
+static void do_ali_reset(intel8x0_t *chip)
+{
+ iputdword(chip, ICHREG(ALI_SCR), ICH_ALI_SC_RESET);
+ iputdword(chip, ICHREG(ALI_FIFOCR1), 0x83838383);
+ iputdword(chip, ICHREG(ALI_FIFOCR2), 0x83838383);
+ iputdword(chip, ICHREG(ALI_FIFOCR3), 0x83838383);
+ iputdword(chip, ICHREG(ALI_INTERFACECR),
+ ICH_ALI_IF_MC|ICH_ALI_IF_PI|ICH_ALI_IF_PO);
+ iputdword(chip, ICHREG(ALI_INTERRUPTCR), 0x00000000);
+ iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000);
+}
+
+#define do_delay(chip) do {\
+ set_current_state(TASK_UNINTERRUPTIBLE);\
+ schedule_timeout(1);\
+} while (0)
+
+static int snd_intel8x0_ich_chip_init(intel8x0_t *chip, int probing)
+{
+ unsigned long end_time;
+ unsigned int cnt, status, nstatus;
+
+ /* put logic to right state */
+ /* first clear status bits */
+ status = ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT;
+ if (chip->device_type == DEVICE_NFORCE)
+ status |= ICH_NVSPINT;
+ cnt = igetdword(chip, ICHREG(GLOB_STA));
+ iputdword(chip, ICHREG(GLOB_STA), cnt & status);
+
+ /* ACLink on, 2 channels */
+ cnt = igetdword(chip, ICHREG(GLOB_CNT));
+ cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
+ /* finish cold or do warm reset */
+ cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
+ iputdword(chip, ICHREG(GLOB_CNT), cnt);
+ end_time = (jiffies + (HZ / 4)) + 1;
+ do {
+ if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
+ goto __ok;
+ do_delay(chip);
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk("AC'97 warm reset still in progress? [0x%x]\n", igetdword(chip, ICHREG(GLOB_CNT)));
+ return -EIO;
+
+ __ok:
+ if (probing) {
+ /* wait for any codec ready status.
+ * Once it becomes ready it should remain ready
+ * as long as we do not disable the ac97 link.
+ */
+ end_time = jiffies + HZ;
+ do {
+ status = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+ if (status)
+ break;
+ do_delay(chip);
+ } while (time_after_eq(end_time, jiffies));
+ if (! status) {
+ /* no codec is found */
+ snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n", igetdword(chip, ICHREG(GLOB_STA)));
+ return -EIO;
+ }
+
+ if (chip->device_type == DEVICE_INTEL_ICH4)
+ /* ICH4 can have three codecs */
+ nstatus = ICH_PCR | ICH_SCR | ICH_TCR;
+ else
+ /* others up to two codecs */
+ nstatus = ICH_PCR | ICH_SCR;
+
+ /* wait for other codecs ready status. */
+ end_time = jiffies + HZ / 4;
+ while (status != nstatus && time_after_eq(end_time, jiffies)) {
+ do_delay(chip);
+ status |= igetdword(chip, ICHREG(GLOB_STA)) & nstatus;
+ }
+
+ } else {
+ /* resume phase */
+ int i;
+ status = 0;
+ for (i = 0; i < 3; i++)
+ if (chip->ac97[i])
+ status |= get_ich_codec_bit(chip, i);
+ /* wait until all the probed codecs are ready */
+ end_time = jiffies + HZ;
+ do {
+ nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+ if (status == nstatus)
+ break;
+ do_delay(chip);
+ } while (time_after_eq(end_time, jiffies));
+ }
+
+ if (chip->device_type == DEVICE_SIS) {
+ /* unmute the output on SIS7012 */
+ iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
+ }
+ if (chip->device_type == DEVICE_NFORCE) {
+ /* enable SPDIF interrupt */
+ unsigned int val;
+ pci_read_config_dword(chip->pci, 0x4c, &val);
+ val |= 0x1000000;
+ pci_write_config_dword(chip->pci, 0x4c, val);
+ }
+ return 0;
+}
+
+static int snd_intel8x0_ali_chip_init(intel8x0_t *chip, int probing)
+{
+ u32 reg;
+ int i = 0;
+
+ reg = igetdword(chip, ICHREG(ALI_SCR));
+ if ((reg & 2) == 0) /* Cold required */
+ reg |= 2;
+ else
+ reg |= 1; /* Warm */
+ reg &= ~0x80000000; /* ACLink on */
+ iputdword(chip, ICHREG(ALI_SCR), reg);
+
+ for (i = 0; i < HZ / 2; i++) {
+ if (! (igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ALI_INT_GPIO))
+ goto __ok;
+ do_delay(chip);
+ }
+ snd_printk(KERN_ERR "AC'97 reset failed.\n");
+ if (probing)
+ return -EIO;
+
+ __ok:
+ for (i = 0; i < HZ / 2; i++) {
+ reg = igetdword(chip, ICHREG(ALI_RTSR));
+ if (reg & 0x80) /* primary codec */
+ break;
+ iputdword(chip, ICHREG(ALI_RTSR), reg | 0x80);
+ do_delay(chip);
+ }
+
+ do_ali_reset(chip);
+ return 0;
+}
+
+static int snd_intel8x0_chip_init(intel8x0_t *chip, int probing)
+{
+ unsigned int i;
+ int err;
+
+ if (chip->device_type != DEVICE_ALI) {
+ if ((err = snd_intel8x0_ich_chip_init(chip, probing)) < 0)
+ return err;
+ iagetword(chip, 0); /* clear semaphore flag */
+ } else {
+ if ((err = snd_intel8x0_ali_chip_init(chip, probing)) < 0)
+ return err;
+ }
+
+ /* disable interrupts */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
+ /* reset channels */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
+ /* initialize Buffer Descriptor Lists */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr);
+ return 0;
+}
+
+static int snd_intel8x0_free(intel8x0_t *chip)
+{
+ unsigned int i;
+
+ if (chip->irq < 0)
+ goto __hw_end;
+ /* disable interrupts */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
+ /* reset channels */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
+ if (chip->device_type == DEVICE_NFORCE) {
+ /* stop the spdif interrupt */
+ unsigned int val;
+ pci_read_config_dword(chip->pci, 0x4c, &val);
+ val &= ~0x1000000;
+ pci_write_config_dword(chip->pci, 0x4c, val);
+ }
+ /* --- */
+ synchronize_irq(chip->irq);
+ __hw_end:
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->bdbars.area) {
+ if (chip->fix_nocache)
+ fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0);
+ snd_dma_free_pages(&chip->bdbars);
+ }
+ if (chip->remap_addr)
+ iounmap(chip->remap_addr);
+ if (chip->remap_bmaddr)
+ iounmap(chip->remap_bmaddr);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int intel8x0_suspend(snd_card_t *card, pm_message_t state)
+{
+ intel8x0_t *chip = card->pm_private_data;
+ int i;
+
+ for (i = 0; i < chip->pcm_devs; i++)
+ snd_pcm_suspend_all(chip->pcm[i]);
+ /* clear nocache */
+ if (chip->fix_nocache) {
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev_t *ichdev = &chip->ichd[i];
+ if (ichdev->substream && ichdev->page_attr_changed) {
+ snd_pcm_runtime_t *runtime = ichdev->substream->runtime;
+ if (runtime->dma_area)
+ fill_nocache(runtime->dma_area, runtime->dma_bytes, 0);
+ }
+ }
+ }
+ for (i = 0; i < 3; i++)
+ if (chip->ac97[i])
+ snd_ac97_suspend(chip->ac97[i]);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int intel8x0_resume(snd_card_t *card)
+{
+ intel8x0_t *chip = card->pm_private_data;
+ int i;
+
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+ snd_intel8x0_chip_init(chip, 0);
+
+ /* refill nocache */
+ if (chip->fix_nocache)
+ fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1);
+
+ for (i = 0; i < 3; i++)
+ if (chip->ac97[i])
+ snd_ac97_resume(chip->ac97[i]);
+
+ /* refill nocache */
+ if (chip->fix_nocache) {
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev_t *ichdev = &chip->ichd[i];
+ if (ichdev->substream && ichdev->page_attr_changed) {
+ snd_pcm_runtime_t *runtime = ichdev->substream->runtime;
+ if (runtime->dma_area)
+ fill_nocache(runtime->dma_area, runtime->dma_bytes, 1);
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */
+
+static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
+{
+ snd_pcm_substream_t *subs;
+ ichdev_t *ichdev;
+ unsigned long port;
+ unsigned long pos, t;
+ struct timeval start_time, stop_time;
+
+ if (chip->ac97_bus->clock != 48000)
+ return; /* specified in module option */
+
+ subs = chip->pcm[0]->streams[0].substream;
+ if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
+ snd_printk("no playback buffer allocated - aborting measure ac97 clock\n");
+ return;
+ }
+ ichdev = &chip->ichd[ICHD_PCMOUT];
+ ichdev->physbuf = subs->dma_buffer.addr;
+ ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE;
+ ichdev->substream = NULL; /* don't process interrupts */
+
+ /* set rate */
+ if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
+ snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97_bus->clock);
+ return;
+ }
+ snd_intel8x0_setup_periods(chip, ichdev);
+ port = ichdev->reg_offset;
+ spin_lock_irq(&chip->reg_lock);
+ chip->in_measurement = 1;
+ /* trigger */
+ if (chip->device_type != DEVICE_ALI)
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
+ else {
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+ iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+ }
+ do_gettimeofday(&start_time);
+ spin_unlock_irq(&chip->reg_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 20);
+ spin_lock_irq(&chip->reg_lock);
+ /* check the position */
+ pos = ichdev->fragsize1;
+ pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift;
+ pos += ichdev->position;
+ chip->in_measurement = 0;
+ do_gettimeofday(&stop_time);
+ /* stop */
+ if (chip->device_type == DEVICE_ALI) {
+ iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8));
+ iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+ while (igetbyte(chip, port + ICH_REG_OFF_CR))
+ ;
+ } else {
+ iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
+ ;
+ }
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+ spin_unlock_irq(&chip->reg_lock);
+
+ t = stop_time.tv_sec - start_time.tv_sec;
+ t *= 1000000;
+ t += stop_time.tv_usec - start_time.tv_usec;
+ printk(KERN_INFO "%s: measured %lu usecs\n", __FUNCTION__, t);
+ if (t == 0) {
+ snd_printk(KERN_ERR "?? calculation error..\n");
+ return;
+ }
+ pos = (pos / 4) * 1000;
+ pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
+ if (pos < 40000 || pos >= 60000)
+ /* abnormal value. hw problem? */
+ printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
+ else if (pos < 47500 || pos > 48500)
+ /* not 48000Hz, tuning the clock.. */
+ chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
+ printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock);
+}
+
+static void snd_intel8x0_proc_read(snd_info_entry_t * entry,
+ snd_info_buffer_t * buffer)
+{
+ intel8x0_t *chip = entry->private_data;
+ unsigned int tmp;
+
+ snd_iprintf(buffer, "Intel8x0\n\n");
+ if (chip->device_type == DEVICE_ALI)
+ return;
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ snd_iprintf(buffer, "Global control : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT)));
+ snd_iprintf(buffer, "Global status : 0x%08x\n", tmp);
+ if (chip->device_type == DEVICE_INTEL_ICH4)
+ snd_iprintf(buffer, "SDM : 0x%08x\n", igetdword(chip, ICHREG(SDM)));
+ snd_iprintf(buffer, "AC'97 codecs ready :%s%s%s%s\n",
+ tmp & ICH_PCR ? " primary" : "",
+ tmp & ICH_SCR ? " secondary" : "",
+ tmp & ICH_TCR ? " tertiary" : "",
+ (tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : "");
+ if (chip->device_type == DEVICE_INTEL_ICH4)
+ snd_iprintf(buffer, "AC'97 codecs SDIN : %i %i %i\n",
+ chip->ac97_sdin[0],
+ chip->ac97_sdin[1],
+ chip->ac97_sdin[2]);
+}
+
+static void __devinit snd_intel8x0_proc_init(intel8x0_t * chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(chip->card, "intel8x0", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0_proc_read);
+}
+
+static int snd_intel8x0_dev_free(snd_device_t *device)
+{
+ intel8x0_t *chip = device->device_data;
+ return snd_intel8x0_free(chip);
+}
+
+struct ich_reg_info {
+ unsigned int int_sta_mask;
+ unsigned int offset;
+};
+
+static int __devinit snd_intel8x0_create(snd_card_t * card,
+ struct pci_dev *pci,
+ unsigned long device_type,
+ intel8x0_t ** r_intel8x0)
+{
+ intel8x0_t *chip;
+ int err;
+ unsigned int i;
+ unsigned int int_sta_masks;
+ ichdev_t *ichdev;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_intel8x0_dev_free,
+ };
+
+ static unsigned int bdbars[] = {
+ 3, /* DEVICE_INTEL */
+ 6, /* DEVICE_INTEL_ICH4 */
+ 3, /* DEVICE_SIS */
+ 6, /* DEVICE_ALI */
+ 4, /* DEVICE_NFORCE */
+ };
+ static struct ich_reg_info intel_regs[6] = {
+ { ICH_PIINT, 0 },
+ { ICH_POINT, 0x10 },
+ { ICH_MCINT, 0x20 },
+ { ICH_M2INT, 0x40 },
+ { ICH_P2INT, 0x50 },
+ { ICH_SPINT, 0x60 },
+ };
+ static struct ich_reg_info nforce_regs[4] = {
+ { ICH_PIINT, 0 },
+ { ICH_POINT, 0x10 },
+ { ICH_MCINT, 0x20 },
+ { ICH_NVSPINT, 0x70 },
+ };
+ static struct ich_reg_info ali_regs[6] = {
+ { ALI_INT_PCMIN, 0x40 },
+ { ALI_INT_PCMOUT, 0x50 },
+ { ALI_INT_MICIN, 0x60 },
+ { ALI_INT_CODECSPDIFOUT, 0x70 },
+ { ALI_INT_SPDIFIN, 0xa0 },
+ { ALI_INT_SPDIFOUT, 0xb0 },
+ };
+ struct ich_reg_info *tbl;
+
+ *r_intel8x0 = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&chip->reg_lock);
+ chip->device_type = device_type;
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ if (pci->vendor == PCI_VENDOR_ID_INTEL &&
+ pci->device == PCI_DEVICE_ID_INTEL_440MX)
+ chip->fix_nocache = 1; /* enable workaround */
+
+ /* some Nforce[2] and ICH boards have problems with IRQ handling.
+ * Needs to return IRQ_HANDLED for unknown irqs.
+ */
+ if (device_type == DEVICE_NFORCE)
+ chip->buggy_irq = 1;
+
+ if ((err = pci_request_regions(pci, card->shortname)) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ if (device_type == DEVICE_ALI) {
+ /* ALI5455 has no ac97 region */
+ chip->bmaddr = pci_resource_start(pci, 0);
+ goto port_inited;
+ }
+
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */
+ chip->mmio = 1;
+ chip->addr = pci_resource_start(pci, 2);
+ chip->remap_addr = ioremap_nocache(chip->addr,
+ pci_resource_len(pci, 2));
+ if (chip->remap_addr == NULL) {
+ snd_printk("AC'97 space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
+ }
+ } else {
+ chip->addr = pci_resource_start(pci, 0);
+ }
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */
+ chip->bm_mmio = 1;
+ chip->bmaddr = pci_resource_start(pci, 3);
+ chip->remap_bmaddr = ioremap_nocache(chip->bmaddr,
+ pci_resource_len(pci, 3));
+ if (chip->remap_bmaddr == NULL) {
+ snd_printk("Controller space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
+ }
+ } else {
+ chip->bmaddr = pci_resource_start(pci, 1);
+ }
+
+ port_inited:
+ if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_intel8x0_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
+ synchronize_irq(chip->irq);
+
+ chip->bdbars_count = bdbars[device_type];
+
+ /* initialize offsets */
+ switch (device_type) {
+ case DEVICE_NFORCE:
+ tbl = nforce_regs;
+ break;
+ case DEVICE_ALI:
+ tbl = ali_regs;
+ break;
+ default:
+ tbl = intel_regs;
+ break;
+ }
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev = &chip->ichd[i];
+ ichdev->ichd = i;
+ ichdev->reg_offset = tbl[i].offset;
+ ichdev->int_sta_mask = tbl[i].int_sta_mask;
+ if (device_type == DEVICE_SIS) {
+ /* SiS 7012 swaps the registers */
+ ichdev->roff_sr = ICH_REG_OFF_PICB;
+ ichdev->roff_picb = ICH_REG_OFF_SR;
+ } else {
+ ichdev->roff_sr = ICH_REG_OFF_SR;
+ ichdev->roff_picb = ICH_REG_OFF_PICB;
+ }
+ if (device_type == DEVICE_ALI)
+ ichdev->ali_slot = (ichdev->reg_offset - 0x40) / 0x10;
+ /* SIS7012 handles the pcm data in bytes, others are in samples */
+ ichdev->pos_shift = (device_type == DEVICE_SIS) ? 0 : 1;
+ }
+
+ /* allocate buffer descriptor lists */
+ /* the start of each lists must be aligned to 8 bytes */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
+ &chip->bdbars) < 0) {
+ snd_intel8x0_free(chip);
+ snd_printk(KERN_ERR "intel8x0: cannot allocate buffer descriptors\n");
+ return -ENOMEM;
+ }
+ /* tables must be aligned to 8 bytes here, but the kernel pages
+ are much bigger, so we don't care (on i386) */
+ /* workaround for 440MX */
+ if (chip->fix_nocache)
+ fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1);
+ int_sta_masks = 0;
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev = &chip->ichd[i];
+ ichdev->bdbar = ((u32 *)chip->bdbars.area) + (i * ICH_MAX_FRAGS * 2);
+ ichdev->bdbar_addr = chip->bdbars.addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
+ int_sta_masks |= ichdev->int_sta_mask;
+ }
+ chip->int_sta_reg = device_type == DEVICE_ALI ? ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA;
+ chip->int_sta_mask = int_sta_masks;
+
+ if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
+ snd_intel8x0_free(chip);
+ return err;
+ }
+
+ snd_card_set_pm_callback(card, intel8x0_suspend, intel8x0_resume, chip);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_intel8x0_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_intel8x0 = chip;
+ return 0;
+}
+
+static struct shortname_table {
+ unsigned int id;
+ const char *s;
+} shortnames[] __devinitdata = {
+ { PCI_DEVICE_ID_INTEL_82801, "Intel 82801AA-ICH" },
+ { PCI_DEVICE_ID_INTEL_82901, "Intel 82901AB-ICH0" },
+ { PCI_DEVICE_ID_INTEL_82801BA, "Intel 82801BA-ICH2" },
+ { PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" },
+ { PCI_DEVICE_ID_INTEL_ICH3, "Intel 82801CA-ICH3" },
+ { PCI_DEVICE_ID_INTEL_ICH4, "Intel 82801DB-ICH4" },
+ { PCI_DEVICE_ID_INTEL_ICH5, "Intel ICH5" },
+ { PCI_DEVICE_ID_INTEL_ESB_5, "Intel 6300ESB" },
+ { PCI_DEVICE_ID_INTEL_ICH6_18, "Intel ICH6" },
+ { PCI_DEVICE_ID_INTEL_ICH7_20, "Intel ICH7" },
+ { PCI_DEVICE_ID_SI_7012, "SiS SI7012" },
+ { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" },
+ { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" },
+ { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" },
+ { PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" },
+ { PCI_DEVICE_ID_NVIDIA_CK804_AUDIO, "NVidia CK804" },
+ { PCI_DEVICE_ID_NVIDIA_CK8_AUDIO, "NVidia CK8" },
+ { 0x003a, "NVidia MCP04" },
+ { 0x746d, "AMD AMD8111" },
+ { 0x7445, "AMD AMD768" },
+ { 0x5455, "ALi M5455" },
+ { 0, NULL },
+};
+
+static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ intel8x0_t *chip;
+ int err;
+ struct shortname_table *name;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ switch (pci_id->driver_data) {
+ case DEVICE_NFORCE:
+ strcpy(card->driver, "NFORCE");
+ break;
+ case DEVICE_INTEL_ICH4:
+ strcpy(card->driver, "ICH4");
+ break;
+ default:
+ strcpy(card->driver, "ICH");
+ break;
+ }
+
+ strcpy(card->shortname, "Intel ICH");
+ for (name = shortnames; name->id; name++) {
+ if (pci->device == name->id) {
+ strcpy(card->shortname, name->s);
+ break;
+ }
+ }
+
+ if ((err = snd_intel8x0_create(card, pci, pci_id->driver_data, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (buggy_irq[dev])
+ chip->buggy_irq = 1;
+ if (xbox[dev])
+ chip->xbox = 1;
+
+ if ((err = snd_intel8x0_mixer(chip, ac97_clock[dev], ac97_quirk[dev])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_intel8x0_pcm(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_intel8x0_proc_init(chip);
+
+ snprintf(card->longname, sizeof(card->longname),
+ "%s with %s at %#lx, irq %i", card->shortname,
+ snd_ac97_get_short_name(chip->ac97[0]), chip->addr, chip->irq);
+
+ if (! ac97_clock[dev])
+ intel8x0_measure_ac97_clock(chip);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Intel ICH",
+ .id_table = snd_intel8x0_ids,
+ .probe = snd_intel8x0_probe,
+ .remove = __devexit_p(snd_intel8x0_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+
+static int __init alsa_card_intel8x0_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_intel8x0_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_intel8x0_init)
+module_exit(alsa_card_intel8x0_exit)
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
new file mode 100644
index 00000000000..67da096d659
--- /dev/null
+++ b/sound/pci/intel8x0m.c
@@ -0,0 +1,1462 @@
+/*
+ * ALSA modem driver for Intel ICH (i8x0) chipsets
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This is modified (by Sasha Khapyorsky <sashak@smlink.com>) version
+ * of ALSA ICH sound driver intel8x0.c .
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7013; NVidia MCP/2/2S/3 modems");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"
+ "{Intel,82901AB-ICH0},"
+ "{Intel,82801BA-ICH2},"
+ "{Intel,82801CA-ICH3},"
+ "{Intel,82801DB-ICH4},"
+ "{Intel,ICH5},"
+ "{Intel,ICH6},"
+ "{Intel,ICH7},"
+ "{Intel,MX440},"
+ "{SiS,7013},"
+ "{NVidia,NForce Modem},"
+ "{NVidia,NForce2 Modem},"
+ "{NVidia,NForce2s Modem},"
+ "{NVidia,NForce3 Modem},"
+ "{AMD,AMD768}}");
+
+static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Intel i8x0 modemcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Intel i8x0 modemcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel i8x0 modemcard.");
+module_param_array(ac97_clock, int, NULL, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");
+
+/*
+ * Direct registers
+ */
+
+#ifndef PCI_DEVICE_ID_INTEL_82801_6
+#define PCI_DEVICE_ID_INTEL_82801_6 0x2416
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82901_6
+#define PCI_DEVICE_ID_INTEL_82901_6 0x2426
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BA_6
+#define PCI_DEVICE_ID_INTEL_82801BA_6 0x2446
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_440MX_6
+#define PCI_DEVICE_ID_INTEL_440MX_6 0x7196
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH3_6
+#define PCI_DEVICE_ID_INTEL_ICH3_6 0x2486
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH4_6
+#define PCI_DEVICE_ID_INTEL_ICH4_6 0x24c6
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH5_6
+#define PCI_DEVICE_ID_INTEL_ICH5_6 0x24d6
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH6_6
+#define PCI_DEVICE_ID_INTEL_ICH6_6 0x266d
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH7_6
+#define PCI_DEVICE_ID_INTEL_ICH7_6 0x27dd
+#endif
+#ifndef PCI_DEVICE_ID_SI_7013
+#define PCI_DEVICE_ID_SI_7013 0x7013
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP_MODEM
+#define PCI_DEVICE_ID_NVIDIA_MCP_MODEM 0x01c1
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP2_MODEM
+#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM
+#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_MODEM
+#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9
+#endif
+
+
+enum { DEVICE_INTEL, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };
+
+#define ICHREG(x) ICH_REG_##x
+
+#define DEFINE_REGSET(name,base) \
+enum { \
+ ICH_REG_##name##_BDBAR = base + 0x0, /* dword - buffer descriptor list base address */ \
+ ICH_REG_##name##_CIV = base + 0x04, /* byte - current index value */ \
+ ICH_REG_##name##_LVI = base + 0x05, /* byte - last valid index */ \
+ ICH_REG_##name##_SR = base + 0x06, /* byte - status register */ \
+ ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
+ ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
+ ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
+};
+
+/* busmaster blocks */
+DEFINE_REGSET(OFF, 0); /* offset */
+
+/* values for each busmaster block */
+
+/* LVI */
+#define ICH_REG_LVI_MASK 0x1f
+
+/* SR */
+#define ICH_FIFOE 0x10 /* FIFO error */
+#define ICH_BCIS 0x08 /* buffer completion interrupt status */
+#define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */
+#define ICH_CELV 0x02 /* current equals last valid */
+#define ICH_DCH 0x01 /* DMA controller halted */
+
+/* PIV */
+#define ICH_REG_PIV_MASK 0x1f /* mask */
+
+/* CR */
+#define ICH_IOCE 0x10 /* interrupt on completion enable */
+#define ICH_FEIE 0x08 /* fifo error interrupt enable */
+#define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */
+#define ICH_RESETREGS 0x02 /* reset busmaster registers */
+#define ICH_STARTBM 0x01 /* start busmaster operation */
+
+
+/* global block */
+#define ICH_REG_GLOB_CNT 0x3c /* dword - global control */
+#define ICH_TRIE 0x00000040 /* tertiary resume interrupt enable */
+#define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */
+#define ICH_PRIE 0x00000010 /* primary resume interrupt enable */
+#define ICH_ACLINK 0x00000008 /* AClink shut off */
+#define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */
+#define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */
+#define ICH_GIE 0x00000001 /* GPI interrupt enable */
+#define ICH_REG_GLOB_STA 0x40 /* dword - global status */
+#define ICH_TRI 0x20000000 /* ICH4: tertiary (AC_SDIN2) resume interrupt */
+#define ICH_TCR 0x10000000 /* ICH4: tertiary (AC_SDIN2) codec ready */
+#define ICH_BCS 0x08000000 /* ICH4: bit clock stopped */
+#define ICH_SPINT 0x04000000 /* ICH4: S/PDIF interrupt */
+#define ICH_P2INT 0x02000000 /* ICH4: PCM2-In interrupt */
+#define ICH_M2INT 0x01000000 /* ICH4: Mic2-In interrupt */
+#define ICH_SAMPLE_CAP 0x00c00000 /* ICH4: sample capability bits (RO) */
+#define ICH_MULTICHAN_CAP 0x00300000 /* ICH4: multi-channel capability bits (RO) */
+#define ICH_MD3 0x00020000 /* modem power down semaphore */
+#define ICH_AD3 0x00010000 /* audio power down semaphore */
+#define ICH_RCS 0x00008000 /* read completion status */
+#define ICH_BIT3 0x00004000 /* bit 3 slot 12 */
+#define ICH_BIT2 0x00002000 /* bit 2 slot 12 */
+#define ICH_BIT1 0x00001000 /* bit 1 slot 12 */
+#define ICH_SRI 0x00000800 /* secondary (AC_SDIN1) resume interrupt */
+#define ICH_PRI 0x00000400 /* primary (AC_SDIN0) resume interrupt */
+#define ICH_SCR 0x00000200 /* secondary (AC_SDIN1) codec ready */
+#define ICH_PCR 0x00000100 /* primary (AC_SDIN0) codec ready */
+#define ICH_MCINT 0x00000080 /* MIC capture interrupt */
+#define ICH_POINT 0x00000040 /* playback interrupt */
+#define ICH_PIINT 0x00000020 /* capture interrupt */
+#define ICH_NVSPINT 0x00000010 /* nforce spdif interrupt */
+#define ICH_MOINT 0x00000004 /* modem playback interrupt */
+#define ICH_MIINT 0x00000002 /* modem capture interrupt */
+#define ICH_GSCI 0x00000001 /* GPI status change interrupt */
+#define ICH_REG_ACC_SEMA 0x44 /* byte - codec write semaphore */
+#define ICH_CAS 0x01 /* codec access semaphore */
+
+#define ICH_MAX_FRAGS 32 /* max hw frags */
+
+
+/*
+ *
+ */
+
+enum { ICHD_MDMIN, ICHD_MDMOUT, ICHD_MDMLAST = ICHD_MDMOUT };
+enum { ALID_MDMIN, ALID_MDMOUT, ALID_MDMLAST = ALID_MDMOUT };
+
+#define get_ichdev(substream) (ichdev_t *)(substream->runtime->private_data)
+
+typedef struct {
+ unsigned int ichd; /* ich device number */
+ unsigned long reg_offset; /* offset to bmaddr */
+ u32 *bdbar; /* CPU address (32bit) */
+ unsigned int bdbar_addr; /* PCI bus address (32bit) */
+ snd_pcm_substream_t *substream;
+ unsigned int physbuf; /* physical address (32bit) */
+ unsigned int size;
+ unsigned int fragsize;
+ unsigned int fragsize1;
+ unsigned int position;
+ int frags;
+ int lvi;
+ int lvi_frag;
+ int civ;
+ int ack;
+ int ack_reload;
+ unsigned int ack_bit;
+ unsigned int roff_sr;
+ unsigned int roff_picb;
+ unsigned int int_sta_mask; /* interrupt status mask */
+ unsigned int ali_slot; /* ALI DMA slot */
+ ac97_t *ac97;
+} ichdev_t;
+
+typedef struct _snd_intel8x0m intel8x0_t;
+
+struct _snd_intel8x0m {
+ unsigned int device_type;
+
+ int irq;
+
+ unsigned int mmio;
+ unsigned long addr;
+ void __iomem *remap_addr;
+ unsigned int bm_mmio;
+ unsigned long bmaddr;
+ void __iomem *remap_bmaddr;
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+
+ int pcm_devs;
+ snd_pcm_t *pcm[2];
+ ichdev_t ichd[2];
+
+ unsigned int in_ac97_init: 1;
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97;
+
+ spinlock_t reg_lock;
+
+ struct snd_dma_buffer bdbars;
+ u32 bdbars_count;
+ u32 int_sta_reg; /* interrupt status register */
+ u32 int_sta_mask; /* interrupt status mask */
+ unsigned int pcm_pos_shift;
+};
+
+static struct pci_device_id snd_intel8x0m_ids[] = {
+ { 0x8086, 0x2416, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */
+ { 0x8086, 0x2426, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */
+ { 0x8086, 0x2446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */
+ { 0x8086, 0x2486, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */
+ { 0x8086, 0x24c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH4 */
+ { 0x8086, 0x24d6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH5 */
+ { 0x8086, 0x266d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH6 */
+ { 0x8086, 0x27dd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH7 */
+ { 0x8086, 0x7196, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */
+ { 0x1022, 0x7446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */
+ { 0x1039, 0x7013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7013 */
+ { 0x10de, 0x01c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */
+ { 0x10de, 0x0069, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */
+ { 0x10de, 0x0089, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2s */
+ { 0x10de, 0x00d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */
+#if 0
+ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */
+ { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */
+#endif
+ { 0, }
+};
+static int snd_intel8x0m_switch_default_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol);
+static int snd_intel8x0m_switch_default_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol);
+static int snd_intel8x0m_switch_default_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo);
+
+#define PRIVATE_VALUE_INITIALIZER(r,m) (((r) & 0xffff) << 16 | ((m) & 0xffff))
+#define PRIVATE_VALUE_MASK(control) ((control)->private_value & 0xffff)
+#define PRIVATE_VALUE_REG(control) (((control)->private_value >> 16) & 0xffff)
+
+static snd_kcontrol_new_t snd_intel8x0m_mixer_switches[] __devinitdata = {
+ { .name = "Off-hook Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_intel8x0m_switch_default_info,
+ .get = snd_intel8x0m_switch_default_get,
+ .put = snd_intel8x0m_switch_default_put,
+ .private_value = PRIVATE_VALUE_INITIALIZER(AC97_GPIO_STATUS,AC97_GPIO_LINE1_OH)
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_intel8x0m_ids);
+
+static int snd_intel8x0m_switch_default_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_intel8x0m_switch_default_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ unsigned short mask = PRIVATE_VALUE_MASK(kcontrol);
+ unsigned short reg = PRIVATE_VALUE_REG(kcontrol);
+ intel8x0_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int status;
+ status = snd_ac97_read(chip->ac97, reg) & mask ? 1 : 0;
+ ucontrol->value.integer.value[0] = status;
+ return 0;
+}
+static int snd_intel8x0m_switch_default_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ unsigned short mask = PRIVATE_VALUE_MASK(kcontrol);
+ unsigned short reg = PRIVATE_VALUE_REG(kcontrol);
+ intel8x0_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short new_status = ucontrol->value.integer.value[0] ? mask : ~mask;
+ return snd_ac97_update_bits(chip->ac97, reg,
+ mask, new_status);
+}
+/*
+ * Lowlevel I/O - busmaster
+ */
+
+static u8 igetbyte(intel8x0_t *chip, u32 offset)
+{
+ if (chip->bm_mmio)
+ return readb(chip->remap_bmaddr + offset);
+ else
+ return inb(chip->bmaddr + offset);
+}
+
+static u16 igetword(intel8x0_t *chip, u32 offset)
+{
+ if (chip->bm_mmio)
+ return readw(chip->remap_bmaddr + offset);
+ else
+ return inw(chip->bmaddr + offset);
+}
+
+static u32 igetdword(intel8x0_t *chip, u32 offset)
+{
+ if (chip->bm_mmio)
+ return readl(chip->remap_bmaddr + offset);
+ else
+ return inl(chip->bmaddr + offset);
+}
+
+static void iputbyte(intel8x0_t *chip, u32 offset, u8 val)
+{
+ if (chip->bm_mmio)
+ writeb(val, chip->remap_bmaddr + offset);
+ else
+ outb(val, chip->bmaddr + offset);
+}
+
+static void iputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+ if (chip->bm_mmio)
+ writew(val, chip->remap_bmaddr + offset);
+ else
+ outw(val, chip->bmaddr + offset);
+}
+
+static void iputdword(intel8x0_t *chip, u32 offset, u32 val)
+{
+ if (chip->bm_mmio)
+ writel(val, chip->remap_bmaddr + offset);
+ else
+ outl(val, chip->bmaddr + offset);
+}
+
+/*
+ * Lowlevel I/O - AC'97 registers
+ */
+
+static u16 iagetword(intel8x0_t *chip, u32 offset)
+{
+ if (chip->mmio)
+ return readw(chip->remap_addr + offset);
+ else
+ return inw(chip->addr + offset);
+}
+
+static void iaputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+ if (chip->mmio)
+ writew(val, chip->remap_addr + offset);
+ else
+ outw(val, chip->addr + offset);
+}
+
+/*
+ * Basic I/O
+ */
+
+/*
+ * access to AC97 codec via normal i/o (for ICH and SIS7013)
+ */
+
+/* return the GLOB_STA bit for the corresponding codec */
+static unsigned int get_ich_codec_bit(intel8x0_t *chip, unsigned int codec)
+{
+ static unsigned int codec_bit[3] = {
+ ICH_PCR, ICH_SCR, ICH_TCR
+ };
+ snd_assert(codec < 3, return ICH_PCR);
+ return codec_bit[codec];
+}
+
+static int snd_intel8x0m_codec_semaphore(intel8x0_t *chip, unsigned int codec)
+{
+ int time;
+
+ if (codec > 1)
+ return -EIO;
+ codec = get_ich_codec_bit(chip, codec);
+
+ /* codec ready ? */
+ if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0)
+ return -EIO;
+
+ /* Anyone holding a semaphore for 1 msec should be shot... */
+ time = 100;
+ do {
+ if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS))
+ return 0;
+ udelay(10);
+ } while (time--);
+
+ /* access to some forbidden (non existant) ac97 registers will not
+ * reset the semaphore. So even if you don't get the semaphore, still
+ * continue the access. We don't need the semaphore anyway. */
+ snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+ igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
+ iagetword(chip, 0); /* clear semaphore flag */
+ /* I don't care about the semaphore */
+ return -EBUSY;
+}
+
+static void snd_intel8x0_codec_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ intel8x0_t *chip = ac97->private_data;
+
+ if (snd_intel8x0m_codec_semaphore(chip, ac97->num) < 0) {
+ if (! chip->in_ac97_init)
+ snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ }
+ iaputword(chip, reg + ac97->num * 0x80, val);
+}
+
+static unsigned short snd_intel8x0_codec_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ intel8x0_t *chip = ac97->private_data;
+ unsigned short res;
+ unsigned int tmp;
+
+ if (snd_intel8x0m_codec_semaphore(chip, ac97->num) < 0) {
+ if (! chip->in_ac97_init)
+ snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ res = 0xffff;
+ } else {
+ res = iagetword(chip, reg + ac97->num * 0x80);
+ if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ /* reset RCS and preserve other R/WC bits */
+ iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
+ if (! chip->in_ac97_init)
+ snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+ res = 0xffff;
+ }
+ }
+ return res;
+}
+
+
+/*
+ * DMA I/O
+ */
+static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev)
+{
+ int idx;
+ u32 *bdbar = ichdev->bdbar;
+ unsigned long port = ichdev->reg_offset;
+
+ iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
+ if (ichdev->size == ichdev->fragsize) {
+ ichdev->ack_reload = ichdev->ack = 2;
+ ichdev->fragsize1 = ichdev->fragsize >> 1;
+ for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) {
+ bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf);
+ bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+ ichdev->fragsize1 >> chip->pcm_pos_shift);
+ bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1));
+ bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+ ichdev->fragsize1 >> chip->pcm_pos_shift);
+ }
+ ichdev->frags = 2;
+ } else {
+ ichdev->ack_reload = ichdev->ack = 1;
+ ichdev->fragsize1 = ichdev->fragsize;
+ for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) {
+ bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size));
+ bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+ ichdev->fragsize >> chip->pcm_pos_shift);
+ // printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
+ }
+ ichdev->frags = ichdev->size / ichdev->fragsize;
+ }
+ iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
+ ichdev->civ = 0;
+ iputbyte(chip, port + ICH_REG_OFF_CIV, 0);
+ ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
+ ichdev->position = 0;
+#if 0
+ printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
+ ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
+#endif
+ /* clear interrupts */
+ iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
+}
+
+/*
+ * Interrupt handler
+ */
+
+static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev)
+{
+ unsigned long port = ichdev->reg_offset;
+ int civ, i, step;
+ int ack = 0;
+
+ civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
+ if (civ == ichdev->civ) {
+ // snd_printd("civ same %d\n", civ);
+ step = 1;
+ ichdev->civ++;
+ ichdev->civ &= ICH_REG_LVI_MASK;
+ } else {
+ step = civ - ichdev->civ;
+ if (step < 0)
+ step += ICH_REG_LVI_MASK + 1;
+ // if (step != 1)
+ // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
+ ichdev->civ = civ;
+ }
+
+ ichdev->position += step * ichdev->fragsize1;
+ ichdev->position %= ichdev->size;
+ ichdev->lvi += step;
+ ichdev->lvi &= ICH_REG_LVI_MASK;
+ iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
+ for (i = 0; i < step; i++) {
+ ichdev->lvi_frag++;
+ ichdev->lvi_frag %= ichdev->frags;
+ ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
+ // printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));
+ if (--ichdev->ack == 0) {
+ ichdev->ack = ichdev->ack_reload;
+ ack = 1;
+ }
+ }
+ if (ack && ichdev->substream) {
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(ichdev->substream);
+ spin_lock(&chip->reg_lock);
+ }
+ iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
+}
+
+static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ intel8x0_t *chip = dev_id;
+ ichdev_t *ichdev;
+ unsigned int status;
+ unsigned int i;
+
+ spin_lock(&chip->reg_lock);
+ status = igetdword(chip, chip->int_sta_reg);
+ if (status == 0xffffffff) { /* we are not yet resumed */
+ spin_unlock(&chip->reg_lock);
+ return IRQ_NONE;
+ }
+ if ((status & chip->int_sta_mask) == 0) {
+ if (status)
+ iputdword(chip, chip->int_sta_reg, status);
+ spin_unlock(&chip->reg_lock);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev = &chip->ichd[i];
+ if (status & ichdev->int_sta_mask)
+ snd_intel8x0_update(chip, ichdev);
+ }
+
+ /* ack them */
+ iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
+ spin_unlock(&chip->reg_lock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * PCM part
+ */
+
+static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ ichdev_t *ichdev = get_ichdev(substream);
+ unsigned char val = 0;
+ unsigned long port = ichdev->reg_offset;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ val = ICH_IOCE | ICH_STARTBM;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ val = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = ICH_IOCE;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = ICH_IOCE | ICH_STARTBM;
+ break;
+ default:
+ return -EINVAL;
+ }
+ iputbyte(chip, port + ICH_REG_OFF_CR, val);
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ /* wait until DMA stopped */
+ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ;
+ /* reset whole DMA things */
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+ }
+ return 0;
+}
+
+static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ ichdev_t *ichdev = get_ichdev(substream);
+ size_t ptr1, ptr;
+
+ ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << chip->pcm_pos_shift;
+ if (ptr1 != 0)
+ ptr = ichdev->fragsize1 - ptr1;
+ else
+ ptr = 0;
+ ptr += ichdev->position;
+ if (ptr >= ichdev->size)
+ return 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static int snd_intel8x0m_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ /* hook off/on on start/stop */
+ /* Moved this to mixer control */
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return snd_intel8x0_pcm_trigger(substream,cmd);
+}
+
+static int snd_intel8x0m_pcm_prepare(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ichdev_t *ichdev = get_ichdev(substream);
+
+ ichdev->physbuf = runtime->dma_addr;
+ ichdev->size = snd_pcm_lib_buffer_bytes(substream);
+ ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
+ snd_ac97_write(ichdev->ac97, AC97_LINE1_RATE, runtime->rate);
+ snd_ac97_write(ichdev->ac97, AC97_LINE1_LEVEL, 0);
+ snd_intel8x0_setup_periods(chip, ichdev);
+ return 0;
+}
+
+static snd_pcm_hardware_t snd_intel8x0m_stream =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 64 * 1024,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+
+static int snd_intel8x0m_pcm_open(snd_pcm_substream_t * substream, ichdev_t *ichdev)
+{
+ static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+
+ ichdev->substream = substream;
+ runtime->hw = snd_intel8x0m_stream;
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+ if ( err < 0 )
+ return err;
+ runtime->private_data = ichdev;
+ return 0;
+}
+
+static int snd_intel8x0m_playback_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0m_pcm_open(substream, &chip->ichd[ICHD_MDMOUT]);
+}
+
+static int snd_intel8x0m_playback_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ICHD_MDMOUT].substream = NULL;
+ return 0;
+}
+
+static int snd_intel8x0m_capture_open(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ return snd_intel8x0m_pcm_open(substream, &chip->ichd[ICHD_MDMIN]);
+}
+
+static int snd_intel8x0m_capture_close(snd_pcm_substream_t * substream)
+{
+ intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->ichd[ICHD_MDMIN].substream = NULL;
+ return 0;
+}
+
+
+static snd_pcm_ops_t snd_intel8x0m_playback_ops = {
+ .open = snd_intel8x0m_playback_open,
+ .close = snd_intel8x0m_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0m_pcm_prepare,
+ .trigger = snd_intel8x0m_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0m_capture_ops = {
+ .open = snd_intel8x0m_capture_open,
+ .close = snd_intel8x0m_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intel8x0_hw_params,
+ .hw_free = snd_intel8x0_hw_free,
+ .prepare = snd_intel8x0m_pcm_prepare,
+ .trigger = snd_intel8x0m_pcm_trigger,
+ .pointer = snd_intel8x0_pcm_pointer,
+};
+
+
+struct ich_pcm_table {
+ char *suffix;
+ snd_pcm_ops_t *playback_ops;
+ snd_pcm_ops_t *capture_ops;
+ size_t prealloc_size;
+ size_t prealloc_max_size;
+ int ac97_idx;
+};
+
+static int __devinit snd_intel8x0_pcm1(intel8x0_t *chip, int device, struct ich_pcm_table *rec)
+{
+ snd_pcm_t *pcm;
+ int err;
+ char name[32];
+
+ if (rec->suffix)
+ sprintf(name, "Intel ICH - %s", rec->suffix);
+ else
+ strcpy(name, "Intel ICH");
+ err = snd_pcm_new(chip->card, name, device,
+ rec->playback_ops ? 1 : 0,
+ rec->capture_ops ? 1 : 0, &pcm);
+ if (err < 0)
+ return err;
+
+ if (rec->playback_ops)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, rec->playback_ops);
+ if (rec->capture_ops)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, rec->capture_ops);
+
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ if (rec->suffix)
+ sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
+ else
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcm[device] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ rec->prealloc_size,
+ rec->prealloc_max_size);
+
+ return 0;
+}
+
+static struct ich_pcm_table intel_pcms[] __devinitdata = {
+ {
+ .suffix = "Modem",
+ .playback_ops = &snd_intel8x0m_playback_ops,
+ .capture_ops = &snd_intel8x0m_capture_ops,
+ .prealloc_size = 32 * 1024,
+ .prealloc_max_size = 64 * 1024,
+ },
+};
+
+static int __devinit snd_intel8x0_pcm(intel8x0_t *chip)
+{
+ int i, tblsize, device, err;
+ struct ich_pcm_table *tbl, *rec;
+
+#if 1
+ tbl = intel_pcms;
+ tblsize = 1;
+#else
+ switch (chip->device_type) {
+ case DEVICE_NFORCE:
+ tbl = nforce_pcms;
+ tblsize = ARRAY_SIZE(nforce_pcms);
+ break;
+ case DEVICE_ALI:
+ tbl = ali_pcms;
+ tblsize = ARRAY_SIZE(ali_pcms);
+ break;
+ default:
+ tbl = intel_pcms;
+ tblsize = 2;
+ break;
+ }
+#endif
+ device = 0;
+ for (i = 0; i < tblsize; i++) {
+ rec = tbl + i;
+ if (i > 0 && rec->ac97_idx) {
+ /* activate PCM only when associated AC'97 codec */
+ if (! chip->ichd[rec->ac97_idx].ac97)
+ continue;
+ }
+ err = snd_intel8x0_pcm1(chip, device, rec);
+ if (err < 0)
+ return err;
+ device++;
+ }
+
+ chip->pcm_devs = device;
+ return 0;
+}
+
+
+/*
+ * Mixer part
+ */
+
+static void snd_intel8x0_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ intel8x0_t *chip = bus->private_data;
+ chip->ac97_bus = NULL;
+}
+
+static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97)
+{
+ intel8x0_t *chip = ac97->private_data;
+ chip->ac97 = NULL;
+}
+
+
+static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ ac97_t *x97;
+ int err;
+ unsigned int glob_sta = 0;
+ unsigned int idx;
+ static ac97_bus_ops_t ops = {
+ .write = snd_intel8x0_codec_write,
+ .read = snd_intel8x0_codec_read,
+ };
+
+ chip->in_ac97_init = 1;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_intel8x0_mixer_free_ac97;
+ ac97.scaps = AC97_SCAP_SKIP_AUDIO;
+
+ glob_sta = igetdword(chip, ICHREG(GLOB_STA));
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ goto __err;
+ pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
+ pbus->shared_type = AC97_SHARED_TYPE_ICH; /* shared with audio driver */
+ if (ac97_clock >= 8000 && ac97_clock <= 48000)
+ pbus->clock = ac97_clock;
+ chip->ac97_bus = pbus;
+
+ ac97.pci = chip->pci;
+ ac97.num = glob_sta & ICH_SCR ? 1 : 0;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &x97)) < 0) {
+ snd_printk(KERN_ERR "Unable to initialize codec #%d\n", ac97.num);
+ if (ac97.num == 0)
+ goto __err;
+ return err;
+ }
+ chip->ac97 = x97;
+ if(ac97_is_modem(x97) && !chip->ichd[ICHD_MDMIN].ac97) {
+ chip->ichd[ICHD_MDMIN].ac97 = x97;
+ chip->ichd[ICHD_MDMOUT].ac97 = x97;
+ }
+ for (idx = 0; idx < ARRAY_SIZE(snd_intel8x0m_mixer_switches); idx++) {
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_intel8x0m_mixer_switches[idx], chip))) < 0)
+ goto __err;
+ }
+
+ chip->in_ac97_init = 0;
+ return 0;
+
+ __err:
+ /* clear the cold-reset bit for the next chance */
+ if (chip->device_type != DEVICE_ALI)
+ iputdword(chip, ICHREG(GLOB_CNT), igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_AC97COLD);
+ return err;
+}
+
+
+/*
+ *
+ */
+
+#define do_delay(chip) do {\
+ set_current_state(TASK_UNINTERRUPTIBLE);\
+ schedule_timeout(1);\
+} while (0)
+
+static int snd_intel8x0m_ich_chip_init(intel8x0_t *chip, int probing)
+{
+ unsigned long end_time;
+ unsigned int cnt, status, nstatus;
+
+ /* put logic to right state */
+ /* first clear status bits */
+ status = ICH_RCS | ICH_MIINT | ICH_MOINT;
+ cnt = igetdword(chip, ICHREG(GLOB_STA));
+ iputdword(chip, ICHREG(GLOB_STA), cnt & status);
+
+ /* ACLink on, 2 channels */
+ cnt = igetdword(chip, ICHREG(GLOB_CNT));
+ cnt &= ~(ICH_ACLINK);
+ /* finish cold or do warm reset */
+ cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
+ iputdword(chip, ICHREG(GLOB_CNT), cnt);
+ end_time = (jiffies + (HZ / 4)) + 1;
+ do {
+ if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
+ goto __ok;
+ do_delay(chip);
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk("AC'97 warm reset still in progress? [0x%x]\n", igetdword(chip, ICHREG(GLOB_CNT)));
+ return -EIO;
+
+ __ok:
+ if (probing) {
+ /* wait for any codec ready status.
+ * Once it becomes ready it should remain ready
+ * as long as we do not disable the ac97 link.
+ */
+ end_time = jiffies + HZ;
+ do {
+ status = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+ if (status)
+ break;
+ do_delay(chip);
+ } while (time_after_eq(end_time, jiffies));
+ if (! status) {
+ /* no codec is found */
+ snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n", igetdword(chip, ICHREG(GLOB_STA)));
+ return -EIO;
+ }
+
+ /* up to two codecs (modem cannot be tertiary with ICH4) */
+ nstatus = ICH_PCR | ICH_SCR;
+
+ /* wait for other codecs ready status. */
+ end_time = jiffies + HZ / 4;
+ while (status != nstatus && time_after_eq(end_time, jiffies)) {
+ do_delay(chip);
+ status |= igetdword(chip, ICHREG(GLOB_STA)) & nstatus;
+ }
+
+ } else {
+ /* resume phase */
+ status = 0;
+ if (chip->ac97)
+ status |= get_ich_codec_bit(chip, chip->ac97->num);
+ /* wait until all the probed codecs are ready */
+ end_time = jiffies + HZ;
+ do {
+ nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+ if (status == nstatus)
+ break;
+ do_delay(chip);
+ } while (time_after_eq(end_time, jiffies));
+ }
+
+ if (chip->device_type == DEVICE_SIS) {
+ /* unmute the output on SIS7012 */
+ iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
+ }
+
+ return 0;
+}
+
+static int snd_intel8x0_chip_init(intel8x0_t *chip, int probing)
+{
+ unsigned int i;
+ int err;
+
+ if ((err = snd_intel8x0m_ich_chip_init(chip, probing)) < 0)
+ return err;
+ iagetword(chip, 0); /* clear semaphore flag */
+
+ /* disable interrupts */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
+ /* reset channels */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
+ /* initialize Buffer Descriptor Lists */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr);
+ return 0;
+}
+
+static int snd_intel8x0_free(intel8x0_t *chip)
+{
+ unsigned int i;
+
+ if (chip->irq < 0)
+ goto __hw_end;
+ /* disable interrupts */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
+ /* reset channels */
+ for (i = 0; i < chip->bdbars_count; i++)
+ iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
+ /* --- */
+ synchronize_irq(chip->irq);
+ __hw_end:
+ if (chip->bdbars.area)
+ snd_dma_free_pages(&chip->bdbars);
+ if (chip->remap_addr)
+ iounmap(chip->remap_addr);
+ if (chip->remap_bmaddr)
+ iounmap(chip->remap_bmaddr);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int intel8x0m_suspend(snd_card_t *card, pm_message_t state)
+{
+ intel8x0_t *chip = card->pm_private_data;
+ int i;
+
+ for (i = 0; i < chip->pcm_devs; i++)
+ snd_pcm_suspend_all(chip->pcm[i]);
+ if (chip->ac97)
+ snd_ac97_suspend(chip->ac97);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int intel8x0m_resume(snd_card_t *card)
+{
+ intel8x0_t *chip = card->pm_private_data;
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+ snd_intel8x0_chip_init(chip, 0);
+ if (chip->ac97)
+ snd_ac97_resume(chip->ac97);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static void snd_intel8x0m_proc_read(snd_info_entry_t * entry,
+ snd_info_buffer_t * buffer)
+{
+ intel8x0_t *chip = entry->private_data;
+ unsigned int tmp;
+
+ snd_iprintf(buffer, "Intel8x0m\n\n");
+ if (chip->device_type == DEVICE_ALI)
+ return;
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ snd_iprintf(buffer, "Global control : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT)));
+ snd_iprintf(buffer, "Global status : 0x%08x\n", tmp);
+ snd_iprintf(buffer, "AC'97 codecs ready :%s%s%s%s\n",
+ tmp & ICH_PCR ? " primary" : "",
+ tmp & ICH_SCR ? " secondary" : "",
+ tmp & ICH_TCR ? " tertiary" : "",
+ (tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : "");
+}
+
+static void __devinit snd_intel8x0m_proc_init(intel8x0_t * chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(chip->card, "intel8x0m", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0m_proc_read);
+}
+
+static int snd_intel8x0_dev_free(snd_device_t *device)
+{
+ intel8x0_t *chip = device->device_data;
+ return snd_intel8x0_free(chip);
+}
+
+struct ich_reg_info {
+ unsigned int int_sta_mask;
+ unsigned int offset;
+};
+
+static int __devinit snd_intel8x0m_create(snd_card_t * card,
+ struct pci_dev *pci,
+ unsigned long device_type,
+ intel8x0_t ** r_intel8x0)
+{
+ intel8x0_t *chip;
+ int err;
+ unsigned int i;
+ unsigned int int_sta_masks;
+ ichdev_t *ichdev;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_intel8x0_dev_free,
+ };
+ static struct ich_reg_info intel_regs[2] = {
+ { ICH_MIINT, 0 },
+ { ICH_MOINT, 0x10 },
+ };
+ struct ich_reg_info *tbl;
+
+ *r_intel8x0 = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&chip->reg_lock);
+ chip->device_type = device_type;
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ if ((err = pci_request_regions(pci, card->shortname)) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ if (device_type == DEVICE_ALI) {
+ /* ALI5455 has no ac97 region */
+ chip->bmaddr = pci_resource_start(pci, 0);
+ goto port_inited;
+ }
+
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */
+ chip->mmio = 1;
+ chip->addr = pci_resource_start(pci, 2);
+ chip->remap_addr = ioremap_nocache(chip->addr,
+ pci_resource_len(pci, 2));
+ if (chip->remap_addr == NULL) {
+ snd_printk("AC'97 space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
+ }
+ } else {
+ chip->addr = pci_resource_start(pci, 0);
+ }
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */
+ chip->bm_mmio = 1;
+ chip->bmaddr = pci_resource_start(pci, 3);
+ chip->remap_bmaddr = ioremap_nocache(chip->bmaddr,
+ pci_resource_len(pci, 3));
+ if (chip->remap_bmaddr == NULL) {
+ snd_printk("Controller space ioremap problem\n");
+ snd_intel8x0_free(chip);
+ return -EIO;
+ }
+ } else {
+ chip->bmaddr = pci_resource_start(pci, 1);
+ }
+
+ port_inited:
+ if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_intel8x0_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
+ synchronize_irq(chip->irq);
+
+ /* initialize offsets */
+ chip->bdbars_count = 2;
+ tbl = intel_regs;
+
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev = &chip->ichd[i];
+ ichdev->ichd = i;
+ ichdev->reg_offset = tbl[i].offset;
+ ichdev->int_sta_mask = tbl[i].int_sta_mask;
+ if (device_type == DEVICE_SIS) {
+ /* SiS 7013 swaps the registers */
+ ichdev->roff_sr = ICH_REG_OFF_PICB;
+ ichdev->roff_picb = ICH_REG_OFF_SR;
+ } else {
+ ichdev->roff_sr = ICH_REG_OFF_SR;
+ ichdev->roff_picb = ICH_REG_OFF_PICB;
+ }
+ if (device_type == DEVICE_ALI)
+ ichdev->ali_slot = (ichdev->reg_offset - 0x40) / 0x10;
+ }
+ /* SIS7013 handles the pcm data in bytes, others are in words */
+ chip->pcm_pos_shift = (device_type == DEVICE_SIS) ? 0 : 1;
+
+ /* allocate buffer descriptor lists */
+ /* the start of each lists must be aligned to 8 bytes */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
+ &chip->bdbars) < 0) {
+ snd_intel8x0_free(chip);
+ return -ENOMEM;
+ }
+ /* tables must be aligned to 8 bytes here, but the kernel pages
+ are much bigger, so we don't care (on i386) */
+ int_sta_masks = 0;
+ for (i = 0; i < chip->bdbars_count; i++) {
+ ichdev = &chip->ichd[i];
+ ichdev->bdbar = ((u32 *)chip->bdbars.area) + (i * ICH_MAX_FRAGS * 2);
+ ichdev->bdbar_addr = chip->bdbars.addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
+ int_sta_masks |= ichdev->int_sta_mask;
+ }
+ chip->int_sta_reg = ICH_REG_GLOB_STA;
+ chip->int_sta_mask = int_sta_masks;
+
+ if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
+ snd_intel8x0_free(chip);
+ return err;
+ }
+
+ snd_card_set_pm_callback(card, intel8x0m_suspend, intel8x0m_resume, chip);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_intel8x0_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_intel8x0 = chip;
+ return 0;
+}
+
+static struct shortname_table {
+ unsigned int id;
+ const char *s;
+} shortnames[] __devinitdata = {
+ { PCI_DEVICE_ID_INTEL_82801_6, "Intel 82801AA-ICH" },
+ { PCI_DEVICE_ID_INTEL_82901_6, "Intel 82901AB-ICH0" },
+ { PCI_DEVICE_ID_INTEL_82801BA_6, "Intel 82801BA-ICH2" },
+ { PCI_DEVICE_ID_INTEL_440MX_6, "Intel 440MX" },
+ { PCI_DEVICE_ID_INTEL_ICH3_6, "Intel 82801CA-ICH3" },
+ { PCI_DEVICE_ID_INTEL_ICH4_6, "Intel 82801DB-ICH4" },
+ { PCI_DEVICE_ID_INTEL_ICH5_6, "Intel ICH5" },
+ { PCI_DEVICE_ID_INTEL_ICH6_6, "Intel ICH6" },
+ { PCI_DEVICE_ID_INTEL_ICH7_6, "Intel ICH7" },
+ { 0x7446, "AMD AMD768" },
+ { PCI_DEVICE_ID_SI_7013, "SiS SI7013" },
+ { PCI_DEVICE_ID_NVIDIA_MCP_MODEM, "NVidia nForce" },
+ { PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
+ { PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
+ { PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
+#if 0
+ { 0x5455, "ALi M5455" },
+ { 0x746d, "AMD AMD8111" },
+#endif
+ { 0 },
+};
+
+static int __devinit snd_intel8x0m_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ intel8x0_t *chip;
+ int err;
+ struct shortname_table *name;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ strcpy(card->driver, "ICH-MODEM");
+ strcpy(card->shortname, "Intel ICH");
+ for (name = shortnames; name->id; name++) {
+ if (pci->device == name->id) {
+ strcpy(card->shortname, name->s);
+ break;
+ }
+ }
+ strcat(card->shortname," Modem");
+
+ if ((err = snd_intel8x0m_create(card, pci, pci_id->driver_data, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_intel8x0_mixer(chip, ac97_clock[dev])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_intel8x0_pcm(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_intel8x0m_proc_init(chip);
+
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, chip->addr, chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_intel8x0m_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Intel ICH Modem",
+ .id_table = snd_intel8x0m_ids,
+ .probe = snd_intel8x0m_probe,
+ .remove = __devexit_p(snd_intel8x0m_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+
+static int __init alsa_card_intel8x0m_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_intel8x0m_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_intel8x0m_init)
+module_exit(alsa_card_intel8x0m_exit)
diff --git a/sound/pci/korg1212/Makefile b/sound/pci/korg1212/Makefile
new file mode 100644
index 00000000000..78c9dc6eeb2
--- /dev/null
+++ b/sound/pci/korg1212/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-korg1212-objs := korg1212.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o
diff --git a/sound/pci/korg1212/korg1212-firmware.h b/sound/pci/korg1212/korg1212-firmware.h
new file mode 100644
index 00000000000..f6f5b91806a
--- /dev/null
+++ b/sound/pci/korg1212/korg1212-firmware.h
@@ -0,0 +1,987 @@
+static char dspCode [] = {
+0x01,0xff,0x18,0xff,0xf5,0xff,0xcf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x26,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x38,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,0x67,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x0c,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
+0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x72,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x02,0xff,0x91,0xff,0xff,0xff,0x80,0xff,
+0x02,0xff,0x91,0xff,0xff,0xff,0x90,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0xff,0xff,0x47,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x17,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x17,0xff,
+0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
+0x02,0xff,0x34,0xff,0xff,0xff,0x4a,0xff,0x02,0xff,0x38,0xff,0xff,0xff,0x4a,0xff,
+0x01,0xff,0x34,0xff,0xff,0xff,0x2b,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x2b,0xff,
+0x80,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x81,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
+0x84,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x85,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x82,0xff,0x37,0xff,0xff,0xff,0x81,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x88,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8c,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8a,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8e,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0x80,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x07,0xff,
+0x40,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,
+0x02,0xff,0x41,0xff,0xff,0xff,0x82,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xcb,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0xe2,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xdb,0xff,
+0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0x82,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0x9b,0xff,0x03,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xa2,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x21,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x40,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x85,0xff,0x0a,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,
+0x0a,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
+0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x0b,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
+0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x03,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
+0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x5b,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0xdf,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
+0xfe,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0xc1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x04,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x23,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x59,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x0c,0xff,0x14,0xff,0xff,0xff,0xe4,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x24,0xff,
+0x00,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x76,0xff,0x1c,0xff,0xff,0xff,0x9f,0xff,
+0x86,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0x64,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0x81,0xff,
+0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x61,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,0x77,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,
+0x63,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
+0x0f,0xff,0x14,0xff,0xff,0xff,0x6e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0xe0,0xff,
+0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
+0x10,0xff,0x14,0xff,0xff,0xff,0x0e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x79,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x80,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x11,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,
+0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x63,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x83,0xff,0x37,0xff,0xff,0xff,0x80,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x83,0xff,0x43,0xff,0xff,0xff,0x00,0xff,
+0x87,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x40,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x90,0xff,
+0x80,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x13,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x43,0xff,0xff,0xff,0x01,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0xe1,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x72,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
+0x38,0xff,0x42,0xff,0xff,0xff,0x50,0xff,0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x20,0xff,0x1c,0xff,0xff,0xff,0xcf,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
+0x79,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x19,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,
+0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,
+0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
+0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
+0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
+0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
+0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x8b,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x40,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,
+0x1b,0xff,0x14,0xff,0xff,0xff,0xce,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe1,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x9b,0xff,0xff,0xff,0xa0,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x1d,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x51,0xff,
+0x79,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x14,0xff,0xff,0xff,0xd5,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,
+0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
+0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
+0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x8f,0xff,0xff,0xff,0xc5,0xff,0x20,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
+0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,0x21,0xff,0x14,0xff,0xff,0xff,0x5e,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
+0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0c,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,
+0x98,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xa5,0xff,
+0x24,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,
+0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,
+0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
+0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x58,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x01,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x26,0xff,0x18,0xff,0xff,0xff,0x94,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x80,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x16,0xff,0x0f,0xff,0xff,0xff,0x02,0xff,
+0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
+0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
+0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,0x27,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x29,0xff,0x18,0xff,0xff,0xff,0x92,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x03,0xff,
+0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x31,0xff,0x18,0xff,0xff,0xff,0x12,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x34,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x55,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x18,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x48,0xff,0x10,0xff,0x0f,0xff,0xff,0xff,0xfe,0xff,
+0x87,0xff,0x93,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x2e,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x06,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa1,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x28,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x38,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,
+0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x32,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x44,0xff,
+0x08,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x8a,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x5a,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x35,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
+0x00,0xff,0x4c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
+0x35,0xff,0x1c,0xff,0xff,0xff,0x24,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x33,0xff,0x18,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x34,0xff,0x18,0xff,0xff,0xff,0x85,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x0d,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0xff,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x90,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x37,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x30,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x40,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x50,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x39,0xff,0x18,0xff,0xff,0xff,0x22,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x1c,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x41,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x42,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xd4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x47,0xff,0x18,0xff,0xff,0xff,0xa0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xb4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x48,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4a,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x94,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4c,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4d,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x74,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0x20,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x64,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x44,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x45,0xff,0x18,0xff,0xff,0xff,0x50,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3d,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x10,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x80,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x34,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x40,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x21,0xff,0x40,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x25,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0xe9,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0xed,0xff,0x41,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0x23,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf2,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0xe3,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
+0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x32,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
+0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x05,0xff,0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xc7,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe7,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x51,0xff,0x14,0xff,0xff,0xff,0x81,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,
+0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,
+0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,
+0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
+0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x54,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
+0x54,0xff,0x14,0xff,0xff,0xff,0x1e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb3,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x55,0xff,0x14,0xff,0xff,0xff,0x21,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x31,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x54,0xff,0x18,0xff,0xff,0xff,0xd5,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x82,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0xf1,0xff,0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x56,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x54,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x14,0xff,0xff,0xff,0x91,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,
+0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,
+0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,
+0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,
+0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x57,0xff,0x18,0xff,0xff,0xff,0x10,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x5a,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
+0x5a,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x59,0xff,0x18,0xff,0xff,0xff,0xd3,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x5b,0xff,0x14,0xff,0xff,0xff,0x61,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x5e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x5b,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x05,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x5e,0xff,0x1c,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x5f,0xff,0x1c,0xff,0xff,0xff,0x54,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0xa8,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x08,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x90,0xff,
+0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0xb6,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xfa,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
+0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0xe8,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xac,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0xd4,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xa8,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xd8,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
+0x01,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x01,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
+0x00,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x07,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
+0x03,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x02,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x08,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
+0x03,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x01,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x04,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x04,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
+0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,0x65,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,
+0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x80,0xff,0xff,0xff,0x48,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x03,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x74,0xff,0x18,0xff,0xff,0xff,0x54,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
+0x86,0xff,0x83,0xff,0xff,0xff,0x80,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xd0,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x8b,0xff,0xff,0xff,0xb0,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x44,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x55,0xff,
+0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xa0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0x24,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0x35,0xff,0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
+0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
+0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x6a,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x01,0xff,0x34,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,0x87,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x88,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x01,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x6a,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x6a,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x82,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xf4,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x86,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x64,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,
+0x01,0xff,0x34,0xff,0xff,0xff,0x96,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x65,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x96,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x49,0xff,
+0x02,0xff,0x38,0xff,0xff,0xff,0x49,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x02,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6d,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc5,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6e,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xe9,0xff,
+0x01,0xff,0x38,0xff,0xff,0xff,0x28,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x29,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x66,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x75,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
+0x00,0xff,0x41,0xff,0xff,0xff,0x07,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x98,0xff,0x70,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,
+0x70,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x41,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,
+0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x46,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x45,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x41,0xff,
+0x63,0xff,0x6a,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,
+0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0xf0,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,
+0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x72,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x30,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0x10,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0xb0,0xff,0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
+0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x40,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x10,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x20,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x48,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x86,0xff,0x97,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0x90,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xb0,0xff,
+0x88,0xff,0x37,0xff,0xff,0xff,0x03,0xff,0x8c,0xff,0x3b,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
+0x82,0xff,0x43,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0x80,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x80,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x76,0xff,0x14,0xff,0xff,0xff,0xde,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
+0x84,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,0x77,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,
+0x77,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x64,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x77,0xff,0x14,0xff,0xff,0xff,0xe5,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x78,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x56,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x01,0xff,0x40,0xff,0xff,0xff,0xc1,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x15,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x45,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xc8,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,
+0x7e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xb1,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xc5,0xff,
+0x7a,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0x46,0xff,
+0xe1,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x7a,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xa7,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff };
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
new file mode 100644
index 00000000000..bb1de200817
--- /dev/null
+++ b/sound/pci/korg1212/korg1212.c
@@ -0,0 +1,2553 @@
+/*
+ * Driver for the Korg 1212 IO PCI card
+ *
+ * Copyright (c) 2001 Haroldo Gamal <gamal@alternex.com.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+// ----------------------------------------------------------------------------
+// Debug Stuff
+// ----------------------------------------------------------------------------
+#define K1212_DEBUG_LEVEL 0
+#define K1212_DEBUG_PRINTK printk
+//#define K1212_DEBUG_PRINTK(x...) printk("<0>" x)
+
+// ----------------------------------------------------------------------------
+// Record/Play Buffer Allocation Method. If K1212_LARGEALLOC is defined all
+// buffers are alocated as a large piece inside KorgSharedBuffer.
+// ----------------------------------------------------------------------------
+//#define K1212_LARGEALLOC 1
+
+// ----------------------------------------------------------------------------
+// Valid states of the Korg 1212 I/O card.
+// ----------------------------------------------------------------------------
+typedef enum {
+ K1212_STATE_NONEXISTENT, // there is no card here
+ K1212_STATE_UNINITIALIZED, // the card is awaiting DSP download
+ K1212_STATE_DSP_IN_PROCESS, // the card is currently downloading its DSP code
+ K1212_STATE_DSP_COMPLETE, // the card has finished the DSP download
+ K1212_STATE_READY, // the card can be opened by an application. Any application
+ // requests prior to this state should fail. Only an open
+ // request can be made at this state.
+ K1212_STATE_OPEN, // an application has opened the card
+ K1212_STATE_SETUP, // the card has been setup for play
+ K1212_STATE_PLAYING, // the card is playing
+ K1212_STATE_MONITOR, // the card is in the monitor mode
+ K1212_STATE_CALIBRATING, // the card is currently calibrating
+ K1212_STATE_ERRORSTOP, // the card has stopped itself because of an error and we
+ // are in the process of cleaning things up.
+ K1212_STATE_MAX_STATE // state values of this and beyond are invalid
+} CardState;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants written to the card's
+// host-to-card doorbell to initiate a command.
+// ----------------------------------------------------------------------------
+typedef enum {
+ K1212_DB_RequestForData = 0, // sent by the card to request a buffer fill.
+ K1212_DB_TriggerPlay = 1, // starts playback/record on the card.
+ K1212_DB_SelectPlayMode = 2, // select monitor, playback setup, or stop.
+ K1212_DB_ConfigureBufferMemory = 3, // tells card where the host audio buffers are.
+ K1212_DB_RequestAdatTimecode = 4, // asks the card for the latest ADAT timecode value.
+ K1212_DB_SetClockSourceRate = 5, // sets the clock source and rate for the card.
+ K1212_DB_ConfigureMiscMemory = 6, // tells card where other buffers are.
+ K1212_DB_TriggerFromAdat = 7, // tells card to trigger from Adat at a specific
+ // timecode value.
+ K1212_DB_DMAERROR = 0x80, // DMA Error - the PCI bus is congestioned.
+ K1212_DB_CARDSTOPPED = 0x81, // Card has stopped by user request.
+ K1212_DB_RebootCard = 0xA0, // instructs the card to reboot.
+ K1212_DB_BootFromDSPPage4 = 0xA4, // instructs the card to boot from the DSP microcode
+ // on page 4 (local page to card).
+ K1212_DB_DSPDownloadDone = 0xAE, // sent by the card to indicate the download has
+ // completed.
+ K1212_DB_StartDSPDownload = 0xAF // tells the card to download its DSP firmware.
+} korg1212_dbcnst_t;
+
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines return codes
+// to the Korg 1212 I/O driver.
+// ----------------------------------------------------------------------------
+typedef enum {
+ K1212_CMDRET_Success = 0, // command was successfully placed
+ K1212_CMDRET_DIOCFailure, // the DeviceIoControl call failed
+ K1212_CMDRET_PMFailure, // the protected mode call failed
+ K1212_CMDRET_FailUnspecified, // unspecified failure
+ K1212_CMDRET_FailBadState, // the specified command can not be given in
+ // the card's current state. (or the wave device's
+ // state)
+ K1212_CMDRET_CardUninitialized, // the card is uninitialized and cannot be used
+ K1212_CMDRET_BadIndex, // an out of range card index was specified
+ K1212_CMDRET_BadHandle, // an invalid card handle was specified
+ K1212_CMDRET_NoFillRoutine, // a play request has been made before a fill routine set
+ K1212_CMDRET_FillRoutineInUse, // can't set a new fill routine while one is in use
+ K1212_CMDRET_NoAckFromCard, // the card never acknowledged a command
+ K1212_CMDRET_BadParams, // bad parameters were provided by the caller
+
+ K1212_CMDRET_BadDevice, // the specified wave device was out of range
+ K1212_CMDRET_BadFormat // the specified wave format is unsupported
+} snd_korg1212rc;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants used to select the play
+// mode for the card in the SelectPlayMode command.
+// ----------------------------------------------------------------------------
+typedef enum {
+ K1212_MODE_SetupPlay = 0x00000001, // provides card with pre-play information
+ K1212_MODE_MonitorOn = 0x00000002, // tells card to turn on monitor mode
+ K1212_MODE_MonitorOff = 0x00000004, // tells card to turn off monitor mode
+ K1212_MODE_StopPlay = 0x00000008 // stops playback on the card
+} PlayModeSelector;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants used to select the monitor
+// mode for the card in the SetMonitorMode command.
+// ----------------------------------------------------------------------------
+typedef enum {
+ K1212_MONMODE_Off = 0, // tells card to turn off monitor mode
+ K1212_MONMODE_On // tells card to turn on monitor mode
+} MonitorModeSelector;
+
+#define MAILBOX0_OFFSET 0x40 // location of mailbox 0 relative to base address
+#define MAILBOX1_OFFSET 0x44 // location of mailbox 1 relative to base address
+#define MAILBOX2_OFFSET 0x48 // location of mailbox 2 relative to base address
+#define MAILBOX3_OFFSET 0x4c // location of mailbox 3 relative to base address
+#define OUT_DOORBELL_OFFSET 0x60 // location of PCI to local doorbell
+#define IN_DOORBELL_OFFSET 0x64 // location of local to PCI doorbell
+#define STATUS_REG_OFFSET 0x68 // location of interrupt control/status register
+#define PCI_CONTROL_OFFSET 0x6c // location of the EEPROM, PCI, User I/O, init control
+ // register
+#define SENS_CONTROL_OFFSET 0x6e // location of the input sensitivity setting register.
+ // this is the upper word of the PCI control reg.
+#define DEV_VEND_ID_OFFSET 0x70 // location of the device and vendor ID register
+
+#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement
+ // from the card after sending a command.
+#define INTERCOMMAND_DELAY 40
+#define MAX_COMMAND_RETRIES 5 // maximum number of times the driver will attempt
+ // to send a command before giving up.
+#define COMMAND_ACK_MASK 0x8000 // the MSB is set in the command acknowledgment from
+ // the card.
+#define DOORBELL_VAL_MASK 0x00FF // the doorbell value is one byte
+
+#define CARD_BOOT_DELAY_IN_MS 10
+#define CARD_BOOT_TIMEOUT 10
+#define DSP_BOOT_DELAY_IN_MS 200
+
+#define kNumBuffers 8
+#define k1212MaxCards 4
+#define k1212NumWaveDevices 6
+#define k16BitChannels 10
+#define k32BitChannels 2
+#define kAudioChannels (k16BitChannels + k32BitChannels)
+#define kPlayBufferFrames 1024
+
+#define K1212_ANALOG_CHANNELS 2
+#define K1212_SPDIF_CHANNELS 2
+#define K1212_ADAT_CHANNELS 8
+#define K1212_CHANNELS (K1212_ADAT_CHANNELS + K1212_ANALOG_CHANNELS)
+#define K1212_MIN_CHANNELS 1
+#define K1212_MAX_CHANNELS K1212_CHANNELS
+#define K1212_FRAME_SIZE (sizeof(KorgAudioFrame))
+#define K1212_MAX_SAMPLES (kPlayBufferFrames*kNumBuffers)
+#define K1212_PERIODS (kNumBuffers)
+#define K1212_PERIOD_BYTES (K1212_FRAME_SIZE*kPlayBufferFrames)
+#define K1212_BUF_SIZE (K1212_PERIOD_BYTES*kNumBuffers)
+#define K1212_ANALOG_BUF_SIZE (K1212_ANALOG_CHANNELS * 2 * kPlayBufferFrames * kNumBuffers)
+#define K1212_SPDIF_BUF_SIZE (K1212_SPDIF_CHANNELS * 3 * kPlayBufferFrames * kNumBuffers)
+#define K1212_ADAT_BUF_SIZE (K1212_ADAT_CHANNELS * 2 * kPlayBufferFrames * kNumBuffers)
+#define K1212_MAX_BUF_SIZE (K1212_ANALOG_BUF_SIZE + K1212_ADAT_BUF_SIZE)
+
+#define k1212MinADCSens 0x7f
+#define k1212MaxADCSens 0x00
+#define k1212MaxVolume 0x7fff
+#define k1212MaxWaveVolume 0xffff
+#define k1212MinVolume 0x0000
+#define k1212MaxVolInverted 0x8000
+
+// -----------------------------------------------------------------
+// the following bits are used for controlling interrupts in the
+// interrupt control/status reg
+// -----------------------------------------------------------------
+#define PCI_INT_ENABLE_BIT 0x00000100
+#define PCI_DOORBELL_INT_ENABLE_BIT 0x00000200
+#define LOCAL_INT_ENABLE_BIT 0x00010000
+#define LOCAL_DOORBELL_INT_ENABLE_BIT 0x00020000
+#define LOCAL_DMA1_INT_ENABLE_BIT 0x00080000
+
+// -----------------------------------------------------------------
+// the following bits are defined for the PCI command register
+// -----------------------------------------------------------------
+#define PCI_CMD_MEM_SPACE_ENABLE_BIT 0x0002
+#define PCI_CMD_IO_SPACE_ENABLE_BIT 0x0001
+#define PCI_CMD_BUS_MASTER_ENABLE_BIT 0x0004
+
+// -----------------------------------------------------------------
+// the following bits are defined for the PCI status register
+// -----------------------------------------------------------------
+#define PCI_STAT_PARITY_ERROR_BIT 0x8000
+#define PCI_STAT_SYSTEM_ERROR_BIT 0x4000
+#define PCI_STAT_MASTER_ABORT_RCVD_BIT 0x2000
+#define PCI_STAT_TARGET_ABORT_RCVD_BIT 0x1000
+#define PCI_STAT_TARGET_ABORT_SENT_BIT 0x0800
+
+// ------------------------------------------------------------------------
+// the following constants are used in setting the 1212 I/O card's input
+// sensitivity.
+// ------------------------------------------------------------------------
+#define SET_SENS_LOCALINIT_BITPOS 15
+#define SET_SENS_DATA_BITPOS 10
+#define SET_SENS_CLOCK_BITPOS 8
+#define SET_SENS_LOADSHIFT_BITPOS 0
+
+#define SET_SENS_LEFTCHANID 0x00
+#define SET_SENS_RIGHTCHANID 0x01
+
+#define K1212SENSUPDATE_DELAY_IN_MS 50
+
+// --------------------------------------------------------------------------
+// WaitRTCTicks
+//
+// This function waits the specified number of real time clock ticks.
+// According to the DDK, each tick is ~0.8 microseconds.
+// The defines following the function declaration can be used for the
+// numTicksToWait parameter.
+// --------------------------------------------------------------------------
+#define ONE_RTC_TICK 1
+#define SENSCLKPULSE_WIDTH 4
+#define LOADSHIFT_DELAY 4
+#define INTERCOMMAND_DELAY 40
+#define STOPCARD_DELAY 300 // max # RTC ticks for the card to stop once we write
+ // the command register. (could be up to 180 us)
+#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement
+ // from the card after sending a command.
+
+#include "korg1212-firmware.h"
+
+typedef struct _snd_korg1212 korg1212_t;
+
+typedef u16 K1212Sample; // channels 0-9 use 16 bit samples
+typedef u32 K1212SpdifSample; // channels 10-11 use 32 bits - only 20 are sent
+ // across S/PDIF.
+typedef u32 K1212TimeCodeSample; // holds the ADAT timecode value
+
+typedef enum {
+ K1212_CLKIDX_AdatAt44_1K = 0, // selects source as ADAT at 44.1 kHz
+ K1212_CLKIDX_AdatAt48K, // selects source as ADAT at 48 kHz
+ K1212_CLKIDX_WordAt44_1K, // selects source as S/PDIF at 44.1 kHz
+ K1212_CLKIDX_WordAt48K, // selects source as S/PDIF at 48 kHz
+ K1212_CLKIDX_LocalAt44_1K, // selects source as local clock at 44.1 kHz
+ K1212_CLKIDX_LocalAt48K, // selects source as local clock at 48 kHz
+ K1212_CLKIDX_Invalid // used to check validity of the index
+} ClockSourceIndex;
+
+typedef enum {
+ K1212_CLKIDX_Adat = 0, // selects source as ADAT
+ K1212_CLKIDX_Word, // selects source as S/PDIF
+ K1212_CLKIDX_Local // selects source as local clock
+} ClockSourceType;
+
+typedef struct KorgAudioFrame {
+ K1212Sample frameData16[k16BitChannels];
+ K1212SpdifSample frameData32[k32BitChannels];
+ K1212TimeCodeSample timeCodeVal;
+} KorgAudioFrame;
+
+typedef struct KorgAudioBuffer {
+ KorgAudioFrame bufferData[kPlayBufferFrames]; /* buffer definition */
+} KorgAudioBuffer;
+
+typedef struct KorgSharedBuffer {
+#ifdef K1212_LARGEALLOC
+ KorgAudioBuffer playDataBufs[kNumBuffers];
+ KorgAudioBuffer recordDataBufs[kNumBuffers];
+#endif
+ short volumeData[kAudioChannels];
+ u32 cardCommand;
+ u16 routeData [kAudioChannels];
+ u32 AdatTimeCode; // ADAT timecode value
+} KorgSharedBuffer;
+
+typedef struct SensBits {
+ union {
+ struct {
+ unsigned int leftChanVal:8;
+ unsigned int leftChanId:8;
+ } v;
+ u16 leftSensBits;
+ } l;
+ union {
+ struct {
+ unsigned int rightChanVal:8;
+ unsigned int rightChanId:8;
+ } v;
+ u16 rightSensBits;
+ } r;
+} SensBits;
+
+struct _snd_korg1212 {
+ snd_card_t *card;
+ struct pci_dev *pci;
+ snd_pcm_t *pcm;
+ int irq;
+
+ spinlock_t lock;
+ struct semaphore open_mutex;
+
+ struct timer_list timer; /* timer callback for checking ack of stop request */
+ int stop_pending_cnt; /* counter for stop pending check */
+
+ wait_queue_head_t wait;
+
+ unsigned long iomem;
+ unsigned long ioport;
+ unsigned long iomem2;
+ unsigned long irqcount;
+ unsigned long inIRQ;
+ void __iomem *iobase;
+
+ struct snd_dma_buffer dma_dsp;
+ struct snd_dma_buffer dma_play;
+ struct snd_dma_buffer dma_rec;
+ struct snd_dma_buffer dma_shared;
+
+ u32 dspCodeSize;
+
+ u32 DataBufsSize;
+
+ KorgAudioBuffer * playDataBufsPtr;
+ KorgAudioBuffer * recordDataBufsPtr;
+
+ KorgSharedBuffer * sharedBufferPtr;
+
+ u32 RecDataPhy;
+ u32 PlayDataPhy;
+ unsigned long sharedBufferPhy;
+ u32 VolumeTablePhy;
+ u32 RoutingTablePhy;
+ u32 AdatTimeCodePhy;
+
+ u32 __iomem * statusRegPtr; // address of the interrupt status/control register
+ u32 __iomem * outDoorbellPtr; // address of the host->card doorbell register
+ u32 __iomem * inDoorbellPtr; // address of the card->host doorbell register
+ u32 __iomem * mailbox0Ptr; // address of mailbox 0 on the card
+ u32 __iomem * mailbox1Ptr; // address of mailbox 1 on the card
+ u32 __iomem * mailbox2Ptr; // address of mailbox 2 on the card
+ u32 __iomem * mailbox3Ptr; // address of mailbox 3 on the card
+ u32 __iomem * controlRegPtr; // address of the EEPROM, PCI, I/O, Init ctrl reg
+ u16 __iomem * sensRegPtr; // address of the sensitivity setting register
+ u32 __iomem * idRegPtr; // address of the device and vendor ID registers
+
+ size_t periodsize;
+ int channels;
+ int currentBuffer;
+
+ snd_pcm_substream_t *playback_substream;
+ snd_pcm_substream_t *capture_substream;
+
+ pid_t capture_pid;
+ pid_t playback_pid;
+
+ CardState cardState;
+ int running;
+ int idleMonitorOn; // indicates whether the card is in idle monitor mode.
+ u32 cmdRetryCount; // tracks how many times we have retried sending to the card.
+
+ ClockSourceIndex clkSrcRate; // sample rate and clock source
+
+ ClockSourceType clkSource; // clock source
+ int clkRate; // clock rate
+
+ int volumePhase[kAudioChannels];
+
+ u16 leftADCInSens; // ADC left channel input sensitivity
+ u16 rightADCInSens; // ADC right channel input sensitivity
+
+ int opencnt; // Open/Close count
+ int setcnt; // SetupForPlay count
+ int playcnt; // TriggerPlay count
+ int errorcnt; // Error Count
+ unsigned long totalerrorcnt; // Total Error Count
+
+ int dsp_is_loaded;
+ int dsp_stop_is_processed;
+
+};
+
+MODULE_DESCRIPTION("korg1212");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{KORG,korg1212}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Korg 1212 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Korg 1212 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard.");
+MODULE_AUTHOR("Haroldo Gamal <gamal@alternex.com.br>");
+
+static struct pci_device_id snd_korg1212_ids[] = {
+ {
+ .vendor = 0x10b5,
+ .device = 0x906d,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { 0, },
+};
+
+static char* stateName[] = {
+ "Non-existent",
+ "Uninitialized",
+ "DSP download in process",
+ "DSP download complete",
+ "Ready",
+ "Open",
+ "Setup for play",
+ "Playing",
+ "Monitor mode on",
+ "Calibrating"
+ "Invalid"
+};
+
+static char* clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" };
+
+static char* clockSourceName[] = {
+ "ADAT at 44.1 kHz",
+ "ADAT at 48 kHz",
+ "S/PDIF at 44.1 kHz",
+ "S/PDIF at 48 kHz",
+ "local clock at 44.1 kHz",
+ "local clock at 48 kHz"
+};
+
+static char* channelName[] = {
+ "ADAT-1",
+ "ADAT-2",
+ "ADAT-3",
+ "ADAT-4",
+ "ADAT-5",
+ "ADAT-6",
+ "ADAT-7",
+ "ADAT-8",
+ "Analog-L",
+ "Analog-R",
+ "SPDIF-L",
+ "SPDIF-R",
+};
+
+static u16 ClockSourceSelector[] =
+ {0x8000, // selects source as ADAT at 44.1 kHz
+ 0x0000, // selects source as ADAT at 48 kHz
+ 0x8001, // selects source as S/PDIF at 44.1 kHz
+ 0x0001, // selects source as S/PDIF at 48 kHz
+ 0x8002, // selects source as local clock at 44.1 kHz
+ 0x0002 // selects source as local clock at 48 kHz
+ };
+
+static snd_korg1212rc rc;
+
+MODULE_DEVICE_TABLE(pci, snd_korg1212_ids);
+
+typedef union swap_u32 { unsigned char c[4]; u32 i; } swap_u32;
+
+#ifdef SNDRV_BIG_ENDIAN
+static u32 LowerWordSwap(u32 swappee)
+#else
+static u32 UpperWordSwap(u32 swappee)
+#endif
+{
+ swap_u32 retVal, swapper;
+
+ swapper.i = swappee;
+ retVal.c[2] = swapper.c[3];
+ retVal.c[3] = swapper.c[2];
+ retVal.c[1] = swapper.c[1];
+ retVal.c[0] = swapper.c[0];
+
+ return retVal.i;
+}
+
+#ifdef SNDRV_BIG_ENDIAN
+static u32 UpperWordSwap(u32 swappee)
+#else
+static u32 LowerWordSwap(u32 swappee)
+#endif
+{
+ swap_u32 retVal, swapper;
+
+ swapper.i = swappee;
+ retVal.c[2] = swapper.c[2];
+ retVal.c[3] = swapper.c[3];
+ retVal.c[1] = swapper.c[0];
+ retVal.c[0] = swapper.c[1];
+
+ return retVal.i;
+}
+
+#if 0 /* not used */
+
+static u32 EndianSwap(u32 swappee)
+{
+ swap_u32 retVal, swapper;
+
+ swapper.i = swappee;
+ retVal.c[0] = swapper.c[3];
+ retVal.c[1] = swapper.c[2];
+ retVal.c[2] = swapper.c[1];
+ retVal.c[3] = swapper.c[0];
+
+ return retVal.i;
+}
+
+#endif /* not used */
+
+#define SetBitInWord(theWord,bitPosition) (*theWord) |= (0x0001 << bitPosition)
+#define SetBitInDWord(theWord,bitPosition) (*theWord) |= (0x00000001 << bitPosition)
+#define ClearBitInWord(theWord,bitPosition) (*theWord) &= ~(0x0001 << bitPosition)
+#define ClearBitInDWord(theWord,bitPosition) (*theWord) &= ~(0x00000001 << bitPosition)
+
+static snd_korg1212rc snd_korg1212_Send1212Command(korg1212_t *korg1212, korg1212_dbcnst_t doorbellVal,
+ u32 mailBox0Val, u32 mailBox1Val, u32 mailBox2Val, u32 mailBox3Val)
+{
+ u32 retryCount;
+ u16 mailBox3Lo;
+ snd_korg1212rc rc = K1212_CMDRET_Success;
+
+ if (!korg1212->outDoorbellPtr) {
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: CardUninitialized\n");
+#endif
+ return K1212_CMDRET_CardUninitialized;
+ }
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Card <- 0x%08x 0x%08x [%s]\n", doorbellVal, mailBox0Val, stateName[korg1212->cardState]);
+#endif
+ for (retryCount = 0; retryCount < MAX_COMMAND_RETRIES; retryCount++) {
+ writel(mailBox3Val, korg1212->mailbox3Ptr);
+ writel(mailBox2Val, korg1212->mailbox2Ptr);
+ writel(mailBox1Val, korg1212->mailbox1Ptr);
+ writel(mailBox0Val, korg1212->mailbox0Ptr);
+ writel(doorbellVal, korg1212->outDoorbellPtr); // interrupt the card
+
+ // --------------------------------------------------------------
+ // the reboot command will not give an acknowledgement.
+ // --------------------------------------------------------------
+ if ( doorbellVal == K1212_DB_RebootCard ||
+ doorbellVal == K1212_DB_BootFromDSPPage4 ||
+ doorbellVal == K1212_DB_StartDSPDownload ) {
+ rc = K1212_CMDRET_Success;
+ break;
+ }
+
+ // --------------------------------------------------------------
+ // See if the card acknowledged the command. Wait a bit, then
+ // read in the low word of mailbox3. If the MSB is set and the
+ // low byte is equal to the doorbell value, then it ack'd.
+ // --------------------------------------------------------------
+ udelay(COMMAND_ACK_DELAY);
+ mailBox3Lo = readl(korg1212->mailbox3Ptr);
+ if (mailBox3Lo & COMMAND_ACK_MASK) {
+ if ((mailBox3Lo & DOORBELL_VAL_MASK) == (doorbellVal & DOORBELL_VAL_MASK)) {
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Card <- Success\n");
+#endif
+ rc = K1212_CMDRET_Success;
+ break;
+ }
+ }
+ }
+ korg1212->cmdRetryCount += retryCount;
+
+ if (retryCount >= MAX_COMMAND_RETRIES) {
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Card <- NoAckFromCard\n");
+#endif
+ rc = K1212_CMDRET_NoAckFromCard;
+ }
+
+ return rc;
+}
+
+/* spinlock already held */
+static void snd_korg1212_SendStop(korg1212_t *korg1212)
+{
+ if (! korg1212->stop_pending_cnt) {
+ korg1212->sharedBufferPtr->cardCommand = 0xffffffff;
+ /* program the timer */
+ korg1212->stop_pending_cnt = HZ;
+ korg1212->timer.expires = jiffies + 1;
+ add_timer(&korg1212->timer);
+ }
+}
+
+static void snd_korg1212_SendStopAndWait(korg1212_t *korg1212)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&korg1212->lock, flags);
+ korg1212->dsp_stop_is_processed = 0;
+ snd_korg1212_SendStop(korg1212);
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+ wait_event_timeout(korg1212->wait, korg1212->dsp_stop_is_processed, (HZ * 3) / 2);
+}
+
+/* timer callback for checking the ack of stop request */
+static void snd_korg1212_timer_func(unsigned long data)
+{
+ korg1212_t *korg1212 = (korg1212_t *) data;
+
+ spin_lock(&korg1212->lock);
+ if (korg1212->sharedBufferPtr->cardCommand == 0) {
+ /* ack'ed */
+ korg1212->stop_pending_cnt = 0;
+ korg1212->dsp_stop_is_processed = 1;
+ wake_up(&korg1212->wait);
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Stop ack'ed [%s]\n", stateName[korg1212->cardState]);
+#endif
+ } else {
+ if (--korg1212->stop_pending_cnt > 0) {
+ /* reprogram timer */
+ korg1212->timer.expires = jiffies + 1;
+ add_timer(&korg1212->timer);
+ } else {
+ snd_printd("korg1212_timer_func timeout\n");
+ korg1212->sharedBufferPtr->cardCommand = 0;
+ korg1212->dsp_stop_is_processed = 1;
+ wake_up(&korg1212->wait);
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Stop timeout [%s]\n", stateName[korg1212->cardState]);
+#endif
+ }
+ }
+ spin_unlock(&korg1212->lock);
+}
+
+static void snd_korg1212_TurnOnIdleMonitor(korg1212_t *korg1212)
+{
+ unsigned long flags;
+
+ udelay(INTERCOMMAND_DELAY);
+ spin_lock_irqsave(&korg1212->lock, flags);
+ korg1212->idleMonitorOn = 1;
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+ K1212_MODE_MonitorOn, 0, 0, 0);
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+}
+
+static void snd_korg1212_TurnOffIdleMonitor(korg1212_t *korg1212)
+{
+ if (korg1212->idleMonitorOn) {
+ snd_korg1212_SendStopAndWait(korg1212);
+ korg1212->idleMonitorOn = 0;
+ }
+}
+
+static inline void snd_korg1212_setCardState(korg1212_t * korg1212, CardState csState)
+{
+ korg1212->cardState = csState;
+}
+
+static int snd_korg1212_OpenCard(korg1212_t * korg1212)
+{
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: OpenCard [%s] %d\n", stateName[korg1212->cardState], korg1212->opencnt);
+#endif
+ down(&korg1212->open_mutex);
+ if (korg1212->opencnt++ == 0) {
+ snd_korg1212_TurnOffIdleMonitor(korg1212);
+ snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+ }
+
+ up(&korg1212->open_mutex);
+ return 1;
+}
+
+static int snd_korg1212_CloseCard(korg1212_t * korg1212)
+{
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard [%s] %d\n", stateName[korg1212->cardState], korg1212->opencnt);
+#endif
+
+ down(&korg1212->open_mutex);
+ if (--(korg1212->opencnt)) {
+ up(&korg1212->open_mutex);
+ return 0;
+ }
+
+ if (korg1212->cardState == K1212_STATE_SETUP) {
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+ K1212_MODE_StopPlay, 0, 0, 0);
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ if (rc != K1212_CMDRET_Success) {
+ up(&korg1212->open_mutex);
+ return 0;
+ }
+ } else if (korg1212->cardState > K1212_STATE_SETUP) {
+ snd_korg1212_SendStopAndWait(korg1212);
+ }
+
+ if (korg1212->cardState > K1212_STATE_READY) {
+ snd_korg1212_TurnOnIdleMonitor(korg1212);
+ snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
+ }
+
+ up(&korg1212->open_mutex);
+ return 0;
+}
+
+/* spinlock already held */
+static int snd_korg1212_SetupForPlay(korg1212_t * korg1212)
+{
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: SetupForPlay [%s] %d\n", stateName[korg1212->cardState], korg1212->setcnt);
+#endif
+
+ if (korg1212->setcnt++)
+ return 0;
+
+ snd_korg1212_setCardState(korg1212, K1212_STATE_SETUP);
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+ K1212_MODE_SetupPlay, 0, 0, 0);
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: SetupForPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+ if (rc != K1212_CMDRET_Success) {
+ return 1;
+ }
+ return 0;
+}
+
+/* spinlock already held */
+static int snd_korg1212_TriggerPlay(korg1212_t * korg1212)
+{
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: TriggerPlay [%s] %d\n", stateName[korg1212->cardState], korg1212->playcnt);
+#endif
+
+ if (korg1212->playcnt++)
+ return 0;
+
+ snd_korg1212_setCardState(korg1212, K1212_STATE_PLAYING);
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_TriggerPlay, 0, 0, 0, 0);
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: TriggerPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ if (rc != K1212_CMDRET_Success) {
+ return 1;
+ }
+ return 0;
+}
+
+/* spinlock already held */
+static int snd_korg1212_StopPlay(korg1212_t * korg1212)
+{
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: StopPlay [%s] %d\n", stateName[korg1212->cardState], korg1212->playcnt);
+#endif
+
+ if (--(korg1212->playcnt))
+ return 0;
+
+ korg1212->setcnt = 0;
+
+ if (korg1212->cardState != K1212_STATE_ERRORSTOP)
+ snd_korg1212_SendStop(korg1212);
+
+ snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+ return 0;
+}
+
+static void snd_korg1212_EnableCardInterrupts(korg1212_t * korg1212)
+{
+ writel(PCI_INT_ENABLE_BIT |
+ PCI_DOORBELL_INT_ENABLE_BIT |
+ LOCAL_INT_ENABLE_BIT |
+ LOCAL_DOORBELL_INT_ENABLE_BIT |
+ LOCAL_DMA1_INT_ENABLE_BIT,
+ korg1212->statusRegPtr);
+}
+
+#if 0 /* not used */
+
+static int snd_korg1212_SetMonitorMode(korg1212_t *korg1212, MonitorModeSelector mode)
+{
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: SetMonitorMode [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ switch (mode) {
+ case K1212_MONMODE_Off:
+ if (korg1212->cardState != K1212_STATE_MONITOR) {
+ return 0;
+ } else {
+ snd_korg1212_SendStopAndWait(korg1212);
+ snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+ }
+ break;
+
+ case K1212_MONMODE_On:
+ if (korg1212->cardState != K1212_STATE_OPEN) {
+ return 0;
+ } else {
+ snd_korg1212_setCardState(korg1212, K1212_STATE_MONITOR);
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+ K1212_MODE_MonitorOn, 0, 0, 0);
+ if (rc != K1212_CMDRET_Success) {
+ return 0;
+ }
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+#endif /* not used */
+
+static inline int snd_korg1212_use_is_exclusive(korg1212_t *korg1212)
+{
+ int ret = 1;
+
+ if ((korg1212->playback_pid != korg1212->capture_pid) &&
+ (korg1212->playback_pid >= 0) && (korg1212->capture_pid >= 0)) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static int snd_korg1212_SetRate(korg1212_t *korg1212, int rate)
+{
+ static ClockSourceIndex s44[] = { K1212_CLKIDX_AdatAt44_1K,
+ K1212_CLKIDX_WordAt44_1K,
+ K1212_CLKIDX_LocalAt44_1K };
+ static ClockSourceIndex s48[] = {
+ K1212_CLKIDX_AdatAt48K,
+ K1212_CLKIDX_WordAt48K,
+ K1212_CLKIDX_LocalAt48K };
+ int parm;
+
+ if (!snd_korg1212_use_is_exclusive (korg1212)) {
+ return -EBUSY;
+ }
+
+ switch(rate) {
+ case 44100:
+ parm = s44[korg1212->clkSource];
+ break;
+
+ case 48000:
+ parm = s48[korg1212->clkSource];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ korg1212->clkSrcRate = parm;
+ korg1212->clkRate = rate;
+
+ udelay(INTERCOMMAND_DELAY);
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate,
+ ClockSourceSelector[korg1212->clkSrcRate],
+ 0, 0, 0);
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ return 0;
+}
+
+static int snd_korg1212_SetClockSource(korg1212_t *korg1212, int source)
+{
+
+ if (source<0 || source >2)
+ return -EINVAL;
+
+ korg1212->clkSource = source;
+
+ snd_korg1212_SetRate(korg1212, korg1212->clkRate);
+
+ return 0;
+}
+
+static void snd_korg1212_DisableCardInterrupts(korg1212_t *korg1212)
+{
+ writel(0, korg1212->statusRegPtr);
+}
+
+static int snd_korg1212_WriteADCSensitivity(korg1212_t *korg1212)
+{
+ SensBits sensVals;
+ int bitPosition;
+ int channel;
+ int clkIs48K;
+ int monModeSet;
+ u16 controlValue; // this keeps the current value to be written to
+ // the card's eeprom control register.
+ u16 count;
+ unsigned long flags;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: WriteADCSensivity [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ // ----------------------------------------------------------------------------
+ // initialize things. The local init bit is always set when writing to the
+ // card's control register.
+ // ----------------------------------------------------------------------------
+ controlValue = 0;
+ SetBitInWord(&controlValue, SET_SENS_LOCALINIT_BITPOS); // init the control value
+
+ // ----------------------------------------------------------------------------
+ // make sure the card is not in monitor mode when we do this update.
+ // ----------------------------------------------------------------------------
+ if (korg1212->cardState == K1212_STATE_MONITOR || korg1212->idleMonitorOn) {
+ monModeSet = 1;
+ snd_korg1212_SendStopAndWait(korg1212);
+ } else
+ monModeSet = 0;
+
+ spin_lock_irqsave(&korg1212->lock, flags);
+
+ // ----------------------------------------------------------------------------
+ // we are about to send new values to the card, so clear the new values queued
+ // flag. Also, clear out mailbox 3, so we don't lockup.
+ // ----------------------------------------------------------------------------
+ writel(0, korg1212->mailbox3Ptr);
+ udelay(LOADSHIFT_DELAY);
+
+ // ----------------------------------------------------------------------------
+ // determine whether we are running a 48K or 44.1K clock. This info is used
+ // later when setting the SPDIF FF after the volume has been shifted in.
+ // ----------------------------------------------------------------------------
+ switch (korg1212->clkSrcRate) {
+ case K1212_CLKIDX_AdatAt44_1K:
+ case K1212_CLKIDX_WordAt44_1K:
+ case K1212_CLKIDX_LocalAt44_1K:
+ clkIs48K = 0;
+ break;
+
+ case K1212_CLKIDX_WordAt48K:
+ case K1212_CLKIDX_AdatAt48K:
+ case K1212_CLKIDX_LocalAt48K:
+ default:
+ clkIs48K = 1;
+ break;
+ }
+
+ // ----------------------------------------------------------------------------
+ // start the update. Setup the bit structure and then shift the bits.
+ // ----------------------------------------------------------------------------
+ sensVals.l.v.leftChanId = SET_SENS_LEFTCHANID;
+ sensVals.r.v.rightChanId = SET_SENS_RIGHTCHANID;
+ sensVals.l.v.leftChanVal = korg1212->leftADCInSens;
+ sensVals.r.v.rightChanVal = korg1212->rightADCInSens;
+
+ // ----------------------------------------------------------------------------
+ // now start shifting the bits in. Start with the left channel then the right.
+ // ----------------------------------------------------------------------------
+ for (channel = 0; channel < 2; channel++) {
+
+ // ----------------------------------------------------------------------------
+ // Bring the load/shift line low, then wait - the spec says >150ns from load/
+ // shift low to the first rising edge of the clock.
+ // ----------------------------------------------------------------------------
+ ClearBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS);
+ ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+ writew(controlValue, korg1212->sensRegPtr); // load/shift goes low
+ udelay(LOADSHIFT_DELAY);
+
+ for (bitPosition = 15; bitPosition >= 0; bitPosition--) { // for all the bits
+ if (channel == 0) {
+ if (sensVals.l.leftSensBits & (0x0001 << bitPosition)) {
+ SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high
+ } else {
+ ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low
+ }
+ } else {
+ if (sensVals.r.rightSensBits & (0x0001 << bitPosition)) {
+ SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high
+ } else {
+ ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low
+ }
+ }
+
+ ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+ writew(controlValue, korg1212->sensRegPtr); // clock goes low
+ udelay(SENSCLKPULSE_WIDTH);
+ SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+ writew(controlValue, korg1212->sensRegPtr); // clock goes high
+ udelay(SENSCLKPULSE_WIDTH);
+ }
+
+ // ----------------------------------------------------------------------------
+ // finish up SPDIF for left. Bring the load/shift line high, then write a one
+ // bit if the clock rate is 48K otherwise write 0.
+ // ----------------------------------------------------------------------------
+ ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+ ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+ SetBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS);
+ writew(controlValue, korg1212->sensRegPtr); // load shift goes high - clk low
+ udelay(SENSCLKPULSE_WIDTH);
+
+ if (clkIs48K)
+ SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+
+ writew(controlValue, korg1212->sensRegPtr); // set/clear data bit
+ udelay(ONE_RTC_TICK);
+ SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+ writew(controlValue, korg1212->sensRegPtr); // clock goes high
+ udelay(SENSCLKPULSE_WIDTH);
+ ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+ writew(controlValue, korg1212->sensRegPtr); // clock goes low
+ udelay(SENSCLKPULSE_WIDTH);
+ }
+
+ // ----------------------------------------------------------------------------
+ // The update is complete. Set a timeout. This is the inter-update delay.
+ // Also, if the card was in monitor mode, restore it.
+ // ----------------------------------------------------------------------------
+ for (count = 0; count < 10; count++)
+ udelay(SENSCLKPULSE_WIDTH);
+
+ if (monModeSet) {
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+ K1212_MODE_MonitorOn, 0, 0, 0);
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: WriteADCSensivity - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ }
+
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+
+ return 1;
+}
+
+static void snd_korg1212_OnDSPDownloadComplete(korg1212_t *korg1212)
+{
+ int channel;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: DSP download is complete. [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ // ----------------------------------------------------
+ // tell the card to boot
+ // ----------------------------------------------------
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_BootFromDSPPage4, 0, 0, 0, 0);
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Boot from Page 4 - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+ mdelay(DSP_BOOT_DELAY_IN_MS);
+
+ // --------------------------------------------------------------------------------
+ // Let the card know where all the buffers are.
+ // --------------------------------------------------------------------------------
+ rc = snd_korg1212_Send1212Command(korg1212,
+ K1212_DB_ConfigureBufferMemory,
+ LowerWordSwap(korg1212->PlayDataPhy),
+ LowerWordSwap(korg1212->RecDataPhy),
+ ((kNumBuffers * kPlayBufferFrames) / 2), // size given to the card
+ // is based on 2 buffers
+ 0
+ );
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Configure Buffer Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ udelay(INTERCOMMAND_DELAY);
+
+ rc = snd_korg1212_Send1212Command(korg1212,
+ K1212_DB_ConfigureMiscMemory,
+ LowerWordSwap(korg1212->VolumeTablePhy),
+ LowerWordSwap(korg1212->RoutingTablePhy),
+ LowerWordSwap(korg1212->AdatTimeCodePhy),
+ 0
+ );
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Configure Misc Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+
+ // --------------------------------------------------------------------------------
+ // Initialize the routing and volume tables, then update the card's state.
+ // --------------------------------------------------------------------------------
+ udelay(INTERCOMMAND_DELAY);
+
+ for (channel = 0; channel < kAudioChannels; channel++) {
+ korg1212->sharedBufferPtr->volumeData[channel] = k1212MaxVolume;
+ //korg1212->sharedBufferPtr->routeData[channel] = channel;
+ korg1212->sharedBufferPtr->routeData[channel] = 8 + (channel & 1);
+ }
+
+ snd_korg1212_WriteADCSensitivity(korg1212);
+
+ udelay(INTERCOMMAND_DELAY);
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate,
+ ClockSourceSelector[korg1212->clkSrcRate],
+ 0, 0, 0);
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ snd_korg1212_TurnOnIdleMonitor(korg1212);
+ snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Set Monitor On - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_COMPLETE);
+}
+
+static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 doorbellValue;
+ korg1212_t *korg1212 = dev_id;
+
+ if(irq != korg1212->irq)
+ return IRQ_NONE;
+
+ doorbellValue = readl(korg1212->inDoorbellPtr);
+
+ if (!doorbellValue)
+ return IRQ_NONE;
+
+ spin_lock(&korg1212->lock);
+
+ writel(doorbellValue, korg1212->inDoorbellPtr);
+
+ korg1212->irqcount++;
+
+ korg1212->inIRQ++;
+
+
+ switch (doorbellValue) {
+ case K1212_DB_DSPDownloadDone:
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ DNLD count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+ if (korg1212->cardState == K1212_STATE_DSP_IN_PROCESS) {
+ korg1212->dsp_is_loaded = 1;
+ wake_up(&korg1212->wait);
+ }
+ break;
+
+ // ------------------------------------------------------------------------
+ // an error occurred - stop the card
+ // ------------------------------------------------------------------------
+ case K1212_DB_DMAERROR:
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ DMAE count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+ snd_printk(KERN_ERR "korg1212: DMA Error\n");
+ korg1212->errorcnt++;
+ korg1212->totalerrorcnt++;
+ korg1212->sharedBufferPtr->cardCommand = 0;
+ snd_korg1212_setCardState(korg1212, K1212_STATE_ERRORSTOP);
+ break;
+
+ // ------------------------------------------------------------------------
+ // the card has stopped by our request. Clear the command word and signal
+ // the semaphore in case someone is waiting for this.
+ // ------------------------------------------------------------------------
+ case K1212_DB_CARDSTOPPED:
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ CSTP count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+ korg1212->sharedBufferPtr->cardCommand = 0;
+ break;
+
+ default:
+#if K1212_DEBUG_LEVEL > 3
+ K1212_DEBUG_PRINTK("K1212_DEBUG: IRQ DFLT count - %ld, %x, cpos=%d [%s].\n", korg1212->irqcount, doorbellValue,
+ korg1212->currentBuffer, stateName[korg1212->cardState]);
+#endif
+ if ((korg1212->cardState > K1212_STATE_SETUP) || korg1212->idleMonitorOn) {
+ korg1212->currentBuffer++;
+
+ if (korg1212->currentBuffer >= kNumBuffers)
+ korg1212->currentBuffer = 0;
+
+ if (!korg1212->running)
+ break;
+
+ if (korg1212->capture_substream) {
+ spin_unlock(&korg1212->lock);
+ snd_pcm_period_elapsed(korg1212->capture_substream);
+ spin_lock(&korg1212->lock);
+ }
+
+ if (korg1212->playback_substream) {
+ spin_unlock(&korg1212->lock);
+ snd_pcm_period_elapsed(korg1212->playback_substream);
+ spin_lock(&korg1212->lock);
+ }
+ }
+ break;
+ }
+
+ korg1212->inIRQ--;
+
+ spin_unlock(&korg1212->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int snd_korg1212_downloadDSPCode(korg1212_t *korg1212)
+{
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: DSP download is starting... [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ // ---------------------------------------------------------------
+ // verify the state of the card before proceeding.
+ // ---------------------------------------------------------------
+ if (korg1212->cardState >= K1212_STATE_DSP_IN_PROCESS) {
+ return 1;
+ }
+
+ snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS);
+
+ memcpy(korg1212->dma_dsp.area, dspCode, korg1212->dspCodeSize);
+
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload,
+ UpperWordSwap(korg1212->dma_dsp.addr),
+ 0, 0, 0);
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Start DSP Download RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ korg1212->dsp_is_loaded = 0;
+ wait_event_timeout(korg1212->wait, korg1212->dsp_is_loaded, HZ * CARD_BOOT_TIMEOUT);
+ if (! korg1212->dsp_is_loaded )
+ return -EBUSY; /* timeout */
+
+ snd_korg1212_OnDSPDownloadComplete(korg1212);
+
+ return 0;
+}
+
+static snd_pcm_hardware_t snd_korg1212_playback_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = K1212_MIN_CHANNELS,
+ .channels_max = K1212_MAX_CHANNELS,
+ .buffer_bytes_max = K1212_MAX_BUF_SIZE,
+ .period_bytes_min = K1212_MIN_CHANNELS * 2 * kPlayBufferFrames,
+ .period_bytes_max = K1212_MAX_CHANNELS * 2 * kPlayBufferFrames,
+ .periods_min = K1212_PERIODS,
+ .periods_max = K1212_PERIODS,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_korg1212_capture_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = K1212_MIN_CHANNELS,
+ .channels_max = K1212_MAX_CHANNELS,
+ .buffer_bytes_max = K1212_MAX_BUF_SIZE,
+ .period_bytes_min = K1212_MIN_CHANNELS * 2 * kPlayBufferFrames,
+ .period_bytes_max = K1212_MAX_CHANNELS * 2 * kPlayBufferFrames,
+ .periods_min = K1212_PERIODS,
+ .periods_max = K1212_PERIODS,
+ .fifo_size = 0,
+};
+
+static int snd_korg1212_silence(korg1212_t *korg1212, int pos, int count, int offset, int size)
+{
+ KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+ int i;
+
+#if K1212_DEBUG_LEVEL > 2
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_silence pos=%d offset=%d size=%d count=%d\n", pos, offset, size, count);
+#endif
+ snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+ for (i=0; i < count; i++) {
+#if K1212_DEBUG_LEVEL > 0
+ if ( (void *) dst < (void *) korg1212->playDataBufsPtr ||
+ (void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) {
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_silence KERNEL EFAULT dst=%p iter=%d\n", dst, i);
+ return -EFAULT;
+ }
+#endif
+ memset((void*) dst + offset, 0, size);
+ dst++;
+ }
+
+ return 0;
+}
+
+static int snd_korg1212_copy_to(korg1212_t *korg1212, void __user *dst, int pos, int count, int offset, int size)
+{
+ KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos;
+ int i, rc;
+
+#if K1212_DEBUG_LEVEL > 2
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n", pos, offset, size);
+#endif
+ snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+ for (i=0; i < count; i++) {
+#if K1212_DEBUG_LEVEL > 0
+ if ( (void *) src < (void *) korg1212->recordDataBufsPtr ||
+ (void *) src > (void *) korg1212->recordDataBufsPtr[8].bufferData ) {
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ return -EFAULT;
+ }
+#endif
+ rc = copy_to_user(dst + offset, src, size);
+ if (rc) {
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i);
+#endif
+ return -EFAULT;
+ }
+ src++;
+ dst += size;
+ }
+
+ return 0;
+}
+
+static int snd_korg1212_copy_from(korg1212_t *korg1212, void __user *src, int pos, int count, int offset, int size)
+{
+ KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+ int i, rc;
+
+#if K1212_DEBUG_LEVEL > 2
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n", pos, offset, size, count);
+#endif
+
+ snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+ for (i=0; i < count; i++) {
+#if K1212_DEBUG_LEVEL > 0
+ if ( (void *) dst < (void *) korg1212->playDataBufsPtr ||
+ (void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) {
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ return -EFAULT;
+ }
+#endif
+ rc = copy_from_user((void*) dst + offset, src, size);
+ if (rc) {
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i);
+#endif
+ return -EFAULT;
+ }
+ dst++;
+ src += size;
+ }
+
+ return 0;
+}
+
+static void snd_korg1212_free_pcm(snd_pcm_t *pcm)
+{
+ korg1212_t *korg1212 = (korg1212_t *) pcm->private_data;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_free_pcm [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ korg1212->pcm = NULL;
+}
+
+static int snd_korg1212_playback_open(snd_pcm_substream_t *substream)
+{
+ unsigned long flags;
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_open [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ snd_pcm_set_sync(substream); // ???
+
+ snd_korg1212_OpenCard(korg1212);
+
+ runtime->hw = snd_korg1212_playback_info;
+ snd_pcm_set_runtime_buffer(substream, &korg1212->dma_play);
+
+ spin_lock_irqsave(&korg1212->lock, flags);
+
+ korg1212->playback_substream = substream;
+ korg1212->playback_pid = current->pid;
+ korg1212->periodsize = K1212_PERIODS;
+ korg1212->channels = K1212_CHANNELS;
+ korg1212->errorcnt = 0;
+
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+ return 0;
+}
+
+
+static int snd_korg1212_capture_open(snd_pcm_substream_t *substream)
+{
+ unsigned long flags;
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_open [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ snd_pcm_set_sync(substream);
+
+ snd_korg1212_OpenCard(korg1212);
+
+ runtime->hw = snd_korg1212_capture_info;
+ snd_pcm_set_runtime_buffer(substream, &korg1212->dma_rec);
+
+ spin_lock_irqsave(&korg1212->lock, flags);
+
+ korg1212->capture_substream = substream;
+ korg1212->capture_pid = current->pid;
+ korg1212->periodsize = K1212_PERIODS;
+ korg1212->channels = K1212_CHANNELS;
+
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+ return 0;
+}
+
+static int snd_korg1212_playback_close(snd_pcm_substream_t *substream)
+{
+ unsigned long flags;
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_close [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ snd_korg1212_silence(korg1212, 0, K1212_MAX_SAMPLES, 0, korg1212->channels * 2);
+
+ spin_lock_irqsave(&korg1212->lock, flags);
+
+ korg1212->playback_pid = -1;
+ korg1212->playback_substream = NULL;
+ korg1212->periodsize = 0;
+
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+
+ snd_korg1212_CloseCard(korg1212);
+ return 0;
+}
+
+static int snd_korg1212_capture_close(snd_pcm_substream_t *substream)
+{
+ unsigned long flags;
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_close [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ spin_lock_irqsave(&korg1212->lock, flags);
+
+ korg1212->capture_pid = -1;
+ korg1212->capture_substream = NULL;
+ korg1212->periodsize = 0;
+
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+
+ snd_korg1212_CloseCard(korg1212);
+ return 0;
+}
+
+static int snd_korg1212_ioctl(snd_pcm_substream_t *substream,
+ unsigned int cmd, void *arg)
+{
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_ioctl: cmd=%d\n", cmd);
+#endif
+
+ if (cmd == SNDRV_PCM_IOCTL1_CHANNEL_INFO ) {
+ snd_pcm_channel_info_t *info = arg;
+ info->offset = 0;
+ info->first = info->channel * 16;
+ info->step = 256;
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: channel_info %d:, offset=%ld, first=%d, step=%d\n", info->channel, info->offset, info->first, info->step);
+#endif
+ return 0;
+ }
+
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_korg1212_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *params)
+{
+ unsigned long flags;
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+ int err;
+ pid_t this_pid;
+ pid_t other_pid;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_hw_params [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ spin_lock_irqsave(&korg1212->lock, flags);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ this_pid = korg1212->playback_pid;
+ other_pid = korg1212->capture_pid;
+ } else {
+ this_pid = korg1212->capture_pid;
+ other_pid = korg1212->playback_pid;
+ }
+
+ if ((other_pid > 0) && (this_pid != other_pid)) {
+
+ /* The other stream is open, and not by the same
+ task as this one. Make sure that the parameters
+ that matter are the same.
+ */
+
+ if ((int)params_rate(params) != korg1212->clkRate) {
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+ return -EBUSY;
+ }
+
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+ return 0;
+ }
+
+ if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) {
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+ return err;
+ }
+
+ korg1212->channels = params_channels(params);
+ korg1212->periodsize = K1212_PERIOD_BYTES;
+
+ spin_unlock_irqrestore(&korg1212->lock, flags);
+
+ return 0;
+}
+
+static int snd_korg1212_prepare(snd_pcm_substream_t *substream)
+{
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+ int rc;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ spin_lock_irq(&korg1212->lock);
+
+ /* FIXME: we should wait for ack! */
+ if (korg1212->stop_pending_cnt > 0) {
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare - Stop is pending... [%s]\n", stateName[korg1212->cardState]);
+#endif
+ spin_unlock_irq(&korg1212->lock);
+ return -EAGAIN;
+ /*
+ korg1212->sharedBufferPtr->cardCommand = 0;
+ del_timer(&korg1212->timer);
+ korg1212->stop_pending_cnt = 0;
+ */
+ }
+
+ rc = snd_korg1212_SetupForPlay(korg1212);
+
+ korg1212->currentBuffer = 0;
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return rc ? -EINVAL : 0;
+}
+
+static int snd_korg1212_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+ int rc;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger [%s] cmd=%d\n", stateName[korg1212->cardState], cmd);
+#endif
+
+ spin_lock(&korg1212->lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+/*
+ if (korg1212->running) {
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger: Already running?\n");
+#endif
+ break;
+ }
+*/
+ korg1212->running++;
+ rc = snd_korg1212_TriggerPlay(korg1212);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+/*
+ if (!korg1212->running) {
+#if K1212_DEBUG_LEVEL > 1
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger: Already stopped?\n");
+#endif
+ break;
+ }
+*/
+ korg1212->running--;
+ rc = snd_korg1212_StopPlay(korg1212);
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+ spin_unlock(&korg1212->lock);
+ return rc ? -EINVAL : 0;
+}
+
+static snd_pcm_uframes_t snd_korg1212_playback_pointer(snd_pcm_substream_t *substream)
+{
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+ snd_pcm_uframes_t pos;
+
+ pos = korg1212->currentBuffer * kPlayBufferFrames;
+
+#if K1212_DEBUG_LEVEL > 2
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_pointer [%s] %ld\n",
+ stateName[korg1212->cardState], pos);
+#endif
+
+ return pos;
+}
+
+static snd_pcm_uframes_t snd_korg1212_capture_pointer(snd_pcm_substream_t *substream)
+{
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+ snd_pcm_uframes_t pos;
+
+ pos = korg1212->currentBuffer * kPlayBufferFrames;
+
+#if K1212_DEBUG_LEVEL > 2
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_pointer [%s] %ld\n",
+ stateName[korg1212->cardState], pos);
+#endif
+
+ return pos;
+}
+
+static int snd_korg1212_playback_copy(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *src,
+ snd_pcm_uframes_t count)
+{
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+
+#if K1212_DEBUG_LEVEL > 2
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count);
+#endif
+
+ return snd_korg1212_copy_from(korg1212, src, pos, count, 0, korg1212->channels * 2);
+
+}
+
+static int snd_korg1212_playback_silence(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_silence [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+ return snd_korg1212_silence(korg1212, pos, count, 0, korg1212->channels * 2);
+}
+
+static int snd_korg1212_capture_copy(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *dst,
+ snd_pcm_uframes_t count)
+{
+ korg1212_t *korg1212 = snd_pcm_substream_chip(substream);
+
+#if K1212_DEBUG_LEVEL > 2
+ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count);
+#endif
+
+ return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2);
+}
+
+static snd_pcm_ops_t snd_korg1212_playback_ops = {
+ .open = snd_korg1212_playback_open,
+ .close = snd_korg1212_playback_close,
+ .ioctl = snd_korg1212_ioctl,
+ .hw_params = snd_korg1212_hw_params,
+ .prepare = snd_korg1212_prepare,
+ .trigger = snd_korg1212_trigger,
+ .pointer = snd_korg1212_playback_pointer,
+ .copy = snd_korg1212_playback_copy,
+ .silence = snd_korg1212_playback_silence,
+};
+
+static snd_pcm_ops_t snd_korg1212_capture_ops = {
+ .open = snd_korg1212_capture_open,
+ .close = snd_korg1212_capture_close,
+ .ioctl = snd_korg1212_ioctl,
+ .hw_params = snd_korg1212_hw_params,
+ .prepare = snd_korg1212_prepare,
+ .trigger = snd_korg1212_trigger,
+ .pointer = snd_korg1212_capture_pointer,
+ .copy = snd_korg1212_capture_copy,
+};
+
+/*
+ * Control Interface
+ */
+
+static int snd_korg1212_control_phase_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+ return 0;
+}
+
+static int snd_korg1212_control_phase_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ int i = kcontrol->private_value;
+
+ spin_lock_irq(&korg1212->lock);
+
+ u->value.integer.value[0] = korg1212->volumePhase[i];
+
+ if (i >= 8)
+ u->value.integer.value[1] = korg1212->volumePhase[i+1];
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return 0;
+}
+
+static int snd_korg1212_control_phase_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+ int i, val;
+
+ spin_lock_irq(&korg1212->lock);
+
+ i = kcontrol->private_value;
+
+ korg1212->volumePhase[i] = u->value.integer.value[0];
+
+ val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value];
+
+ if ((u->value.integer.value[0] > 0) != (val < 0)) {
+ val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1);
+ korg1212->sharedBufferPtr->volumeData[i] = val;
+ change = 1;
+ }
+
+ if (i >= 8) {
+ korg1212->volumePhase[i+1] = u->value.integer.value[1];
+
+ val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1];
+
+ if ((u->value.integer.value[1] > 0) != (val < 0)) {
+ val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1);
+ korg1212->sharedBufferPtr->volumeData[i+1] = val;
+ change = 1;
+ }
+ }
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return change;
+}
+
+static int snd_korg1212_control_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+ uinfo->value.integer.min = k1212MinVolume;
+ uinfo->value.integer.max = k1212MaxVolume;
+ return 0;
+}
+
+static int snd_korg1212_control_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ spin_lock_irq(&korg1212->lock);
+
+ i = kcontrol->private_value;
+ u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]);
+
+ if (i >= 8)
+ u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]);
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return 0;
+}
+
+static int snd_korg1212_control_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+ int i;
+ int val;
+
+ spin_lock_irq(&korg1212->lock);
+
+ i = kcontrol->private_value;
+
+ if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) {
+ val = korg1212->volumePhase[i] > 0 ? -1 : 1;
+ val *= u->value.integer.value[0];
+ korg1212->sharedBufferPtr->volumeData[i] = val;
+ change = 1;
+ }
+
+ if (i >= 8) {
+ if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
+ val = korg1212->volumePhase[i+1] > 0 ? -1 : 1;
+ val *= u->value.integer.value[1];
+ korg1212->sharedBufferPtr->volumeData[i+1] = val;
+ change = 1;
+ }
+ }
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return change;
+}
+
+static int snd_korg1212_control_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+ uinfo->value.enumerated.items = kAudioChannels;
+ if (uinfo->value.enumerated.item > kAudioChannels-1) {
+ uinfo->value.enumerated.item = kAudioChannels-1;
+ }
+ strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_korg1212_control_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ spin_lock_irq(&korg1212->lock);
+
+ i = kcontrol->private_value;
+ u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i];
+
+ if (i >= 8)
+ u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1];
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return 0;
+}
+
+static int snd_korg1212_control_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ int change = 0, i;
+
+ spin_lock_irq(&korg1212->lock);
+
+ i = kcontrol->private_value;
+
+ if (u->value.enumerated.item[0] != (unsigned) korg1212->sharedBufferPtr->volumeData[i]) {
+ korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0];
+ change = 1;
+ }
+
+ if (i >= 8) {
+ if (u->value.enumerated.item[1] != (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) {
+ korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1];
+ change = 1;
+ }
+ }
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return change;
+}
+
+static int snd_korg1212_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = k1212MaxADCSens;
+ uinfo->value.integer.max = k1212MinADCSens;
+ return 0;
+}
+
+static int snd_korg1212_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&korg1212->lock);
+
+ u->value.integer.value[0] = korg1212->leftADCInSens;
+ u->value.integer.value[1] = korg1212->rightADCInSens;
+
+ spin_unlock_irq(&korg1212->lock);
+
+ return 0;
+}
+
+static int snd_korg1212_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+
+ spin_lock_irq(&korg1212->lock);
+
+ if (u->value.integer.value[0] != korg1212->leftADCInSens) {
+ korg1212->leftADCInSens = u->value.integer.value[0];
+ change = 1;
+ }
+ if (u->value.integer.value[1] != korg1212->rightADCInSens) {
+ korg1212->rightADCInSens = u->value.integer.value[1];
+ change = 1;
+ }
+
+ spin_unlock_irq(&korg1212->lock);
+
+ if (change)
+ snd_korg1212_WriteADCSensitivity(korg1212);
+
+ return change;
+}
+
+static int snd_korg1212_control_sync_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2) {
+ uinfo->value.enumerated.item = 2;
+ }
+ strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_korg1212_control_sync_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&korg1212->lock);
+
+ ucontrol->value.enumerated.item[0] = korg1212->clkSource;
+
+ spin_unlock_irq(&korg1212->lock);
+ return 0;
+}
+
+static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ucontrol->value.enumerated.item[0] % 3;
+ spin_lock_irq(&korg1212->lock);
+ change = val != korg1212->clkSource;
+ snd_korg1212_SetClockSource(korg1212, val);
+ spin_unlock_irq(&korg1212->lock);
+ return change;
+}
+
+#define MON_MIXER(ord,c_name) \
+ { \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = c_name " Monitor Volume", \
+ .info = snd_korg1212_control_volume_info, \
+ .get = snd_korg1212_control_volume_get, \
+ .put = snd_korg1212_control_volume_put, \
+ .private_value = ord, \
+ }, \
+ { \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = c_name " Monitor Route", \
+ .info = snd_korg1212_control_route_info, \
+ .get = snd_korg1212_control_route_get, \
+ .put = snd_korg1212_control_route_put, \
+ .private_value = ord, \
+ }, \
+ { \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+ .name = c_name " Monitor Phase Invert", \
+ .info = snd_korg1212_control_phase_info, \
+ .get = snd_korg1212_control_phase_get, \
+ .put = snd_korg1212_control_phase_put, \
+ .private_value = ord, \
+ }
+
+static snd_kcontrol_new_t snd_korg1212_controls[] = {
+ MON_MIXER(8, "Analog"),
+ MON_MIXER(10, "SPDIF"),
+ MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"),
+ MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"),
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Sync Source",
+ .info = snd_korg1212_control_sync_info,
+ .get = snd_korg1212_control_sync_get,
+ .put = snd_korg1212_control_sync_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Attenuation",
+ .info = snd_korg1212_control_info,
+ .get = snd_korg1212_control_get,
+ .put = snd_korg1212_control_put,
+ }
+};
+
+/*
+ * proc interface
+ */
+
+static void snd_korg1212_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ int n;
+ korg1212_t *korg1212 = (korg1212_t *)entry->private_data;
+
+ snd_iprintf(buffer, korg1212->card->longname);
+ snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1);
+ snd_iprintf(buffer, "\nGeneral settings\n");
+ snd_iprintf(buffer, " period size: %Zd bytes\n", K1212_PERIOD_BYTES);
+ snd_iprintf(buffer, " clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] );
+ snd_iprintf(buffer, " left ADC Sens: %d\n", korg1212->leftADCInSens );
+ snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens );
+ snd_iprintf(buffer, " Volume Info:\n");
+ for (n=0; n<kAudioChannels; n++)
+ snd_iprintf(buffer, " Channel %d: %s -> %s [%d]\n", n,
+ channelName[n],
+ channelName[korg1212->sharedBufferPtr->routeData[n]],
+ korg1212->sharedBufferPtr->volumeData[n]);
+ snd_iprintf(buffer, "\nGeneral status\n");
+ snd_iprintf(buffer, " ADAT Time Code: %d\n", korg1212->sharedBufferPtr->AdatTimeCode);
+ snd_iprintf(buffer, " Card State: %s\n", stateName[korg1212->cardState]);
+ snd_iprintf(buffer, "Idle mon. State: %d\n", korg1212->idleMonitorOn);
+ snd_iprintf(buffer, "Cmd retry count: %d\n", korg1212->cmdRetryCount);
+ snd_iprintf(buffer, " Irq count: %ld\n", korg1212->irqcount);
+ snd_iprintf(buffer, " Error count: %ld\n", korg1212->totalerrorcnt);
+}
+
+static void __devinit snd_korg1212_proc_init(korg1212_t *korg1212)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(korg1212->card, "korg1212", &entry))
+ snd_info_set_text_ops(entry, korg1212, 1024, snd_korg1212_proc_read);
+}
+
+static int
+snd_korg1212_free(korg1212_t *korg1212)
+{
+ snd_korg1212_TurnOffIdleMonitor(korg1212);
+
+ if (korg1212->irq >= 0) {
+ synchronize_irq(korg1212->irq);
+ snd_korg1212_DisableCardInterrupts(korg1212);
+ free_irq(korg1212->irq, (void *)korg1212);
+ korg1212->irq = -1;
+ }
+
+ if (korg1212->iobase != NULL) {
+ iounmap(korg1212->iobase);
+ korg1212->iobase = NULL;
+ }
+
+ pci_release_regions(korg1212->pci);
+
+ // ----------------------------------------------------
+ // free up memory resources used for the DSP download.
+ // ----------------------------------------------------
+ if (korg1212->dma_dsp.area) {
+ snd_dma_free_pages(&korg1212->dma_dsp);
+ korg1212->dma_dsp.area = NULL;
+ }
+
+#ifndef K1212_LARGEALLOC
+
+ // ------------------------------------------------------
+ // free up memory resources used for the Play/Rec Buffers
+ // ------------------------------------------------------
+ if (korg1212->dma_play.area) {
+ snd_dma_free_pages(&korg1212->dma_play);
+ korg1212->dma_play.area = NULL;
+ }
+
+ if (korg1212->dma_rec.area) {
+ snd_dma_free_pages(&korg1212->dma_rec);
+ korg1212->dma_rec.area = NULL;
+ }
+
+#endif
+
+ // ----------------------------------------------------
+ // free up memory resources used for the Shared Buffers
+ // ----------------------------------------------------
+ if (korg1212->dma_shared.area) {
+ snd_dma_free_pages(&korg1212->dma_shared);
+ korg1212->dma_shared.area = NULL;
+ }
+
+ pci_disable_device(korg1212->pci);
+ kfree(korg1212);
+ return 0;
+}
+
+static int snd_korg1212_dev_free(snd_device_t *device)
+{
+ korg1212_t *korg1212 = device->device_data;
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Freeing device\n");
+#endif
+ return snd_korg1212_free(korg1212);
+}
+
+static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci,
+ korg1212_t ** rchip)
+
+{
+ int err;
+ unsigned int i;
+ unsigned ioport_size, iomem_size, iomem2_size;
+ korg1212_t * korg1212;
+
+ static snd_device_ops_t ops = {
+ .dev_free = snd_korg1212_dev_free,
+ };
+
+ * rchip = NULL;
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ korg1212 = kcalloc(1, sizeof(*korg1212), GFP_KERNEL);
+ if (korg1212 == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ korg1212->card = card;
+ korg1212->pci = pci;
+
+ init_waitqueue_head(&korg1212->wait);
+ spin_lock_init(&korg1212->lock);
+ init_MUTEX(&korg1212->open_mutex);
+ init_timer(&korg1212->timer);
+ korg1212->timer.function = snd_korg1212_timer_func;
+ korg1212->timer.data = (unsigned long)korg1212;
+
+ korg1212->irq = -1;
+ korg1212->clkSource = K1212_CLKIDX_Local;
+ korg1212->clkRate = 44100;
+ korg1212->inIRQ = 0;
+ korg1212->running = 0;
+ korg1212->opencnt = 0;
+ korg1212->playcnt = 0;
+ korg1212->setcnt = 0;
+ korg1212->totalerrorcnt = 0;
+ korg1212->playback_pid = -1;
+ korg1212->capture_pid = -1;
+ snd_korg1212_setCardState(korg1212, K1212_STATE_UNINITIALIZED);
+ korg1212->idleMonitorOn = 0;
+ korg1212->clkSrcRate = K1212_CLKIDX_LocalAt44_1K;
+ korg1212->leftADCInSens = k1212MaxADCSens;
+ korg1212->rightADCInSens = k1212MaxADCSens;
+
+ for (i=0; i<kAudioChannels; i++)
+ korg1212->volumePhase[i] = 0;
+
+ if ((err = pci_request_regions(pci, "korg1212")) < 0) {
+ kfree(korg1212);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ korg1212->iomem = pci_resource_start(korg1212->pci, 0);
+ korg1212->ioport = pci_resource_start(korg1212->pci, 1);
+ korg1212->iomem2 = pci_resource_start(korg1212->pci, 2);
+
+ iomem_size = pci_resource_len(korg1212->pci, 0);
+ ioport_size = pci_resource_len(korg1212->pci, 1);
+ iomem2_size = pci_resource_len(korg1212->pci, 2);
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: resources:\n"
+ " iomem = 0x%lx (%d)\n"
+ " ioport = 0x%lx (%d)\n"
+ " iomem = 0x%lx (%d)\n"
+ " [%s]\n",
+ korg1212->iomem, iomem_size,
+ korg1212->ioport, ioport_size,
+ korg1212->iomem2, iomem2_size,
+ stateName[korg1212->cardState]);
+#endif
+
+ if ((korg1212->iobase = ioremap(korg1212->iomem, iomem_size)) == NULL) {
+ snd_printk(KERN_ERR "korg1212: unable to remap memory region 0x%lx-0x%lx\n", korg1212->iomem,
+ korg1212->iomem + iomem_size - 1);
+ snd_korg1212_free(korg1212);
+ return -EBUSY;
+ }
+
+ err = request_irq(pci->irq, snd_korg1212_interrupt,
+ SA_INTERRUPT|SA_SHIRQ,
+ "korg1212", (void *) korg1212);
+
+ if (err) {
+ snd_printk(KERN_ERR "korg1212: unable to grab IRQ %d\n", pci->irq);
+ snd_korg1212_free(korg1212);
+ return -EBUSY;
+ }
+
+ korg1212->irq = pci->irq;
+
+ pci_set_master(korg1212->pci);
+
+ korg1212->statusRegPtr = (u32 __iomem *) (korg1212->iobase + STATUS_REG_OFFSET);
+ korg1212->outDoorbellPtr = (u32 __iomem *) (korg1212->iobase + OUT_DOORBELL_OFFSET);
+ korg1212->inDoorbellPtr = (u32 __iomem *) (korg1212->iobase + IN_DOORBELL_OFFSET);
+ korg1212->mailbox0Ptr = (u32 __iomem *) (korg1212->iobase + MAILBOX0_OFFSET);
+ korg1212->mailbox1Ptr = (u32 __iomem *) (korg1212->iobase + MAILBOX1_OFFSET);
+ korg1212->mailbox2Ptr = (u32 __iomem *) (korg1212->iobase + MAILBOX2_OFFSET);
+ korg1212->mailbox3Ptr = (u32 __iomem *) (korg1212->iobase + MAILBOX3_OFFSET);
+ korg1212->controlRegPtr = (u32 __iomem *) (korg1212->iobase + PCI_CONTROL_OFFSET);
+ korg1212->sensRegPtr = (u16 __iomem *) (korg1212->iobase + SENS_CONTROL_OFFSET);
+ korg1212->idRegPtr = (u32 __iomem *) (korg1212->iobase + DEV_VEND_ID_OFFSET);
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: card registers:\n"
+ " Status register = 0x%p\n"
+ " OutDoorbell = 0x%p\n"
+ " InDoorbell = 0x%p\n"
+ " Mailbox0 = 0x%p\n"
+ " Mailbox1 = 0x%p\n"
+ " Mailbox2 = 0x%p\n"
+ " Mailbox3 = 0x%p\n"
+ " ControlReg = 0x%p\n"
+ " SensReg = 0x%p\n"
+ " IDReg = 0x%p\n"
+ " [%s]\n",
+ korg1212->statusRegPtr,
+ korg1212->outDoorbellPtr,
+ korg1212->inDoorbellPtr,
+ korg1212->mailbox0Ptr,
+ korg1212->mailbox1Ptr,
+ korg1212->mailbox2Ptr,
+ korg1212->mailbox3Ptr,
+ korg1212->controlRegPtr,
+ korg1212->sensRegPtr,
+ korg1212->idRegPtr,
+ stateName[korg1212->cardState]);
+#endif
+
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ sizeof(KorgSharedBuffer), &korg1212->dma_shared) < 0) {
+ snd_printk(KERN_ERR "korg1212: can not allocate shared buffer memory (%Zd bytes)\n", sizeof(KorgSharedBuffer));
+ snd_korg1212_free(korg1212);
+ return -ENOMEM;
+ }
+ korg1212->sharedBufferPtr = (KorgSharedBuffer *)korg1212->dma_shared.area;
+ korg1212->sharedBufferPhy = korg1212->dma_shared.addr;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer));
+#endif
+
+#ifndef K1212_LARGEALLOC
+
+ korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers;
+
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ korg1212->DataBufsSize, &korg1212->dma_play) < 0) {
+ snd_printk(KERN_ERR "korg1212: can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
+ snd_korg1212_free(korg1212);
+ return -ENOMEM;
+ }
+ korg1212->playDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_play.area;
+ korg1212->PlayDataPhy = korg1212->dma_play.addr;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n",
+ korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize);
+#endif
+
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ korg1212->DataBufsSize, &korg1212->dma_rec) < 0) {
+ snd_printk(KERN_ERR "korg1212: can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
+ snd_korg1212_free(korg1212);
+ return -ENOMEM;
+ }
+ korg1212->recordDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_rec.area;
+ korg1212->RecDataPhy = korg1212->dma_rec.addr;
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n",
+ korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize);
+#endif
+
+#else // K1212_LARGEALLOC
+
+ korg1212->recordDataBufsPtr = korg1212->sharedBufferPtr->recordDataBufs;
+ korg1212->playDataBufsPtr = korg1212->sharedBufferPtr->playDataBufs;
+ korg1212->PlayDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->playDataBufs;
+ korg1212->RecDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->recordDataBufs;
+
+#endif // K1212_LARGEALLOC
+
+ korg1212->dspCodeSize = sizeof (dspCode);
+
+ korg1212->VolumeTablePhy = korg1212->sharedBufferPhy +
+ offsetof(KorgSharedBuffer, volumeData);
+ korg1212->RoutingTablePhy = korg1212->sharedBufferPhy +
+ offsetof(KorgSharedBuffer, routeData);
+ korg1212->AdatTimeCodePhy = korg1212->sharedBufferPhy +
+ offsetof(KorgSharedBuffer, AdatTimeCode);
+
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) {
+ snd_printk(KERN_ERR "korg1212: can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize);
+ snd_korg1212_free(korg1212);
+ return -ENOMEM;
+ }
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n",
+ korg1212->dma_dsp.area, korg1212->dma_dsp.addr, korg1212->dspCodeSize,
+ stateName[korg1212->cardState]);
+#endif
+
+ rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0);
+
+#if K1212_DEBUG_LEVEL > 0
+ if (rc) K1212_DEBUG_PRINTK("K1212_DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, korg1212, &ops)) < 0) {
+ snd_korg1212_free(korg1212);
+ return err;
+ }
+
+ snd_korg1212_EnableCardInterrupts(korg1212);
+
+ mdelay(CARD_BOOT_DELAY_IN_MS);
+
+ if (snd_korg1212_downloadDSPCode(korg1212))
+ return -EBUSY;
+
+ snd_printk(KERN_ERR
+ "korg1212: dspMemPhy = %08x U[%08x], "
+ "PlayDataPhy = %08x L[%08x]\n"
+ "korg1212: RecDataPhy = %08x L[%08x], "
+ "VolumeTablePhy = %08x L[%08x]\n"
+ "korg1212: RoutingTablePhy = %08x L[%08x], "
+ "AdatTimeCodePhy = %08x L[%08x]\n",
+ (int)korg1212->dma_dsp.addr, UpperWordSwap(korg1212->dma_dsp.addr),
+ korg1212->PlayDataPhy, LowerWordSwap(korg1212->PlayDataPhy),
+ korg1212->RecDataPhy, LowerWordSwap(korg1212->RecDataPhy),
+ korg1212->VolumeTablePhy, LowerWordSwap(korg1212->VolumeTablePhy),
+ korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy),
+ korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy));
+
+ if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm)) < 0)
+ return err;
+
+ korg1212->pcm->private_data = korg1212;
+ korg1212->pcm->private_free = snd_korg1212_free_pcm;
+ strcpy(korg1212->pcm->name, "korg1212");
+
+ snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops);
+
+ snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_korg1212_capture_ops);
+
+ korg1212->pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+ //snd_pcm_lib_preallocate_pages_for_all(korg1212->pcm,
+ // K1212_MAX_BUF_SIZE, K1212_MAX_BUF_SIZE, GFP_KERNEL);
+
+ for (i = 0; i < ARRAY_SIZE(snd_korg1212_controls); i++) {
+ err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212));
+ if (err < 0)
+ return err;
+ }
+
+ snd_korg1212_proc_init(korg1212);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ * rchip = korg1212;
+ return 0;
+
+}
+
+/*
+ * Card initialisation
+ */
+
+static int __devinit
+snd_korg1212_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ korg1212_t *korg1212;
+ snd_card_t *card;
+ int err;
+
+ if (dev >= SNDRV_CARDS) {
+ return -ENODEV;
+ }
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_korg1212_create(card, pci, &korg1212)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "korg1212");
+ strcpy(card->shortname, "korg1212");
+ sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
+ korg1212->iomem, korg1212->irq);
+
+#if K1212_DEBUG_LEVEL > 0
+ K1212_DEBUG_PRINTK("K1212_DEBUG: %s\n", card->longname);
+#endif
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_korg1212_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "korg1212",
+ .id_table = snd_korg1212_ids,
+ .probe = snd_korg1212_probe,
+ .remove = __devexit_p(snd_korg1212_remove),
+};
+
+static int __init alsa_card_korg1212_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_korg1212_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_korg1212_init)
+module_exit(alsa_card_korg1212_exit)
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
new file mode 100644
index 00000000000..2cf33083d7c
--- /dev/null
+++ b/sound/pci/maestro3.c
@@ -0,0 +1,2714 @@
+/*
+ * Driver for ESS Maestro3/Allegro (ES1988) soundcards.
+ * Copyright (c) 2000 by Zach Brown <zab@zabbo.net>
+ * Takashi Iwai <tiwai@suse.de>
+ *
+ * Most of the hardware init stuffs are based on maestro3 driver for
+ * OSS/Free by Zach Brown. Many thanks to Zach!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * ChangeLog:
+ * Aug. 27, 2001
+ * - Fixed deadlock on capture
+ * - Added Canyon3D-2 support by Rob Riggs <rob@pangalactic.org>
+ *
+ */
+
+#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2"
+#define DRIVER_NAME "Maestro3"
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ESS Maestro3 PCI");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ESS,Maestro3 PCI},"
+ "{ESS,ES1988},"
+ "{ESS,Allegro PCI},"
+ "{ESS,Allegro-1 PCI},"
+ "{ESS,Canyon3D-2/LE PCI}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */
+static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static int amp_gpio[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this soundcard.");
+module_param_array(external_amp, bool, NULL, 0444);
+MODULE_PARM_DESC(external_amp, "Enable external amp for " CARD_NAME " soundcard.");
+module_param_array(amp_gpio, int, NULL, 0444);
+MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)");
+
+#define MAX_PLAYBACKS 2
+#define MAX_CAPTURES 1
+#define NR_DSPS (MAX_PLAYBACKS + MAX_CAPTURES)
+
+
+/*
+ * maestro3 registers
+ */
+
+/* Allegro PCI configuration registers */
+#define PCI_LEGACY_AUDIO_CTRL 0x40
+#define SOUND_BLASTER_ENABLE 0x00000001
+#define FM_SYNTHESIS_ENABLE 0x00000002
+#define GAME_PORT_ENABLE 0x00000004
+#define MPU401_IO_ENABLE 0x00000008
+#define MPU401_IRQ_ENABLE 0x00000010
+#define ALIAS_10BIT_IO 0x00000020
+#define SB_DMA_MASK 0x000000C0
+#define SB_DMA_0 0x00000040
+#define SB_DMA_1 0x00000040
+#define SB_DMA_R 0x00000080
+#define SB_DMA_3 0x000000C0
+#define SB_IRQ_MASK 0x00000700
+#define SB_IRQ_5 0x00000000
+#define SB_IRQ_7 0x00000100
+#define SB_IRQ_9 0x00000200
+#define SB_IRQ_10 0x00000300
+#define MIDI_IRQ_MASK 0x00003800
+#define SERIAL_IRQ_ENABLE 0x00004000
+#define DISABLE_LEGACY 0x00008000
+
+#define PCI_ALLEGRO_CONFIG 0x50
+#define SB_ADDR_240 0x00000004
+#define MPU_ADDR_MASK 0x00000018
+#define MPU_ADDR_330 0x00000000
+#define MPU_ADDR_300 0x00000008
+#define MPU_ADDR_320 0x00000010
+#define MPU_ADDR_340 0x00000018
+#define USE_PCI_TIMING 0x00000040
+#define POSTED_WRITE_ENABLE 0x00000080
+#define DMA_POLICY_MASK 0x00000700
+#define DMA_DDMA 0x00000000
+#define DMA_TDMA 0x00000100
+#define DMA_PCPCI 0x00000200
+#define DMA_WBDMA16 0x00000400
+#define DMA_WBDMA4 0x00000500
+#define DMA_WBDMA2 0x00000600
+#define DMA_WBDMA1 0x00000700
+#define DMA_SAFE_GUARD 0x00000800
+#define HI_PERF_GP_ENABLE 0x00001000
+#define PIC_SNOOP_MODE_0 0x00002000
+#define PIC_SNOOP_MODE_1 0x00004000
+#define SOUNDBLASTER_IRQ_MASK 0x00008000
+#define RING_IN_ENABLE 0x00010000
+#define SPDIF_TEST_MODE 0x00020000
+#define CLK_MULT_MODE_SELECT_2 0x00040000
+#define EEPROM_WRITE_ENABLE 0x00080000
+#define CODEC_DIR_IN 0x00100000
+#define HV_BUTTON_FROM_GD 0x00200000
+#define REDUCED_DEBOUNCE 0x00400000
+#define HV_CTRL_ENABLE 0x00800000
+#define SPDIF_ENABLE 0x01000000
+#define CLK_DIV_SELECT 0x06000000
+#define CLK_DIV_BY_48 0x00000000
+#define CLK_DIV_BY_49 0x02000000
+#define CLK_DIV_BY_50 0x04000000
+#define CLK_DIV_RESERVED 0x06000000
+#define PM_CTRL_ENABLE 0x08000000
+#define CLK_MULT_MODE_SELECT 0x30000000
+#define CLK_MULT_MODE_SHIFT 28
+#define CLK_MULT_MODE_0 0x00000000
+#define CLK_MULT_MODE_1 0x10000000
+#define CLK_MULT_MODE_2 0x20000000
+#define CLK_MULT_MODE_3 0x30000000
+#define INT_CLK_SELECT 0x40000000
+#define INT_CLK_MULT_RESET 0x80000000
+
+/* M3 */
+#define INT_CLK_SRC_NOT_PCI 0x00100000
+#define INT_CLK_MULT_ENABLE 0x80000000
+
+#define PCI_ACPI_CONTROL 0x54
+#define PCI_ACPI_D0 0x00000000
+#define PCI_ACPI_D1 0xB4F70000
+#define PCI_ACPI_D2 0xB4F7B4F7
+
+#define PCI_USER_CONFIG 0x58
+#define EXT_PCI_MASTER_ENABLE 0x00000001
+#define SPDIF_OUT_SELECT 0x00000002
+#define TEST_PIN_DIR_CTRL 0x00000004
+#define AC97_CODEC_TEST 0x00000020
+#define TRI_STATE_BUFFER 0x00000080
+#define IN_CLK_12MHZ_SELECT 0x00000100
+#define MULTI_FUNC_DISABLE 0x00000200
+#define EXT_MASTER_PAIR_SEL 0x00000400
+#define PCI_MASTER_SUPPORT 0x00000800
+#define STOP_CLOCK_ENABLE 0x00001000
+#define EAPD_DRIVE_ENABLE 0x00002000
+#define REQ_TRI_STATE_ENABLE 0x00004000
+#define REQ_LOW_ENABLE 0x00008000
+#define MIDI_1_ENABLE 0x00010000
+#define MIDI_2_ENABLE 0x00020000
+#define SB_AUDIO_SYNC 0x00040000
+#define HV_CTRL_TEST 0x00100000
+#define SOUNDBLASTER_TEST 0x00400000
+
+#define PCI_USER_CONFIG_C 0x5C
+
+#define PCI_DDMA_CTRL 0x60
+#define DDMA_ENABLE 0x00000001
+
+
+/* Allegro registers */
+#define HOST_INT_CTRL 0x18
+#define SB_INT_ENABLE 0x0001
+#define MPU401_INT_ENABLE 0x0002
+#define ASSP_INT_ENABLE 0x0010
+#define RING_INT_ENABLE 0x0020
+#define HV_INT_ENABLE 0x0040
+#define CLKRUN_GEN_ENABLE 0x0100
+#define HV_CTRL_TO_PME 0x0400
+#define SOFTWARE_RESET_ENABLE 0x8000
+
+/*
+ * should be using the above defines, probably.
+ */
+#define REGB_ENABLE_RESET 0x01
+#define REGB_STOP_CLOCK 0x10
+
+#define HOST_INT_STATUS 0x1A
+#define SB_INT_PENDING 0x01
+#define MPU401_INT_PENDING 0x02
+#define ASSP_INT_PENDING 0x10
+#define RING_INT_PENDING 0x20
+#define HV_INT_PENDING 0x40
+
+#define HARDWARE_VOL_CTRL 0x1B
+#define SHADOW_MIX_REG_VOICE 0x1C
+#define HW_VOL_COUNTER_VOICE 0x1D
+#define SHADOW_MIX_REG_MASTER 0x1E
+#define HW_VOL_COUNTER_MASTER 0x1F
+
+#define CODEC_COMMAND 0x30
+#define CODEC_READ_B 0x80
+
+#define CODEC_STATUS 0x30
+#define CODEC_BUSY_B 0x01
+
+#define CODEC_DATA 0x32
+
+#define RING_BUS_CTRL_A 0x36
+#define RAC_PME_ENABLE 0x0100
+#define RAC_SDFS_ENABLE 0x0200
+#define LAC_PME_ENABLE 0x0400
+#define LAC_SDFS_ENABLE 0x0800
+#define SERIAL_AC_LINK_ENABLE 0x1000
+#define IO_SRAM_ENABLE 0x2000
+#define IIS_INPUT_ENABLE 0x8000
+
+#define RING_BUS_CTRL_B 0x38
+#define SECOND_CODEC_ID_MASK 0x0003
+#define SPDIF_FUNC_ENABLE 0x0010
+#define SECOND_AC_ENABLE 0x0020
+#define SB_MODULE_INTF_ENABLE 0x0040
+#define SSPE_ENABLE 0x0040
+#define M3I_DOCK_ENABLE 0x0080
+
+#define SDO_OUT_DEST_CTRL 0x3A
+#define COMMAND_ADDR_OUT 0x0003
+#define PCM_LR_OUT_LOCAL 0x0000
+#define PCM_LR_OUT_REMOTE 0x0004
+#define PCM_LR_OUT_MUTE 0x0008
+#define PCM_LR_OUT_BOTH 0x000C
+#define LINE1_DAC_OUT_LOCAL 0x0000
+#define LINE1_DAC_OUT_REMOTE 0x0010
+#define LINE1_DAC_OUT_MUTE 0x0020
+#define LINE1_DAC_OUT_BOTH 0x0030
+#define PCM_CLS_OUT_LOCAL 0x0000
+#define PCM_CLS_OUT_REMOTE 0x0040
+#define PCM_CLS_OUT_MUTE 0x0080
+#define PCM_CLS_OUT_BOTH 0x00C0
+#define PCM_RLF_OUT_LOCAL 0x0000
+#define PCM_RLF_OUT_REMOTE 0x0100
+#define PCM_RLF_OUT_MUTE 0x0200
+#define PCM_RLF_OUT_BOTH 0x0300
+#define LINE2_DAC_OUT_LOCAL 0x0000
+#define LINE2_DAC_OUT_REMOTE 0x0400
+#define LINE2_DAC_OUT_MUTE 0x0800
+#define LINE2_DAC_OUT_BOTH 0x0C00
+#define HANDSET_OUT_LOCAL 0x0000
+#define HANDSET_OUT_REMOTE 0x1000
+#define HANDSET_OUT_MUTE 0x2000
+#define HANDSET_OUT_BOTH 0x3000
+#define IO_CTRL_OUT_LOCAL 0x0000
+#define IO_CTRL_OUT_REMOTE 0x4000
+#define IO_CTRL_OUT_MUTE 0x8000
+#define IO_CTRL_OUT_BOTH 0xC000
+
+#define SDO_IN_DEST_CTRL 0x3C
+#define STATUS_ADDR_IN 0x0003
+#define PCM_LR_IN_LOCAL 0x0000
+#define PCM_LR_IN_REMOTE 0x0004
+#define PCM_LR_RESERVED 0x0008
+#define PCM_LR_IN_BOTH 0x000C
+#define LINE1_ADC_IN_LOCAL 0x0000
+#define LINE1_ADC_IN_REMOTE 0x0010
+#define LINE1_ADC_IN_MUTE 0x0020
+#define MIC_ADC_IN_LOCAL 0x0000
+#define MIC_ADC_IN_REMOTE 0x0040
+#define MIC_ADC_IN_MUTE 0x0080
+#define LINE2_DAC_IN_LOCAL 0x0000
+#define LINE2_DAC_IN_REMOTE 0x0400
+#define LINE2_DAC_IN_MUTE 0x0800
+#define HANDSET_IN_LOCAL 0x0000
+#define HANDSET_IN_REMOTE 0x1000
+#define HANDSET_IN_MUTE 0x2000
+#define IO_STATUS_IN_LOCAL 0x0000
+#define IO_STATUS_IN_REMOTE 0x4000
+
+#define SPDIF_IN_CTRL 0x3E
+#define SPDIF_IN_ENABLE 0x0001
+
+#define GPIO_DATA 0x60
+#define GPIO_DATA_MASK 0x0FFF
+#define GPIO_HV_STATUS 0x3000
+#define GPIO_PME_STATUS 0x4000
+
+#define GPIO_MASK 0x64
+#define GPIO_DIRECTION 0x68
+#define GPO_PRIMARY_AC97 0x0001
+#define GPI_LINEOUT_SENSE 0x0004
+#define GPO_SECONDARY_AC97 0x0008
+#define GPI_VOL_DOWN 0x0010
+#define GPI_VOL_UP 0x0020
+#define GPI_IIS_CLK 0x0040
+#define GPI_IIS_LRCLK 0x0080
+#define GPI_IIS_DATA 0x0100
+#define GPI_DOCKING_STATUS 0x0100
+#define GPI_HEADPHONE_SENSE 0x0200
+#define GPO_EXT_AMP_SHUTDOWN 0x1000
+
+#define GPO_EXT_AMP_M3 1 /* default m3 amp */
+#define GPO_EXT_AMP_ALLEGRO 8 /* default allegro amp */
+
+/* M3 */
+#define GPO_M3_EXT_AMP_SHUTDN 0x0002
+
+#define ASSP_INDEX_PORT 0x80
+#define ASSP_MEMORY_PORT 0x82
+#define ASSP_DATA_PORT 0x84
+
+#define MPU401_DATA_PORT 0x98
+#define MPU401_STATUS_PORT 0x99
+
+#define CLK_MULT_DATA_PORT 0x9C
+
+#define ASSP_CONTROL_A 0xA2
+#define ASSP_0_WS_ENABLE 0x01
+#define ASSP_CTRL_A_RESERVED1 0x02
+#define ASSP_CTRL_A_RESERVED2 0x04
+#define ASSP_CLK_49MHZ_SELECT 0x08
+#define FAST_PLU_ENABLE 0x10
+#define ASSP_CTRL_A_RESERVED3 0x20
+#define DSP_CLK_36MHZ_SELECT 0x40
+
+#define ASSP_CONTROL_B 0xA4
+#define RESET_ASSP 0x00
+#define RUN_ASSP 0x01
+#define ENABLE_ASSP_CLOCK 0x00
+#define STOP_ASSP_CLOCK 0x10
+#define RESET_TOGGLE 0x40
+
+#define ASSP_CONTROL_C 0xA6
+#define ASSP_HOST_INT_ENABLE 0x01
+#define FM_ADDR_REMAP_DISABLE 0x02
+#define HOST_WRITE_PORT_ENABLE 0x08
+
+#define ASSP_HOST_INT_STATUS 0xAC
+#define DSP2HOST_REQ_PIORECORD 0x01
+#define DSP2HOST_REQ_I2SRATE 0x02
+#define DSP2HOST_REQ_TIMER 0x04
+
+/* AC97 registers */
+/* XXX fix this crap up */
+/*#define AC97_RESET 0x00*/
+
+#define AC97_VOL_MUTE_B 0x8000
+#define AC97_VOL_M 0x1F
+#define AC97_LEFT_VOL_S 8
+
+#define AC97_MASTER_VOL 0x02
+#define AC97_LINE_LEVEL_VOL 0x04
+#define AC97_MASTER_MONO_VOL 0x06
+#define AC97_PC_BEEP_VOL 0x0A
+#define AC97_PC_BEEP_VOL_M 0x0F
+#define AC97_SROUND_MASTER_VOL 0x38
+#define AC97_PC_BEEP_VOL_S 1
+
+/*#define AC97_PHONE_VOL 0x0C
+#define AC97_MIC_VOL 0x0E*/
+#define AC97_MIC_20DB_ENABLE 0x40
+
+/*#define AC97_LINEIN_VOL 0x10
+#define AC97_CD_VOL 0x12
+#define AC97_VIDEO_VOL 0x14
+#define AC97_AUX_VOL 0x16*/
+#define AC97_PCM_OUT_VOL 0x18
+/*#define AC97_RECORD_SELECT 0x1A*/
+#define AC97_RECORD_MIC 0x00
+#define AC97_RECORD_CD 0x01
+#define AC97_RECORD_VIDEO 0x02
+#define AC97_RECORD_AUX 0x03
+#define AC97_RECORD_MONO_MUX 0x02
+#define AC97_RECORD_DIGITAL 0x03
+#define AC97_RECORD_LINE 0x04
+#define AC97_RECORD_STEREO 0x05
+#define AC97_RECORD_MONO 0x06
+#define AC97_RECORD_PHONE 0x07
+
+/*#define AC97_RECORD_GAIN 0x1C*/
+#define AC97_RECORD_VOL_M 0x0F
+
+/*#define AC97_GENERAL_PURPOSE 0x20*/
+#define AC97_POWER_DOWN_CTRL 0x26
+#define AC97_ADC_READY 0x0001
+#define AC97_DAC_READY 0x0002
+#define AC97_ANALOG_READY 0x0004
+#define AC97_VREF_ON 0x0008
+#define AC97_PR0 0x0100
+#define AC97_PR1 0x0200
+#define AC97_PR2 0x0400
+#define AC97_PR3 0x0800
+#define AC97_PR4 0x1000
+
+#define AC97_RESERVED1 0x28
+
+#define AC97_VENDOR_TEST 0x5A
+
+#define AC97_CLOCK_DELAY 0x5C
+#define AC97_LINEOUT_MUX_SEL 0x0001
+#define AC97_MONO_MUX_SEL 0x0002
+#define AC97_CLOCK_DELAY_SEL 0x1F
+#define AC97_DAC_CDS_SHIFT 6
+#define AC97_ADC_CDS_SHIFT 11
+
+#define AC97_MULTI_CHANNEL_SEL 0x74
+
+/*#define AC97_VENDOR_ID1 0x7C
+#define AC97_VENDOR_ID2 0x7E*/
+
+/*
+ * ASSP control regs
+ */
+#define DSP_PORT_TIMER_COUNT 0x06
+
+#define DSP_PORT_MEMORY_INDEX 0x80
+
+#define DSP_PORT_MEMORY_TYPE 0x82
+#define MEMTYPE_INTERNAL_CODE 0x0002
+#define MEMTYPE_INTERNAL_DATA 0x0003
+#define MEMTYPE_MASK 0x0003
+
+#define DSP_PORT_MEMORY_DATA 0x84
+
+#define DSP_PORT_CONTROL_REG_A 0xA2
+#define DSP_PORT_CONTROL_REG_B 0xA4
+#define DSP_PORT_CONTROL_REG_C 0xA6
+
+#define REV_A_CODE_MEMORY_BEGIN 0x0000
+#define REV_A_CODE_MEMORY_END 0x0FFF
+#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040
+#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1)
+
+#define REV_B_CODE_MEMORY_BEGIN 0x0000
+#define REV_B_CODE_MEMORY_END 0x0BFF
+#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040
+#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1)
+
+#define REV_A_DATA_MEMORY_BEGIN 0x1000
+#define REV_A_DATA_MEMORY_END 0x2FFF
+#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080
+#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1)
+
+#define REV_B_DATA_MEMORY_BEGIN 0x1000
+#define REV_B_DATA_MEMORY_END 0x2BFF
+#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080
+#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1)
+
+
+#define NUM_UNITS_KERNEL_CODE 16
+#define NUM_UNITS_KERNEL_DATA 2
+
+#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16
+#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5
+
+/*
+ * Kernel data layout
+ */
+
+#define DP_SHIFT_COUNT 7
+
+#define KDATA_BASE_ADDR 0x1000
+#define KDATA_BASE_ADDR2 0x1080
+
+#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000)
+#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001)
+#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002)
+#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003)
+#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004)
+#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005)
+#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006)
+#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007)
+#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008)
+
+#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009)
+#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A)
+
+#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B)
+#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C)
+#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D)
+#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E)
+#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F)
+#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010)
+#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011)
+#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012)
+#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013)
+#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014)
+
+#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015)
+#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016)
+
+#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017)
+#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018)
+
+#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019)
+#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A)
+
+#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B)
+#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C)
+#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D)
+
+#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E)
+#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F)
+#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020)
+#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021)
+#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022)
+
+#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023)
+#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024)
+#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025)
+
+#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026)
+#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027)
+#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028)
+
+#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029)
+#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A)
+#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B)
+#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C)
+#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D)
+#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E)
+#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F)
+#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030)
+#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031)
+#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032)
+
+#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033)
+#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034)
+#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035)
+
+#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036)
+#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037)
+
+#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038)
+#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039)
+#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A)
+
+#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B)
+#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C)
+#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D)
+#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E)
+#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F)
+#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040)
+
+#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041)
+#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042)
+#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043)
+#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044)
+#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045)
+#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046)
+
+#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047)
+#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048)
+#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049)
+#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A)
+#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B)
+#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C)
+
+#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D)
+#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E)
+#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F)
+#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050)
+
+#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051)
+#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052)
+
+#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053)
+#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054)
+
+#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055)
+#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056)
+#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057)
+#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058)
+#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059)
+
+#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A)
+#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B)
+
+#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C)
+#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D)
+#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E)
+
+#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F)
+#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060)
+
+#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061)
+
+#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062)
+#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063)
+#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064)
+#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065)
+#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066)
+#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067)
+#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068)
+#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069)
+#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A)
+#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B)
+#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C)
+#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D)
+
+#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E)
+#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F)
+#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070)
+#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071)
+
+#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072)
+#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073)
+
+#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074)
+#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075)
+#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076)
+#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077)
+
+#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078)
+#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079)
+#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A)
+#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B)
+#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C)
+
+/*
+ * second 'segment' (?) reserved for mixer
+ * buffers..
+ */
+
+#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000)
+#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001)
+#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002)
+#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003)
+#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004)
+#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005)
+#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006)
+#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007)
+#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008)
+#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009)
+#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A)
+#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B)
+#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C)
+#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D)
+#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E)
+#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F)
+
+#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010)
+#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011)
+#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012)
+#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013)
+#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014)
+#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015)
+#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016)
+#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017)
+#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018)
+#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019)
+#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A)
+
+#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B)
+#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C)
+#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D)
+#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E)
+#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F)
+#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020)
+
+#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC)
+#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0)
+#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0)
+#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0)
+
+/*
+ * client data area offsets
+ */
+#define CDATA_INSTANCE_READY 0x00
+
+#define CDATA_HOST_SRC_ADDRL 0x01
+#define CDATA_HOST_SRC_ADDRH 0x02
+#define CDATA_HOST_SRC_END_PLUS_1L 0x03
+#define CDATA_HOST_SRC_END_PLUS_1H 0x04
+#define CDATA_HOST_SRC_CURRENTL 0x05
+#define CDATA_HOST_SRC_CURRENTH 0x06
+
+#define CDATA_IN_BUF_CONNECT 0x07
+#define CDATA_OUT_BUF_CONNECT 0x08
+
+#define CDATA_IN_BUF_BEGIN 0x09
+#define CDATA_IN_BUF_END_PLUS_1 0x0A
+#define CDATA_IN_BUF_HEAD 0x0B
+#define CDATA_IN_BUF_TAIL 0x0C
+#define CDATA_OUT_BUF_BEGIN 0x0D
+#define CDATA_OUT_BUF_END_PLUS_1 0x0E
+#define CDATA_OUT_BUF_HEAD 0x0F
+#define CDATA_OUT_BUF_TAIL 0x10
+
+#define CDATA_DMA_CONTROL 0x11
+#define CDATA_RESERVED 0x12
+
+#define CDATA_FREQUENCY 0x13
+#define CDATA_LEFT_VOLUME 0x14
+#define CDATA_RIGHT_VOLUME 0x15
+#define CDATA_LEFT_SUR_VOL 0x16
+#define CDATA_RIGHT_SUR_VOL 0x17
+
+#define CDATA_HEADER_LEN 0x18
+
+#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN
+#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1)
+#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2)
+#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3)
+#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8)
+#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10)
+#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16)
+#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17)
+
+#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 )
+#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2)
+#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2)
+#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
+#define MINISRC_BIQUAD_STAGE 2
+#define MINISRC_COEF_LOC 0x175
+
+#define DMACONTROL_BLOCK_MASK 0x000F
+#define DMAC_BLOCK0_SELECTOR 0x0000
+#define DMAC_BLOCK1_SELECTOR 0x0001
+#define DMAC_BLOCK2_SELECTOR 0x0002
+#define DMAC_BLOCK3_SELECTOR 0x0003
+#define DMAC_BLOCK4_SELECTOR 0x0004
+#define DMAC_BLOCK5_SELECTOR 0x0005
+#define DMAC_BLOCK6_SELECTOR 0x0006
+#define DMAC_BLOCK7_SELECTOR 0x0007
+#define DMAC_BLOCK8_SELECTOR 0x0008
+#define DMAC_BLOCK9_SELECTOR 0x0009
+#define DMAC_BLOCKA_SELECTOR 0x000A
+#define DMAC_BLOCKB_SELECTOR 0x000B
+#define DMAC_BLOCKC_SELECTOR 0x000C
+#define DMAC_BLOCKD_SELECTOR 0x000D
+#define DMAC_BLOCKE_SELECTOR 0x000E
+#define DMAC_BLOCKF_SELECTOR 0x000F
+#define DMACONTROL_PAGE_MASK 0x00F0
+#define DMAC_PAGE0_SELECTOR 0x0030
+#define DMAC_PAGE1_SELECTOR 0x0020
+#define DMAC_PAGE2_SELECTOR 0x0010
+#define DMAC_PAGE3_SELECTOR 0x0000
+#define DMACONTROL_AUTOREPEAT 0x1000
+#define DMACONTROL_STOPPED 0x2000
+#define DMACONTROL_DIRECTION 0x0100
+
+/*
+ * an arbitrary volume we set the internal
+ * volume settings to so that the ac97 volume
+ * range is a little less insane. 0x7fff is
+ * max.
+ */
+#define ARB_VOLUME ( 0x6800 )
+
+/*
+ */
+
+typedef struct snd_m3_dma m3_dma_t;
+typedef struct snd_m3 m3_t;
+
+/* quirk lists */
+struct m3_quirk {
+ const char *name; /* device name */
+ u16 vendor, device; /* subsystem ids */
+ int amp_gpio; /* gpio pin # for external amp, -1 = default */
+ int irda_workaround; /* non-zero if avoid to touch 0x10 on GPIO_DIRECTION
+ (e.g. for IrDA on Dell Inspirons) */
+};
+
+struct m3_list {
+ int curlen;
+ int mem_addr;
+ int max;
+};
+
+struct snd_m3_dma {
+
+ int number;
+ m3_t *chip;
+ snd_pcm_substream_t *substream;
+
+ struct assp_instance {
+ unsigned short code, data;
+ } inst;
+
+ int running;
+ int opened;
+
+ unsigned long buffer_addr;
+ int dma_size;
+ int period_size;
+ unsigned int hwptr;
+ int count;
+
+ int index[3];
+ struct m3_list *index_list[3];
+
+ int in_lists;
+
+ struct list_head list;
+
+};
+
+struct snd_m3 {
+
+ snd_card_t *card;
+
+ unsigned long iobase;
+
+ int irq;
+ unsigned int allegro_flag : 1;
+
+ ac97_t *ac97;
+
+ snd_pcm_t *pcm;
+
+ struct pci_dev *pci;
+ struct m3_quirk *quirk;
+
+ int dacs_active;
+ int timer_users;
+
+ struct m3_list msrc_list;
+ struct m3_list mixer_list;
+ struct m3_list adc1_list;
+ struct m3_list dma_list;
+
+ /* for storing reset state..*/
+ u8 reset_state;
+
+ int external_amp;
+ int amp_gpio;
+
+ /* midi */
+ snd_rawmidi_t *rmidi;
+
+ /* pcm streams */
+ int num_substreams;
+ m3_dma_t *substreams;
+
+ spinlock_t reg_lock;
+
+#ifdef CONFIG_PM
+ u16 *suspend_mem;
+#endif
+};
+
+/*
+ * pci ids
+ */
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS 0x125D
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ALLEGRO_1
+#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ALLEGRO
+#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989
+#endif
+#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2LE
+#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990
+#endif
+#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2
+#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3
+#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_1
+#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_HW
+#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_2
+#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b
+#endif
+
+static struct pci_device_id snd_m3_ids[] = {
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2LE, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_1, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_HW, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_2, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, snd_m3_ids);
+
+static struct m3_quirk m3_quirk_list[] = {
+ /* panasonic CF-28 "toughbook" */
+ {
+ .name = "Panasonic CF-28",
+ .vendor = 0x10f7,
+ .device = 0x833e,
+ .amp_gpio = 0x0d,
+ },
+ /* panasonic CF-72 "toughbook" */
+ {
+ .name = "Panasonic CF-72",
+ .vendor = 0x10f7,
+ .device = 0x833d,
+ .amp_gpio = 0x0d,
+ },
+ /* Dell Inspiron 4000 */
+ {
+ .name = "Dell Inspiron 4000",
+ .vendor = 0x1028,
+ .device = 0x00b0,
+ .amp_gpio = -1,
+ .irda_workaround = 1,
+ },
+ /* Dell Inspiron 8000 */
+ {
+ .name = "Dell Inspiron 8000",
+ .vendor = 0x1028,
+ .device = 0x00a4,
+ .amp_gpio = -1,
+ .irda_workaround = 1,
+ },
+ /* Dell Inspiron 8100 */
+ {
+ .name = "Dell Inspiron 8100",
+ .vendor = 0x1028,
+ .device = 0x00e6,
+ .amp_gpio = -1,
+ .irda_workaround = 1,
+ },
+ /* NEC LM800J/7 */
+ {
+ .name = "NEC LM800J/7",
+ .vendor = 0x1033,
+ .device = 0x80f1,
+ .amp_gpio = 0x03,
+ },
+ /* LEGEND ZhaoYang 3100CF */
+ {
+ .name = "LEGEND ZhaoYang 3100CF",
+ .vendor = 0x1509,
+ .device = 0x1740,
+ .amp_gpio = 0x03,
+ },
+ /* END */
+ { NULL }
+};
+
+
+/*
+ * lowlevel functions
+ */
+
+#define big_mdelay(msec) do {\
+ set_current_state(TASK_UNINTERRUPTIBLE);\
+ schedule_timeout(((msec) * HZ) / 1000);\
+} while (0)
+
+inline static void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg)
+{
+ outw(value, chip->iobase + reg);
+}
+
+inline static u16 snd_m3_inw(m3_t *chip, unsigned long reg)
+{
+ return inw(chip->iobase + reg);
+}
+
+inline static void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg)
+{
+ outb(value, chip->iobase + reg);
+}
+
+inline static u8 snd_m3_inb(m3_t *chip, unsigned long reg)
+{
+ return inb(chip->iobase + reg);
+}
+
+/*
+ * access 16bit words to the code or data regions of the dsp's memory.
+ * index addresses 16bit words.
+ */
+static u16 snd_m3_assp_read(m3_t *chip, u16 region, u16 index)
+{
+ snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+ snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX);
+ return snd_m3_inw(chip, DSP_PORT_MEMORY_DATA);
+}
+
+static void snd_m3_assp_write(m3_t *chip, u16 region, u16 index, u16 data)
+{
+ snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+ snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX);
+ snd_m3_outw(chip, data, DSP_PORT_MEMORY_DATA);
+}
+
+static void snd_m3_assp_halt(m3_t *chip)
+{
+ chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
+ big_mdelay(10);
+ snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+static void snd_m3_assp_continue(m3_t *chip)
+{
+ snd_m3_outb(chip, chip->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+
+/*
+ * This makes me sad. the maestro3 has lists
+ * internally that must be packed.. 0 terminates,
+ * apparently, or maybe all unused entries have
+ * to be 0, the lists have static lengths set
+ * by the binary code images.
+ */
+
+static int snd_m3_add_list(m3_t *chip, struct m3_list *list, u16 val)
+{
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + list->curlen,
+ val);
+ return list->curlen++;
+}
+
+static void snd_m3_remove_list(m3_t *chip, struct m3_list *list, int index)
+{
+ u16 val;
+ int lastindex = list->curlen - 1;
+
+ if (index != lastindex) {
+ val = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + lastindex);
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + index,
+ val);
+ }
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + lastindex,
+ 0);
+
+ list->curlen--;
+}
+
+static void snd_m3_inc_timer_users(m3_t *chip)
+{
+ chip->timer_users++;
+ if (chip->timer_users != 1)
+ return;
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_RELOAD,
+ 240);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_CURRENT,
+ 240);
+
+ snd_m3_outw(chip,
+ snd_m3_inw(chip, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE,
+ HOST_INT_CTRL);
+}
+
+static void snd_m3_dec_timer_users(m3_t *chip)
+{
+ chip->timer_users--;
+ if (chip->timer_users > 0)
+ return;
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_RELOAD,
+ 0);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_CURRENT,
+ 0);
+
+ snd_m3_outw(chip,
+ snd_m3_inw(chip, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE,
+ HOST_INT_CTRL);
+}
+
+/*
+ * start/stop
+ */
+
+/* spinlock held! */
+static int snd_m3_pcm_start(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+ if (! s || ! subs)
+ return -EINVAL;
+
+ snd_m3_inc_timer_users(chip);
+ switch (subs->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ chip->dacs_active++;
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_INSTANCE_READY, 1);
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_MIXER_TASK_NUMBER,
+ chip->dacs_active);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_m3_assp_write(s->chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_ADC1_REQUEST, 1);
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_INSTANCE_READY, 1);
+ break;
+ }
+ return 0;
+}
+
+/* spinlock held! */
+static int snd_m3_pcm_stop(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+ if (! s || ! subs)
+ return -EINVAL;
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_INSTANCE_READY, 0);
+ snd_m3_dec_timer_users(chip);
+ switch (subs->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ chip->dacs_active--;
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_MIXER_TASK_NUMBER,
+ chip->dacs_active);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_ADC1_REQUEST, 0);
+ break;
+ }
+ return 0;
+}
+
+static int
+snd_m3_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+ m3_t *chip = snd_pcm_substream_chip(subs);
+ m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data;
+ int err = -EINVAL;
+
+ snd_assert(s != NULL, return -ENXIO);
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (s->running)
+ err = -EBUSY;
+ else {
+ s->running = 1;
+ err = snd_m3_pcm_start(chip, s, subs);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (! s->running)
+ err = 0; /* should return error? */
+ else {
+ s->running = 0;
+ err = snd_m3_pcm_stop(chip, s, subs);
+ }
+ break;
+ }
+ spin_unlock(&chip->reg_lock);
+ return err;
+}
+
+/*
+ * setup
+ */
+static void
+snd_m3_pcm_setup1(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+ int dsp_in_size, dsp_out_size, dsp_in_buffer, dsp_out_buffer;
+ snd_pcm_runtime_t *runtime = subs->runtime;
+
+ if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2);
+ dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2);
+ } else {
+ dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x10 * 2);
+ dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2);
+ }
+ dsp_in_buffer = s->inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+ dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+
+ s->dma_size = frames_to_bytes(runtime, runtime->buffer_size);
+ s->period_size = frames_to_bytes(runtime, runtime->period_size);
+ s->hwptr = 0;
+ s->count = 0;
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+ /* host dma buffer pointers */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_ADDRL,
+ LO(s->buffer_addr));
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_ADDRH,
+ HI(s->buffer_addr));
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+ LO(s->buffer_addr + s->dma_size));
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+ HI(s->buffer_addr + s->dma_size));
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_CURRENTL,
+ LO(s->buffer_addr));
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_CURRENTH,
+ HI(s->buffer_addr));
+#undef LO
+#undef HI
+
+ /* dsp buffers */
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_IN_BUF_BEGIN,
+ dsp_in_buffer);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_IN_BUF_END_PLUS_1,
+ dsp_in_buffer + (dsp_in_size / 2));
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_IN_BUF_HEAD,
+ dsp_in_buffer);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_IN_BUF_TAIL,
+ dsp_in_buffer);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_OUT_BUF_BEGIN,
+ dsp_out_buffer);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_OUT_BUF_END_PLUS_1,
+ dsp_out_buffer + (dsp_out_size / 2));
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_OUT_BUF_HEAD,
+ dsp_out_buffer);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_OUT_BUF_TAIL,
+ dsp_out_buffer);
+}
+
+static void snd_m3_pcm_setup2(m3_t *chip, m3_dma_t *s, snd_pcm_runtime_t *runtime)
+{
+ u32 freq;
+
+ /*
+ * put us in the lists if we're not already there
+ */
+ if (! s->in_lists) {
+ s->index[0] = snd_m3_add_list(chip, s->index_list[0],
+ s->inst.data >> DP_SHIFT_COUNT);
+ s->index[1] = snd_m3_add_list(chip, s->index_list[1],
+ s->inst.data >> DP_SHIFT_COUNT);
+ s->index[2] = snd_m3_add_list(chip, s->index_list[2],
+ s->inst.data >> DP_SHIFT_COUNT);
+ s->in_lists = 1;
+ }
+
+ /* write to 'mono' word */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + SRC3_DIRECTION_OFFSET + 1,
+ runtime->channels == 2 ? 0 : 1);
+ /* write to '8bit' word */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + SRC3_DIRECTION_OFFSET + 2,
+ snd_pcm_format_width(runtime->format) == 16 ? 0 : 1);
+
+ /* set up dac/adc rate */
+ freq = ((runtime->rate << 15) + 24000 ) / 48000;
+ if (freq)
+ freq--;
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_FREQUENCY,
+ freq);
+}
+
+
+static struct play_vals {
+ u16 addr, val;
+} pv[] = {
+ {CDATA_LEFT_VOLUME, ARB_VOLUME},
+ {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+ {SRC3_DIRECTION_OFFSET, 0} ,
+ /* +1, +2 are stereo/16 bit */
+ {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+ {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+ {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+ {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+ {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+ {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+ {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+ {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+ {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+ {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+ {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+ {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+ {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */
+ {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */
+ {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */
+ {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+ {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */
+};
+
+
+/* the mode passed should be already shifted and masked */
+static void
+snd_m3_playback_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+ unsigned int i;
+
+ /*
+ * some per client initializers
+ */
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + SRC3_DIRECTION_OFFSET + 12,
+ s->inst.data + 40 + 8);
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + SRC3_DIRECTION_OFFSET + 19,
+ s->inst.code + MINISRC_COEF_LOC);
+
+ /* enable or disable low pass filter? */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + SRC3_DIRECTION_OFFSET + 22,
+ subs->runtime->rate > 45000 ? 0xff : 0);
+
+ /* tell it which way dma is going? */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_DMA_CONTROL,
+ DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+ /*
+ * set an armload of static initializers
+ */
+ for (i = 0; i < ARRAY_SIZE(pv); i++)
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + pv[i].addr, pv[i].val);
+}
+
+/*
+ * Native record driver
+ */
+static struct rec_vals {
+ u16 addr, val;
+} rv[] = {
+ {CDATA_LEFT_VOLUME, ARB_VOLUME},
+ {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+ {SRC3_DIRECTION_OFFSET, 1} ,
+ /* +1, +2 are stereo/16 bit */
+ {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+ {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+ {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+ {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+ {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+ {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+ {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+ {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+ {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+ {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+ {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+ {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+ {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */
+ {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */
+ {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */
+ {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */
+ {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+ {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */
+ {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */
+};
+
+static void
+snd_m3_capture_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+ unsigned int i;
+
+ /*
+ * some per client initializers
+ */
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + SRC3_DIRECTION_OFFSET + 12,
+ s->inst.data + 40 + 8);
+
+ /* tell it which way dma is going? */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_DMA_CONTROL,
+ DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT +
+ DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+ /*
+ * set an armload of static initializers
+ */
+ for (i = 0; i < ARRAY_SIZE(rv); i++)
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + rv[i].addr, rv[i].val);
+}
+
+static int snd_m3_pcm_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ m3_dma_t *s = (m3_dma_t*) substream->runtime->private_data;
+ int err;
+
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ /* set buffer address */
+ s->buffer_addr = substream->runtime->dma_addr;
+ if (s->buffer_addr & 0x3) {
+ snd_printk("oh my, not aligned\n");
+ s->buffer_addr = s->buffer_addr & ~0x3;
+ }
+ return 0;
+}
+
+static int snd_m3_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+ m3_dma_t *s;
+
+ if (substream->runtime->private_data == NULL)
+ return 0;
+ s = (m3_dma_t*) substream->runtime->private_data;
+ snd_pcm_lib_free_pages(substream);
+ s->buffer_addr = 0;
+ return 0;
+}
+
+static int
+snd_m3_pcm_prepare(snd_pcm_substream_t *subs)
+{
+ m3_t *chip = snd_pcm_substream_chip(subs);
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ m3_dma_t *s = (m3_dma_t*)runtime->private_data;
+
+ snd_assert(s != NULL, return -ENXIO);
+
+ if (runtime->format != SNDRV_PCM_FORMAT_U8 &&
+ runtime->format != SNDRV_PCM_FORMAT_S16_LE)
+ return -EINVAL;
+ if (runtime->rate > 48000 ||
+ runtime->rate < 8000)
+ return -EINVAL;
+
+ spin_lock_irq(&chip->reg_lock);
+
+ snd_m3_pcm_setup1(chip, s, subs);
+
+ if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_m3_playback_setup(chip, s, subs);
+ else
+ snd_m3_capture_setup(chip, s, subs);
+
+ snd_m3_pcm_setup2(chip, s, runtime);
+
+ spin_unlock_irq(&chip->reg_lock);
+
+ return 0;
+}
+
+/*
+ * get current pointer
+ */
+static unsigned int
+snd_m3_get_pointer(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+ u16 hi = 0, lo = 0;
+ int retry = 10;
+ u32 addr;
+
+ /*
+ * try and get a valid answer
+ */
+ while (retry--) {
+ hi = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_CURRENTH);
+
+ lo = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_CURRENTL);
+
+ if (hi == snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+ s->inst.data + CDATA_HOST_SRC_CURRENTH))
+ break;
+ }
+ addr = lo | ((u32)hi<<16);
+ return (unsigned int)(addr - s->buffer_addr);
+}
+
+static snd_pcm_uframes_t
+snd_m3_pcm_pointer(snd_pcm_substream_t * subs)
+{
+ m3_t *chip = snd_pcm_substream_chip(subs);
+ unsigned int ptr;
+ m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data;
+ snd_assert(s != NULL, return 0);
+
+ spin_lock(&chip->reg_lock);
+ ptr = snd_m3_get_pointer(chip, s, subs);
+ spin_unlock(&chip->reg_lock);
+ return bytes_to_frames(subs->runtime, ptr);
+}
+
+
+/* update pointer */
+/* spinlock held! */
+static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s)
+{
+ snd_pcm_substream_t *subs = s->substream;
+ unsigned int hwptr;
+ int diff;
+
+ if (! s->running)
+ return;
+
+ hwptr = snd_m3_get_pointer(chip, s, subs) % s->dma_size;
+ diff = (s->dma_size + hwptr - s->hwptr) % s->dma_size;
+ s->hwptr = hwptr;
+ s->count += diff;
+ if (s->count >= (signed)s->period_size) {
+ s->count %= s->period_size;
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(subs);
+ spin_lock(&chip->reg_lock);
+ }
+}
+
+static irqreturn_t
+snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ m3_t *chip = dev_id;
+ u8 status;
+ int i;
+
+ status = inb(chip->iobase + HOST_INT_STATUS);
+
+ if (status == 0xff)
+ return IRQ_NONE;
+
+ /*
+ * ack an assp int if its running
+ * and has an int pending
+ */
+ if (status & ASSP_INT_PENDING) {
+ u8 ctl = inb(chip->iobase + ASSP_CONTROL_B);
+ if (!(ctl & STOP_ASSP_CLOCK)) {
+ ctl = inb(chip->iobase + ASSP_HOST_INT_STATUS);
+ if (ctl & DSP2HOST_REQ_TIMER) {
+ outb(DSP2HOST_REQ_TIMER, chip->iobase + ASSP_HOST_INT_STATUS);
+ /* update adc/dac info if it was a timer int */
+ spin_lock(&chip->reg_lock);
+ for (i = 0; i < chip->num_substreams; i++) {
+ m3_dma_t *s = &chip->substreams[i];
+ if (s->running)
+ snd_m3_update_ptr(chip, s);
+ }
+ spin_unlock(&chip->reg_lock);
+ }
+ }
+ }
+
+#if 0 /* TODO: not supported yet */
+ if ((status & MPU401_INT_PENDING) && chip->rmidi)
+ snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+#endif
+
+ /* ack ints */
+ snd_m3_outw(chip, HOST_INT_STATUS, status);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ */
+
+static snd_pcm_hardware_t snd_m3_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ /*SNDRV_PCM_INFO_PAUSE |*/
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (512*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (512*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+static snd_pcm_hardware_t snd_m3_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ /*SNDRV_PCM_INFO_PAUSE |*/
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (512*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (512*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+
+/*
+ */
+
+static int
+snd_m3_substream_open(m3_t *chip, snd_pcm_substream_t *subs)
+{
+ int i;
+ m3_dma_t *s;
+
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < chip->num_substreams; i++) {
+ s = &chip->substreams[i];
+ if (! s->opened)
+ goto __found;
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return -ENOMEM;
+__found:
+ s->opened = 1;
+ s->running = 0;
+ spin_unlock_irq(&chip->reg_lock);
+
+ subs->runtime->private_data = s;
+ s->substream = subs;
+
+ /* set list owners */
+ if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ s->index_list[0] = &chip->mixer_list;
+ } else
+ s->index_list[0] = &chip->adc1_list;
+ s->index_list[1] = &chip->msrc_list;
+ s->index_list[2] = &chip->dma_list;
+
+ return 0;
+}
+
+static void
+snd_m3_substream_close(m3_t *chip, snd_pcm_substream_t *subs)
+{
+ m3_dma_t *s = (m3_dma_t*) subs->runtime->private_data;
+
+ if (s == NULL)
+ return; /* not opened properly */
+
+ spin_lock_irq(&chip->reg_lock);
+ if (s->substream && s->running)
+ snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */
+ if (s->in_lists) {
+ snd_m3_remove_list(chip, s->index_list[0], s->index[0]);
+ snd_m3_remove_list(chip, s->index_list[1], s->index[1]);
+ snd_m3_remove_list(chip, s->index_list[2], s->index[2]);
+ s->in_lists = 0;
+ }
+ s->running = 0;
+ s->opened = 0;
+ spin_unlock_irq(&chip->reg_lock);
+}
+
+static int
+snd_m3_playback_open(snd_pcm_substream_t *subs)
+{
+ m3_t *chip = snd_pcm_substream_chip(subs);
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ int err;
+
+ if ((err = snd_m3_substream_open(chip, subs)) < 0)
+ return err;
+
+ runtime->hw = snd_m3_playback;
+ snd_pcm_set_sync(subs);
+
+ return 0;
+}
+
+static int
+snd_m3_playback_close(snd_pcm_substream_t *subs)
+{
+ m3_t *chip = snd_pcm_substream_chip(subs);
+
+ snd_m3_substream_close(chip, subs);
+ return 0;
+}
+
+static int
+snd_m3_capture_open(snd_pcm_substream_t *subs)
+{
+ m3_t *chip = snd_pcm_substream_chip(subs);
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ int err;
+
+ if ((err = snd_m3_substream_open(chip, subs)) < 0)
+ return err;
+
+ runtime->hw = snd_m3_capture;
+ snd_pcm_set_sync(subs);
+
+ return 0;
+}
+
+static int
+snd_m3_capture_close(snd_pcm_substream_t *subs)
+{
+ m3_t *chip = snd_pcm_substream_chip(subs);
+
+ snd_m3_substream_close(chip, subs);
+ return 0;
+}
+
+/*
+ * create pcm instance
+ */
+
+static snd_pcm_ops_t snd_m3_playback_ops = {
+ .open = snd_m3_playback_open,
+ .close = snd_m3_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_m3_pcm_hw_params,
+ .hw_free = snd_m3_pcm_hw_free,
+ .prepare = snd_m3_pcm_prepare,
+ .trigger = snd_m3_pcm_trigger,
+ .pointer = snd_m3_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_m3_capture_ops = {
+ .open = snd_m3_capture_open,
+ .close = snd_m3_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_m3_pcm_hw_params,
+ .hw_free = snd_m3_pcm_hw_free,
+ .prepare = snd_m3_pcm_prepare,
+ .trigger = snd_m3_pcm_trigger,
+ .pointer = snd_m3_pcm_pointer,
+};
+
+static int __devinit
+snd_m3_pcm(m3_t * chip, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ err = snd_pcm_new(chip->card, chip->card->driver, device,
+ MAX_PLAYBACKS, MAX_CAPTURES, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_m3_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_m3_capture_ops);
+
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, chip->card->driver);
+ chip->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+
+ return 0;
+}
+
+
+/*
+ * ac97 interface
+ */
+
+/*
+ * Wait for the ac97 serial bus to be free.
+ * return nonzero if the bus is still busy.
+ */
+static int snd_m3_ac97_wait(m3_t *chip)
+{
+ int i = 10000;
+
+ do {
+ if (! (snd_m3_inb(chip, 0x30) & 1))
+ return 0;
+ } while (i-- > 0);
+
+ snd_printk("ac97 serial bus busy\n");
+ return 1;
+}
+
+static unsigned short
+snd_m3_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ m3_t *chip = ac97->private_data;
+
+ if (snd_m3_ac97_wait(chip))
+ return 0xffff;
+ snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
+ if (snd_m3_ac97_wait(chip))
+ return 0xffff;
+ return snd_m3_inw(chip, CODEC_DATA);
+}
+
+static void
+snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+ m3_t *chip = ac97->private_data;
+
+ if (snd_m3_ac97_wait(chip))
+ return;
+ snd_m3_outw(chip, val, CODEC_DATA);
+ snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+}
+
+
+static void snd_m3_remote_codec_config(int io, int isremote)
+{
+ isremote = isremote ? 1 : 0;
+
+ outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
+ io + RING_BUS_CTRL_B);
+ outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
+ io + SDO_OUT_DEST_CTRL);
+ outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
+ io + SDO_IN_DEST_CTRL);
+}
+
+/*
+ * hack, returns non zero on err
+ */
+static int snd_m3_try_read_vendor(m3_t *chip)
+{
+ u16 ret;
+
+ if (snd_m3_ac97_wait(chip))
+ return 1;
+
+ snd_m3_outb(chip, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30);
+
+ if (snd_m3_ac97_wait(chip))
+ return 1;
+
+ ret = snd_m3_inw(chip, 0x32);
+
+ return (ret == 0) || (ret == 0xffff);
+}
+
+static void snd_m3_ac97_reset(m3_t *chip)
+{
+ u16 dir;
+ int delay1 = 0, delay2 = 0, i;
+ int io = chip->iobase;
+
+ if (chip->allegro_flag) {
+ /*
+ * the onboard codec on the allegro seems
+ * to want to wait a very long time before
+ * coming back to life
+ */
+ delay1 = 50;
+ delay2 = 800;
+ } else {
+ /* maestro3 */
+ delay1 = 20;
+ delay2 = 500;
+ }
+
+ for (i = 0; i < 5; i++) {
+ dir = inw(io + GPIO_DIRECTION);
+ if (! chip->quirk || ! chip->quirk->irda_workaround)
+ dir |= 0x10; /* assuming pci bus master? */
+
+ snd_m3_remote_codec_config(io, 0);
+
+ outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
+ udelay(20);
+
+ outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION);
+ outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK);
+ outw(0, io + GPIO_DATA);
+ outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION);
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((delay1 * HZ) / 1000);
+
+ outw(GPO_PRIMARY_AC97, io + GPIO_DATA);
+ udelay(5);
+ /* ok, bring back the ac-link */
+ outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A);
+ outw(~0, io + GPIO_MASK);
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((delay2 * HZ) / 1000);
+
+ if (! snd_m3_try_read_vendor(chip))
+ break;
+
+ delay1 += 10;
+ delay2 += 100;
+
+ snd_printd("maestro3: retrying codec reset with delays of %d and %d ms\n",
+ delay1, delay2);
+ }
+
+#if 0
+ /* more gung-ho reset that doesn't
+ * seem to work anywhere :)
+ */
+ tmp = inw(io + RING_BUS_CTRL_A);
+ outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A);
+ big_mdelay(20);
+ outw(tmp, io + RING_BUS_CTRL_A);
+ big_mdelay(50);
+#endif
+}
+
+static int __devinit snd_m3_mixer(m3_t *chip)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_m3_ac97_write,
+ .read = snd_m3_ac97_read,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+ return err;
+
+ /* seems ac97 PCM needs initialization.. hack hack.. */
+ snd_ac97_write(chip->ac97, AC97_PCM, 0x8000 | (15 << 8) | 15);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ snd_ac97_write(chip->ac97, AC97_PCM, 0);
+
+ return 0;
+}
+
+
+/*
+ * DSP Code images
+ */
+
+static u16 assp_kernel_image[] __devinitdata = {
+ 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4,
+ 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
+ 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
+ 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4,
+ 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08,
+ 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909,
+ 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41,
+ 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308,
+ 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910,
+ 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00,
+ 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026,
+ 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0,
+ 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012,
+ 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A,
+ 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000,
+ 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05,
+ 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26,
+ 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300,
+ 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88,
+ 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008,
+ 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80,
+ 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71,
+ 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A,
+ 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100,
+ 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900,
+ 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903,
+ 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309,
+ 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B,
+ 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540,
+ 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C,
+ 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380,
+ 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340,
+ 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041,
+ 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18,
+ 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40,
+ 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9,
+ 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8,
+ 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A,
+ 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80,
+ 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40,
+ 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80,
+ 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A,
+ 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F,
+ 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1,
+ 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034,
+ 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E,
+ 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977,
+ 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B,
+ 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388,
+ 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80,
+ 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473,
+ 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473,
+ 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880,
+ 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80,
+ 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6,
+ 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909,
+ 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED,
+ 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005,
+ 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388,
+ 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4,
+ 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100,
+ 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10,
+ 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070,
+ 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71,
+ 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A,
+ 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061,
+ 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A,
+ 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6,
+ 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A,
+ 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6,
+ 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358,
+ 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6,
+ 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9,
+ 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379,
+ 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6,
+ 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C,
+ 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166,
+ 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B,
+ 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A,
+ 0xBE3A,
+};
+
+/*
+ * Mini sample rate converter code image
+ * that is to be loaded at 0x400 on the DSP.
+ */
+static u16 assp_minisrc_image[] __devinitdata = {
+
+ 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412,
+ 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41,
+ 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907,
+ 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01,
+ 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904,
+ 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D,
+ 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD,
+ 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22,
+ 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0,
+ 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9,
+ 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89,
+ 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0,
+ 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484,
+ 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80,
+ 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624,
+ 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4,
+ 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027,
+ 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903,
+ 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809,
+ 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46,
+ 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47,
+ 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0,
+ 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516,
+ 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E,
+ 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C,
+ 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180,
+ 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E,
+ 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0,
+ 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0,
+ 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0,
+ 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F,
+ 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+
+/*
+ * initialize ASSP
+ */
+
+#define MINISRC_LPF_LEN 10
+static u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
+ 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
+ 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
+};
+
+static void __devinit snd_m3_assp_init(m3_t *chip)
+{
+ unsigned int i;
+
+ /* zero kernel data */
+ for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_BASE_ADDR + i, 0);
+
+ /* zero mixer data? */
+ for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_BASE_ADDR2 + i, 0);
+
+ /* init dma pointer */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_CURRENT_DMA,
+ KDATA_DMA_XFER0);
+
+ /* write kernel into code memory.. */
+ for (i = 0 ; i < ARRAY_SIZE(assp_kernel_image); i++) {
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+ REV_B_CODE_MEMORY_BEGIN + i,
+ assp_kernel_image[i]);
+ }
+
+ /*
+ * We only have this one client and we know that 0x400
+ * is free in our kernel's mem map, so lets just
+ * drop it there. It seems that the minisrc doesn't
+ * need vectors, so we won't bother with them..
+ */
+ for (i = 0; i < ARRAY_SIZE(assp_minisrc_image); i++) {
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+ 0x400 + i,
+ assp_minisrc_image[i]);
+ }
+
+ /*
+ * write the coefficients for the low pass filter?
+ */
+ for (i = 0; i < MINISRC_LPF_LEN ; i++) {
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+ 0x400 + MINISRC_COEF_LOC + i,
+ minisrc_lpf[i]);
+ }
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+ 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN,
+ 0x8000);
+
+ /*
+ * the minisrc is the only thing on
+ * our task list..
+ */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_TASK0,
+ 0x400);
+
+ /*
+ * init the mixer number..
+ */
+
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_MIXER_TASK_NUMBER,0);
+
+ /*
+ * EXTREME KERNEL MASTER VOLUME
+ */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_DAC_LEFT_VOLUME, ARB_VOLUME);
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME);
+
+ chip->mixer_list.curlen = 0;
+ chip->mixer_list.mem_addr = KDATA_MIXER_XFER0;
+ chip->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS;
+ chip->adc1_list.curlen = 0;
+ chip->adc1_list.mem_addr = KDATA_ADC1_XFER0;
+ chip->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS;
+ chip->dma_list.curlen = 0;
+ chip->dma_list.mem_addr = KDATA_DMA_XFER0;
+ chip->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS;
+ chip->msrc_list.curlen = 0;
+ chip->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC;
+ chip->msrc_list.max = MAX_INSTANCE_MINISRC;
+}
+
+
+static int __devinit snd_m3_assp_client_init(m3_t *chip, m3_dma_t *s, int index)
+{
+ int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 +
+ MINISRC_IN_BUFFER_SIZE / 2 +
+ 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 );
+ int address, i;
+
+ /*
+ * the revb memory map has 0x1100 through 0x1c00
+ * free.
+ */
+
+ /*
+ * align instance address to 256 bytes so that it's
+ * shifted list address is aligned.
+ * list address = (mem address >> 1) >> 7;
+ */
+ data_bytes = (data_bytes + 255) & ~255;
+ address = 0x1100 + ((data_bytes/2) * index);
+
+ if ((address + (data_bytes/2)) >= 0x1c00) {
+ snd_printk("no memory for %d bytes at ind %d (addr 0x%x)\n",
+ data_bytes, index, address);
+ return -ENOMEM;
+ }
+
+ s->number = index;
+ s->inst.code = 0x400;
+ s->inst.data = address;
+
+ for (i = data_bytes / 2; i > 0; address++, i--) {
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ address, 0);
+ }
+
+ return 0;
+}
+
+
+/*
+ * this works for the reference board, have to find
+ * out about others
+ *
+ * this needs more magic for 4 speaker, but..
+ */
+static void
+snd_m3_amp_enable(m3_t *chip, int enable)
+{
+ int io = chip->iobase;
+ u16 gpo, polarity;
+
+ if (! chip->external_amp)
+ return;
+
+ polarity = enable ? 0 : 1;
+ polarity = polarity << chip->amp_gpio;
+ gpo = 1 << chip->amp_gpio;
+
+ outw(~gpo, io + GPIO_MASK);
+
+ outw(inw(io + GPIO_DIRECTION) | gpo,
+ io + GPIO_DIRECTION);
+
+ outw((GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity),
+ io + GPIO_DATA);
+
+ outw(0xffff, io + GPIO_MASK);
+}
+
+static int
+snd_m3_chip_init(m3_t *chip)
+{
+ struct pci_dev *pcidev = chip->pci;
+ u32 n;
+ u16 w;
+ u8 t; /* makes as much sense as 'n', no? */
+
+ pci_read_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, &w);
+ w &= ~(SOUND_BLASTER_ENABLE|FM_SYNTHESIS_ENABLE|
+ MPU401_IO_ENABLE|MPU401_IRQ_ENABLE|ALIAS_10BIT_IO|
+ DISABLE_LEGACY);
+ pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
+
+ pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+ n &= REDUCED_DEBOUNCE;
+ n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
+ pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+ outb(RESET_ASSP, chip->iobase + ASSP_CONTROL_B);
+ pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+ n &= ~INT_CLK_SELECT;
+ if (!chip->allegro_flag) {
+ n &= ~INT_CLK_MULT_ENABLE;
+ n |= INT_CLK_SRC_NOT_PCI;
+ }
+ n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 );
+ pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+ if (chip->allegro_flag) {
+ pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n);
+ n |= IN_CLK_12MHZ_SELECT;
+ pci_write_config_dword(pcidev, PCI_USER_CONFIG, n);
+ }
+
+ t = inb(chip->iobase + ASSP_CONTROL_A);
+ t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT);
+ t |= ASSP_CLK_49MHZ_SELECT;
+ t |= ASSP_0_WS_ENABLE;
+ outb(t, chip->iobase + ASSP_CONTROL_A);
+
+ outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B);
+
+ return 0;
+}
+
+static void
+snd_m3_enable_ints(m3_t *chip)
+{
+ unsigned long io = chip->iobase;
+
+ /* TODO: MPU401 not supported yet */
+ outw(ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL);
+ outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
+ io + ASSP_CONTROL_C);
+}
+
+
+/*
+ */
+
+static int snd_m3_free(m3_t *chip)
+{
+ m3_dma_t *s;
+ int i;
+
+ if (chip->substreams) {
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < chip->num_substreams; i++) {
+ s = &chip->substreams[i];
+ /* check surviving pcms; this should not happen though.. */
+ if (s->substream && s->running)
+ snd_m3_pcm_stop(chip, s, s->substream);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ kfree(chip->substreams);
+ }
+ if (chip->iobase) {
+ snd_m3_outw(chip, HOST_INT_CTRL, 0); /* disable ints */
+ }
+
+#ifdef CONFIG_PM
+ vfree(chip->suspend_mem);
+#endif
+
+ if (chip->irq >= 0) {
+ synchronize_irq(chip->irq);
+ free_irq(chip->irq, (void *)chip);
+ }
+
+ if (chip->iobase)
+ pci_release_regions(chip->pci);
+
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+
+/*
+ * APM support
+ */
+#ifdef CONFIG_PM
+static int m3_suspend(snd_card_t *card, pm_message_t state)
+{
+ m3_t *chip = card->pm_private_data;
+ int i, index;
+
+ if (chip->suspend_mem == NULL)
+ return 0;
+
+ snd_pcm_suspend_all(chip->pcm);
+ snd_ac97_suspend(chip->ac97);
+
+ big_mdelay(10); /* give the assp a chance to idle.. */
+
+ snd_m3_assp_halt(chip);
+
+ /* save dsp image */
+ index = 0;
+ for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
+ chip->suspend_mem[index++] =
+ snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i);
+ for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+ chip->suspend_mem[index++] =
+ snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i);
+
+ /* power down apci registers */
+ snd_m3_outw(chip, 0xffff, 0x54);
+ snd_m3_outw(chip, 0xffff, 0x56);
+
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int m3_resume(snd_card_t *card)
+{
+ m3_t *chip = card->pm_private_data;
+ int i, index;
+
+ if (chip->suspend_mem == NULL)
+ return 0;
+
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+
+ /* first lets just bring everything back. .*/
+ snd_m3_outw(chip, 0, 0x54);
+ snd_m3_outw(chip, 0, 0x56);
+
+ snd_m3_chip_init(chip);
+ snd_m3_assp_halt(chip);
+ snd_m3_ac97_reset(chip);
+
+ /* restore dsp image */
+ index = 0;
+ for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i,
+ chip->suspend_mem[index++]);
+ for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i,
+ chip->suspend_mem[index++]);
+
+ /* tell the dma engine to restart itself */
+ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+ KDATA_DMA_ACTIVE, 0);
+
+ /* restore ac97 registers */
+ snd_ac97_resume(chip->ac97);
+
+ snd_m3_assp_continue(chip);
+ snd_m3_enable_ints(chip);
+ snd_m3_amp_enable(chip, 1);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ */
+
+static int snd_m3_dev_free(snd_device_t *device)
+{
+ m3_t *chip = device->device_data;
+ return snd_m3_free(chip);
+}
+
+static int __devinit
+snd_m3_create(snd_card_t *card, struct pci_dev *pci,
+ int enable_amp,
+ int amp_gpio,
+ m3_t **chip_ret)
+{
+ m3_t *chip;
+ int i, err;
+ struct m3_quirk *quirk;
+ u16 subsystem_vendor, subsystem_device;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_m3_dev_free,
+ };
+
+ *chip_ret = NULL;
+
+ if (pci_enable_device(pci))
+ return -EIO;
+
+ /* check, if we can restrict PCI DMA transfers to 28 bits */
+ if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
+ snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ switch (pci->device) {
+ case PCI_DEVICE_ID_ESS_ALLEGRO:
+ case PCI_DEVICE_ID_ESS_ALLEGRO_1:
+ case PCI_DEVICE_ID_ESS_CANYON3D_2LE:
+ case PCI_DEVICE_ID_ESS_CANYON3D_2:
+ chip->allegro_flag = 1;
+ break;
+ }
+
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+
+ for (quirk = m3_quirk_list; quirk->vendor; quirk++) {
+ if (subsystem_vendor == quirk->vendor &&
+ subsystem_device == quirk->device) {
+ printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name);
+ chip->quirk = quirk;
+ break;
+ }
+ }
+
+ chip->external_amp = enable_amp;
+ if (amp_gpio >= 0 && amp_gpio <= 0x0f)
+ chip->amp_gpio = amp_gpio;
+ else if (chip->quirk && chip->quirk->amp_gpio >= 0)
+ chip->amp_gpio = chip->quirk->amp_gpio;
+ else if (chip->allegro_flag)
+ chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
+ else /* presumably this is for all 'maestro3's.. */
+ chip->amp_gpio = GPO_EXT_AMP_M3;
+
+ chip->num_substreams = NR_DSPS;
+ chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL);
+ if (chip->substreams == NULL) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams);
+
+ if ((err = pci_request_regions(pci, card->driver)) < 0) {
+ snd_m3_free(chip);
+ return err;
+ }
+ chip->iobase = pci_resource_start(pci, 0);
+
+ /* just to be sure */
+ pci_set_master(pci);
+
+ snd_m3_chip_init(chip);
+ snd_m3_assp_halt(chip);
+
+ snd_m3_ac97_reset(chip);
+
+ snd_m3_assp_init(chip);
+ snd_m3_amp_enable(chip, 1);
+
+ if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ card->driver, (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_m3_free(chip);
+ return -ENOMEM;
+ }
+ chip->irq = pci->irq;
+
+#ifdef CONFIG_PM
+ chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH));
+ if (chip->suspend_mem == NULL)
+ snd_printk(KERN_WARNING "can't allocate apm buffer\n");
+ else
+ snd_card_set_pm_callback(card, m3_suspend, m3_resume, chip);
+#endif
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_m3_free(chip);
+ return err;
+ }
+
+ if ((err = snd_m3_mixer(chip)) < 0)
+ return err;
+
+ for (i = 0; i < chip->num_substreams; i++) {
+ m3_dma_t *s = &chip->substreams[i];
+ s->chip = chip;
+ if ((err = snd_m3_assp_client_init(chip, s, i)) < 0)
+ return err;
+ }
+
+ if ((err = snd_m3_pcm(chip, 0)) < 0)
+ return err;
+
+ snd_m3_enable_ints(chip);
+ snd_m3_assp_continue(chip);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *chip_ret = chip;
+
+ return 0;
+}
+
+/*
+ */
+static int __devinit
+snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ m3_t *chip;
+ int err;
+
+ /* don't pick up modems */
+ if (((pci->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+ return -ENODEV;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ switch (pci->device) {
+ case PCI_DEVICE_ID_ESS_ALLEGRO:
+ case PCI_DEVICE_ID_ESS_ALLEGRO_1:
+ strcpy(card->driver, "Allegro");
+ break;
+ case PCI_DEVICE_ID_ESS_CANYON3D_2LE:
+ case PCI_DEVICE_ID_ESS_CANYON3D_2:
+ strcpy(card->driver, "Canyon3D-2");
+ break;
+ default:
+ strcpy(card->driver, "Maestro3");
+ break;
+ }
+
+ if ((err = snd_m3_create(card, pci,
+ external_amp[dev],
+ amp_gpio[dev],
+ &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ sprintf(card->shortname, "ESS %s PCI", card->driver);
+ sprintf(card->longname, "%s at 0x%lx, irq %d",
+ card->shortname, chip->iobase, chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+#if 0 /* TODO: not supported yet */
+ /* TODO enable midi irq and i/o */
+ err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401,
+ chip->iobase + MPU401_DATA_PORT, 1,
+ chip->irq, 0, &chip->rmidi);
+ if (err < 0)
+ printk(KERN_WARNING "maestro3: no midi support.\n");
+#endif
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_m3_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Maestro3",
+ .id_table = snd_m3_ids,
+ .probe = snd_m3_probe,
+ .remove = __devexit_p(snd_m3_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_m3_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_m3_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_m3_init)
+module_exit(alsa_card_m3_exit)
diff --git a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile
new file mode 100644
index 00000000000..fe6ba0c4b56
--- /dev/null
+++ b/sound/pci/mixart/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
+
+obj-$(CONFIG_SND_MIXART) += snd-mixart.o
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
new file mode 100644
index 00000000000..65bb0f47af2
--- /dev/null
+++ b/sound/pci/mixart/mixart.c
@@ -0,0 +1,1443 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * main file with alsa callbacks
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "mixart.h"
+#include "mixart_hwdep.h"
+#include "mixart_core.h"
+#include "mixart_mixer.h"
+
+#define CARD_NAME "miXart"
+
+MODULE_AUTHOR("Digigram <alsa@digigram.com>");
+MODULE_DESCRIPTION("Digigram " CARD_NAME);
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
+
+/*
+ */
+
+static struct pci_device_id snd_mixart_ids[] = {
+ { 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_mixart_ids);
+
+
+static int mixart_set_pipe_state(mixart_mgr_t *mgr, mixart_pipe_t* pipe, int start)
+{
+ mixart_group_state_req_t group_state;
+ mixart_group_state_resp_t group_state_resp;
+ mixart_msg_t request;
+ int err;
+ u32 system_msg_uid;
+
+ switch(pipe->status) {
+ case PIPE_RUNNING:
+ case PIPE_CLOCK_SET:
+ if(start) return 0; /* already started */
+ break;
+ case PIPE_STOPPED:
+ if(!start) return 0; /* already stopped */
+ break;
+ default:
+ snd_printk(KERN_ERR "error mixart_set_pipe_state called with wrong pipe->status!\n");
+ return -EINVAL; /* function called with wrong pipe status */
+ }
+
+ system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */
+
+ /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */
+
+ request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = &system_msg_uid;
+ request.size = sizeof(system_msg_uid);
+
+ err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid);
+ if(err) {
+ snd_printk(KERN_ERR "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
+ return err;
+ }
+
+ /* start or stop the pipe (1 pipe) */
+
+ memset(&group_state, 0, sizeof(group_state));
+ group_state.pipe_count = 1;
+ group_state.pipe_uid[0] = pipe->group_uid;
+
+ if(start)
+ request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET;
+ else
+ request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET;
+
+ request.uid = pipe->group_uid; /*(mixart_uid_t){0,0};*/
+ request.data = &group_state;
+ request.size = sizeof(group_state);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
+ if (err < 0 || group_state_resp.txx_status != 0) {
+ snd_printk(KERN_ERR "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status);
+ return -EINVAL;
+ }
+
+ if(start) {
+ u32 stat;
+
+ group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
+ if (err < 0 || group_state_resp.txx_status != 0) {
+ snd_printk(KERN_ERR "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status);
+ return -EINVAL;
+ }
+
+ /* in case of start send a synchro top */
+
+ request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = NULL;
+ request.size = 0;
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat);
+ if (err < 0 || stat != 0) {
+ snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n", err, stat);
+ return -EINVAL;
+ }
+
+ pipe->status = PIPE_RUNNING;
+ }
+ else /* !start */
+ pipe->status = PIPE_STOPPED;
+
+ return 0;
+}
+
+
+static int mixart_set_clock(mixart_mgr_t *mgr, mixart_pipe_t *pipe, unsigned int rate)
+{
+ mixart_msg_t request;
+ mixart_clock_properties_t clock_properties;
+ mixart_clock_properties_resp_t clock_prop_resp;
+ int err;
+
+ switch(pipe->status) {
+ case PIPE_CLOCK_SET:
+ break;
+ case PIPE_RUNNING:
+ if(rate != 0)
+ break;
+ default:
+ if(rate == 0)
+ return 0; /* nothing to do */
+ else {
+ snd_printk(KERN_ERR "error mixart_set_clock(%d) called with wrong pipe->status !\n", rate);
+ return -EINVAL;
+ }
+ }
+
+ memset(&clock_properties, 0, sizeof(clock_properties));
+ clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK;
+ clock_properties.clock_mode = CM_STANDALONE;
+ clock_properties.frequency = rate;
+ clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
+ clock_properties.uid_caller[0] = pipe->group_uid;
+
+ snd_printdd("mixart_set_clock to %d kHz\n", rate);
+
+ request.message_id = MSG_CLOCK_SET_PROPERTIES;
+ request.uid = mgr->uid_console_manager;
+ request.data = &clock_properties;
+ request.size = sizeof(clock_properties);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp);
+ if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) {
+ snd_printk(KERN_ERR "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n", err, clock_prop_resp.status, clock_prop_resp.clock_mode);
+ return -EINVAL;
+ }
+
+ if(rate) pipe->status = PIPE_CLOCK_SET;
+ else pipe->status = PIPE_RUNNING;
+
+ return 0;
+}
+
+
+/*
+ * Allocate or reference output pipe for analog IOs (pcmp0/1)
+ */
+mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring)
+{
+ int stream_count;
+ mixart_pipe_t *pipe;
+ mixart_msg_t request;
+
+ if(capture) {
+ if (pcm_number == MIXART_PCM_ANALOG) {
+ pipe = &(chip->pipe_in_ana); /* analog inputs */
+ } else {
+ pipe = &(chip->pipe_in_dig); /* digital inputs */
+ }
+ request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP;
+ stream_count = MIXART_CAPTURE_STREAMS;
+ } else {
+ if (pcm_number == MIXART_PCM_ANALOG) {
+ pipe = &(chip->pipe_out_ana); /* analog outputs */
+ } else {
+ pipe = &(chip->pipe_out_dig); /* digital outputs */
+ }
+ request.message_id = MSG_STREAM_ADD_INPUT_GROUP;
+ stream_count = MIXART_PLAYBACK_STREAMS;
+ }
+
+ /* a new stream is opened and there are already all streams in use */
+ if( (monitoring == 0) && (pipe->references >= stream_count) ) {
+ return NULL;
+ }
+
+ /* pipe is not yet defined */
+ if( pipe->status == PIPE_UNDEFINED ) {
+ int err, i;
+ struct {
+ mixart_streaming_group_req_t sgroup_req;
+ mixart_streaming_group_t sgroup_resp;
+ } *buf;
+
+ snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number);
+
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ request.uid = (mixart_uid_t){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */
+ request.data = &buf->sgroup_req;
+ request.size = sizeof(buf->sgroup_req);
+
+ memset(&buf->sgroup_req, 0, sizeof(buf->sgroup_req));
+
+ buf->sgroup_req.stream_count = stream_count;
+ buf->sgroup_req.channel_count = 2;
+ buf->sgroup_req.latency = 256;
+ buf->sgroup_req.connector = pipe->uid_left_connector; /* the left connector */
+
+ for (i=0; i<stream_count; i++) {
+ int j;
+ struct mixart_flowinfo *flowinfo;
+ struct mixart_bufferinfo *bufferinfo;
+
+ /* we don't yet know the format, so config 16 bit pcm audio for instance */
+ buf->sgroup_req.stream_info[i].size_max_byte_frame = 1024;
+ buf->sgroup_req.stream_info[i].size_max_sample_frame = 256;
+ buf->sgroup_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */
+
+ /* find the right bufferinfo_array */
+ j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i;
+ if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */
+
+ buf->sgroup_req.flow_entry[i] = j;
+
+ flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area;
+ flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(mixart_bufferinfo_t));
+ flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */
+
+ bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
+ bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */
+ bufferinfo[j].available_length = 0; /* buffer is not yet allocated */
+
+ /* construct the identifier of the stream buffer received in the interrupts ! */
+ bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i;
+ if(capture) {
+ bufferinfo[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK;
+ }
+ }
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp);
+ if((err < 0) || (buf->sgroup_resp.status != 0)) {
+ snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, buf->sgroup_resp.status);
+ kfree(buf);
+ return NULL;
+ }
+
+ pipe->group_uid = buf->sgroup_resp.group; /* id of the pipe, as returned by embedded */
+ pipe->stream_count = buf->sgroup_resp.stream_count;
+ /* pipe->stream_uid[i] = buf->sgroup_resp.stream[i].stream_uid; */
+
+ pipe->status = PIPE_STOPPED;
+ kfree(buf);
+ }
+
+ if(monitoring) pipe->monitoring = 1;
+ else pipe->references++;
+
+ return pipe;
+}
+
+
+int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring)
+{
+ int err = 0;
+
+ if(pipe->status == PIPE_UNDEFINED)
+ return 0;
+
+ if(monitoring)
+ pipe->monitoring = 0;
+ else
+ pipe->references--;
+
+ if((pipe->references <= 0) && (pipe->monitoring == 0)) {
+
+ mixart_msg_t request;
+ mixart_delete_group_resp_t delete_resp;
+
+ /* release the clock */
+ err = mixart_set_clock( mgr, pipe, 0);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n");
+ }
+
+ /* stop the pipe */
+ err = mixart_set_pipe_state(mgr, pipe, 0);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "error stopping pipe!\n");
+ }
+
+ request.message_id = MSG_STREAM_DELETE_GROUP;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = &pipe->group_uid; /* the streaming group ! */
+ request.size = sizeof(pipe->group_uid);
+
+ /* delete the pipe */
+ err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
+ if ((err < 0) || (delete_resp.status != 0)) {
+ snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status);
+ }
+
+ pipe->group_uid = (mixart_uid_t){0,0};
+ pipe->stream_count = 0;
+ pipe->status = PIPE_UNDEFINED;
+ }
+
+ return err;
+}
+
+static int mixart_set_stream_state(mixart_stream_t *stream, int start)
+{
+ mixart_t *chip;
+ mixart_stream_state_req_t stream_state_req;
+ mixart_msg_t request;
+
+ if(!stream->substream)
+ return -EINVAL;
+
+ memset(&stream_state_req, 0, sizeof(stream_state_req));
+ stream_state_req.stream_count = 1;
+ stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
+ stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;
+
+ if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
+ else
+ request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;
+
+ request.uid = (mixart_uid_t){0,0};
+ request.data = &stream_state_req;
+ request.size = sizeof(stream_state_req);
+
+ stream->abs_period_elapsed = 0; /* reset stream pos */
+ stream->buf_periods = 0;
+ stream->buf_period_frag = 0;
+
+ chip = snd_pcm_substream_chip(stream->substream);
+
+ return snd_mixart_send_msg_nonblock(chip->mgr, &request);
+}
+
+/*
+ * Trigger callback
+ */
+
+static int snd_mixart_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+ mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+
+ snd_printdd("SNDRV_PCM_TRIGGER_START\n");
+
+ /* START_STREAM */
+ if( mixart_set_stream_state(stream, 1) )
+ return -EINVAL;
+
+ stream->status = MIXART_STREAM_STATUS_RUNNING;
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+
+ /* STOP_STREAM */
+ if( mixart_set_stream_state(stream, 0) )
+ return -EINVAL;
+
+ stream->status = MIXART_STREAM_STATUS_OPEN;
+
+ snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* TODO */
+ stream->status = MIXART_STREAM_STATUS_PAUSE;
+ snd_printdd("SNDRV_PCM_PAUSE_PUSH\n");
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* TODO */
+ stream->status = MIXART_STREAM_STATUS_RUNNING;
+ snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mixart_sync_nonblock_events(mixart_mgr_t *mgr)
+{
+ int timeout = HZ;
+ while (atomic_read(&mgr->msg_processed) > 0) {
+ if (! timeout--) {
+ snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n");
+ return -EBUSY;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ return 0;
+}
+
+/*
+ * prepare callback for all pcms
+ */
+static int snd_mixart_prepare(snd_pcm_substream_t *subs)
+{
+ mixart_t *chip = snd_pcm_substream_chip(subs);
+ mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+
+ /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */
+
+ snd_printdd("snd_mixart_prepare\n");
+
+ mixart_sync_nonblock_events(chip->mgr);
+
+ /* only the first stream can choose the sample rate */
+ /* the further opened streams will be limited to its frequency (see open) */
+ if(chip->mgr->ref_count_rate == 1)
+ chip->mgr->sample_rate = subs->runtime->rate;
+
+ /* set the clock only once (first stream) on the same pipe */
+ if(stream->pipe->references == 1) {
+ if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int mixart_set_format(mixart_stream_t *stream, snd_pcm_format_t format)
+{
+ int err;
+ mixart_t *chip;
+ mixart_msg_t request;
+ mixart_stream_param_desc_t stream_param;
+ mixart_return_uid_t resp;
+
+ chip = snd_pcm_substream_chip(stream->substream);
+
+ memset(&stream_param, 0, sizeof(stream_param));
+
+ stream_param.coding_type = CT_LINEAR;
+ stream_param.number_of_channel = stream->channels;
+
+ stream_param.sampling_freq = chip->mgr->sample_rate;
+ if(stream_param.sampling_freq == 0)
+ stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */
+
+ switch(format){
+ case SNDRV_PCM_FORMAT_U8:
+ stream_param.sample_type = ST_INTEGER_8;
+ stream_param.sample_size = 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ stream_param.sample_type = ST_INTEGER_16LE;
+ stream_param.sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ stream_param.sample_type = ST_INTEGER_16BE;
+ stream_param.sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ stream_param.sample_type = ST_INTEGER_24LE;
+ stream_param.sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ stream_param.sample_type = ST_INTEGER_24BE;
+ stream_param.sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ stream_param.sample_type = ST_FLOATING_POINT_32LE;
+ stream_param.sample_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_BE:
+ stream_param.sample_type = ST_FLOATING_POINT_32BE;
+ stream_param.sample_size = 32;
+ break;
+ default:
+ snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n");
+ return -EINVAL;
+ }
+
+ snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
+ stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);
+
+ /* TODO: what else to configure ? */
+ /* stream_param.samples_per_frame = 2; */
+ /* stream_param.bytes_per_frame = 4; */
+ /* stream_param.bytes_per_sample = 2; */
+
+ stream_param.pipe_count = 1; /* set to 1 */
+ stream_param.stream_count = 1; /* set to 1 */
+ stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
+ stream_param.stream_desc[0].stream_idx = stream->substream->number;
+
+ request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = &stream_param;
+ request.size = sizeof(stream_param);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+ if((err < 0) || resp.error_code) {
+ snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*
+ * HW_PARAMS callback for all pcms
+ */
+static int snd_mixart_hw_params(snd_pcm_substream_t *subs,
+ snd_pcm_hw_params_t *hw)
+{
+ mixart_t *chip = snd_pcm_substream_chip(subs);
+ mixart_mgr_t *mgr = chip->mgr;
+ mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+ snd_pcm_format_t format;
+ int err;
+ int channels;
+
+ /* set up channels */
+ channels = params_channels(hw);
+
+ /* set up format for the stream */
+ format = params_format(hw);
+
+ down(&mgr->setup_mutex);
+
+ /* update the stream levels */
+ if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
+ int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
+ if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
+ mixart_update_playback_stream_level(chip, is_aes, subs->number);
+ else
+ mixart_update_capture_stream_level( chip, is_aes);
+ }
+
+ stream->channels = channels;
+
+ /* set the format to the board */
+ err = mixart_set_format(stream, format);
+ if(err < 0) {
+ return err;
+ }
+
+ /* allocate buffer */
+ err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
+
+ if (err > 0) {
+ struct mixart_bufferinfo *bufferinfo;
+ int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
+ if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
+ i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
+ }
+
+ bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
+ bufferinfo[i].buffer_address = subs->runtime->dma_addr;
+ bufferinfo[i].available_length = subs->runtime->dma_bytes;
+ /* bufferinfo[i].buffer_id is already defined */
+
+ snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i,
+ bufferinfo[i].buffer_address,
+ bufferinfo[i].available_length,
+ subs->number);
+ }
+ up(&mgr->setup_mutex);
+
+ return err;
+}
+
+static int snd_mixart_hw_free(snd_pcm_substream_t *subs)
+{
+ mixart_t *chip = snd_pcm_substream_chip(subs);
+ snd_pcm_lib_free_pages(subs);
+ mixart_sync_nonblock_events(chip->mgr);
+ return 0;
+}
+
+
+
+/*
+ * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
+ */
+static snd_pcm_hardware_t snd_mixart_analog_caps =
+{
+ .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = ( SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 256, /* 256 frames U8 mono*/
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = (32*1024/256),
+};
+
+static snd_pcm_hardware_t snd_mixart_digital_caps =
+{
+ .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = ( SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 256, /* 256 frames U8 mono*/
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = (32*1024/256),
+};
+
+
+static int snd_mixart_playback_open(snd_pcm_substream_t *subs)
+{
+ mixart_t *chip = snd_pcm_substream_chip(subs);
+ mixart_mgr_t *mgr = chip->mgr;
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ snd_pcm_t *pcm = subs->pcm;
+ mixart_stream_t *stream;
+ mixart_pipe_t *pipe;
+ int err = 0;
+ int pcm_number;
+
+ down(&mgr->setup_mutex);
+
+ if ( pcm == chip->pcm ) {
+ pcm_number = MIXART_PCM_ANALOG;
+ runtime->hw = snd_mixart_analog_caps;
+ } else {
+ snd_assert ( pcm == chip->pcm_dig );
+ pcm_number = MIXART_PCM_DIGITAL;
+ runtime->hw = snd_mixart_digital_caps;
+ }
+ snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
+
+ /* get stream info */
+ stream = &(chip->playback_stream[pcm_number][subs->number]);
+
+ if (stream->status != MIXART_STREAM_STATUS_FREE){
+ /* streams in use */
+ snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
+ err = -EBUSY;
+ goto _exit_open;
+ }
+
+ /* get pipe pointer (out pipe) */
+ pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);
+
+ if (pipe == NULL) {
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ /* start the pipe if necessary */
+ err = mixart_set_pipe_state(chip->mgr, pipe, 1);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "error starting pipe!\n");
+ snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ stream->pipe = pipe;
+ stream->pcm_number = pcm_number;
+ stream->status = MIXART_STREAM_STATUS_OPEN;
+ stream->substream = subs;
+ stream->channels = 0; /* not configured yet */
+
+ runtime->private_data = stream;
+
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
+
+ /* if a sample rate is already used, another stream cannot change */
+ if(mgr->ref_count_rate++) {
+ if(mgr->sample_rate) {
+ runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
+ }
+ }
+
+ _exit_open:
+ up(&mgr->setup_mutex);
+
+ return err;
+}
+
+
+static int snd_mixart_capture_open(snd_pcm_substream_t *subs)
+{
+ mixart_t *chip = snd_pcm_substream_chip(subs);
+ mixart_mgr_t *mgr = chip->mgr;
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ snd_pcm_t *pcm = subs->pcm;
+ mixart_stream_t *stream;
+ mixart_pipe_t *pipe;
+ int err = 0;
+ int pcm_number;
+
+ down(&mgr->setup_mutex);
+
+ if ( pcm == chip->pcm ) {
+ pcm_number = MIXART_PCM_ANALOG;
+ runtime->hw = snd_mixart_analog_caps;
+ } else {
+ snd_assert ( pcm == chip->pcm_dig );
+ pcm_number = MIXART_PCM_DIGITAL;
+ runtime->hw = snd_mixart_digital_caps;
+ }
+
+ runtime->hw.channels_min = 2; /* for instance, no mono */
+
+ snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
+
+ /* get stream info */
+ stream = &(chip->capture_stream[pcm_number]);
+
+ if (stream->status != MIXART_STREAM_STATUS_FREE){
+ /* streams in use */
+ snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
+ err = -EBUSY;
+ goto _exit_open;
+ }
+
+ /* get pipe pointer (in pipe) */
+ pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);
+
+ if (pipe == NULL) {
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ /* start the pipe if necessary */
+ err = mixart_set_pipe_state(chip->mgr, pipe, 1);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "error starting pipe!\n");
+ snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ stream->pipe = pipe;
+ stream->pcm_number = pcm_number;
+ stream->status = MIXART_STREAM_STATUS_OPEN;
+ stream->substream = subs;
+ stream->channels = 0; /* not configured yet */
+
+ runtime->private_data = stream;
+
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
+
+ /* if a sample rate is already used, another stream cannot change */
+ if(mgr->ref_count_rate++) {
+ if(mgr->sample_rate) {
+ runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
+ }
+ }
+
+ _exit_open:
+ up(&mgr->setup_mutex);
+
+ return err;
+}
+
+
+
+static int snd_mixart_close(snd_pcm_substream_t *subs)
+{
+ mixart_t *chip = snd_pcm_substream_chip(subs);
+ mixart_mgr_t *mgr = chip->mgr;
+ mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+
+ down(&mgr->setup_mutex);
+
+ snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number);
+
+ /* sample rate released */
+ if(--mgr->ref_count_rate == 0) {
+ mgr->sample_rate = 0;
+ }
+
+ /* delete pipe */
+ if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {
+
+ snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number);
+ }
+
+ stream->pipe = NULL;
+ stream->status = MIXART_STREAM_STATUS_FREE;
+ stream->substream = NULL;
+
+ up(&mgr->setup_mutex);
+ return 0;
+}
+
+
+static snd_pcm_uframes_t snd_mixart_stream_pointer(snd_pcm_substream_t * subs)
+{
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ mixart_stream_t *stream = (mixart_stream_t*)runtime->private_data;
+
+ return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
+}
+
+
+
+static snd_pcm_ops_t snd_mixart_playback_ops = {
+ .open = snd_mixart_playback_open,
+ .close = snd_mixart_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = snd_mixart_prepare,
+ .hw_params = snd_mixart_hw_params,
+ .hw_free = snd_mixart_hw_free,
+ .trigger = snd_mixart_trigger,
+ .pointer = snd_mixart_stream_pointer,
+};
+
+static snd_pcm_ops_t snd_mixart_capture_ops = {
+ .open = snd_mixart_capture_open,
+ .close = snd_mixart_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = snd_mixart_prepare,
+ .hw_params = snd_mixart_hw_params,
+ .hw_free = snd_mixart_hw_free,
+ .trigger = snd_mixart_trigger,
+ .pointer = snd_mixart_stream_pointer,
+};
+
+static void preallocate_buffers(mixart_t *chip, snd_pcm_t *pcm)
+{
+#if 0
+ snd_pcm_substream_t *subs;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ int idx = 0;
+ for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
+ /* set up the unique device id with the chip index */
+ subs->dma_device.id = subs->pcm->device << 16 |
+ subs->stream << 8 | (subs->number + 1) |
+ (chip->chip_idx + 1) << 24;
+ }
+#endif
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->mgr->pci), 32*1024, 32*1024);
+}
+
+/*
+ */
+static int snd_mixart_pcm_analog(mixart_t *chip)
+{
+ int err;
+ snd_pcm_t *pcm;
+ char name[32];
+
+ sprintf(name, "miXart analog %d", chip->chip_idx);
+ if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
+ MIXART_PLAYBACK_STREAMS,
+ MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
+ snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx);
+ return err;
+ }
+
+ pcm->private_data = chip;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
+
+ pcm->info_flags = 0;
+ strcpy(pcm->name, name);
+
+ preallocate_buffers(chip, pcm);
+
+ chip->pcm = pcm;
+ return 0;
+}
+
+
+/*
+ */
+static int snd_mixart_pcm_digital(mixart_t *chip)
+{
+ int err;
+ snd_pcm_t *pcm;
+ char name[32];
+
+ sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
+ if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
+ MIXART_PLAYBACK_STREAMS,
+ MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
+ snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx);
+ return err;
+ }
+
+ pcm->private_data = chip;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
+
+ pcm->info_flags = 0;
+ strcpy(pcm->name, name);
+
+ preallocate_buffers(chip, pcm);
+
+ chip->pcm_dig = pcm;
+ return 0;
+}
+
+static int snd_mixart_chip_free(mixart_t *chip)
+{
+ kfree(chip);
+ return 0;
+}
+
+static int snd_mixart_chip_dev_free(snd_device_t *device)
+{
+ mixart_t *chip = device->device_data;
+ return snd_mixart_chip_free(chip);
+}
+
+
+/*
+ */
+static int __devinit snd_mixart_create(mixart_mgr_t *mgr, snd_card_t *card, int idx)
+{
+ int err;
+ mixart_t *chip;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_mixart_chip_dev_free,
+ };
+
+ mgr->chip[idx] = chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (! chip) {
+ snd_printk(KERN_ERR "cannot allocate chip\n");
+ return -ENOMEM;
+ }
+
+ chip->card = card;
+ chip->chip_idx = idx;
+ chip->mgr = mgr;
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_mixart_chip_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &mgr->pci->dev);
+
+ return 0;
+}
+
+int snd_mixart_create_pcm(mixart_t* chip)
+{
+ int err;
+
+ err = snd_mixart_pcm_analog(chip);
+ if (err < 0)
+ return err;
+
+ if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+
+ err = snd_mixart_pcm_digital(chip);
+ if (err < 0)
+ return err;
+ }
+ return err;
+}
+
+
+/*
+ * release all the cards assigned to a manager instance
+ */
+static int snd_mixart_free(mixart_mgr_t *mgr)
+{
+ unsigned int i;
+
+ for (i = 0; i < mgr->num_cards; i++) {
+ if (mgr->chip[i])
+ snd_card_free(mgr->chip[i]->card);
+ }
+
+ /* stop mailbox */
+ snd_mixart_exit_mailbox(mgr);
+
+ /* release irq */
+ if (mgr->irq >= 0)
+ free_irq(mgr->irq, (void *)mgr);
+
+ /* reset board if some firmware was loaded */
+ if(mgr->dsp_loaded) {
+ snd_mixart_reset_board(mgr);
+ snd_printdd("reset miXart !\n");
+ }
+
+ /* release the i/o ports */
+ for (i = 0; i < 2; i++) {
+ if (mgr->mem[i].virt)
+ iounmap(mgr->mem[i].virt);
+ }
+ pci_release_regions(mgr->pci);
+
+ /* free flowarray */
+ if(mgr->flowinfo.area) {
+ snd_dma_free_pages(&mgr->flowinfo);
+ mgr->flowinfo.area = NULL;
+ }
+ /* free bufferarray */
+ if(mgr->bufferinfo.area) {
+ snd_dma_free_pages(&mgr->bufferinfo);
+ mgr->bufferinfo.area = NULL;
+ }
+
+ pci_disable_device(mgr->pci);
+ kfree(mgr);
+ return 0;
+}
+
+/*
+ * proc interface
+ */
+static long long snd_mixart_BA0_llseek(snd_info_entry_t *entry,
+ void *private_file_data,
+ struct file *file,
+ long long offset,
+ int orig)
+{
+ offset = offset & ~3; /* 4 bytes aligned */
+
+ switch(orig) {
+ case 0: /* SEEK_SET */
+ file->f_pos = offset;
+ break;
+ case 1: /* SEEK_CUR */
+ file->f_pos += offset;
+ break;
+ case 2: /* SEEK_END, offset is negative */
+ file->f_pos = MIXART_BA0_SIZE + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if(file->f_pos > MIXART_BA0_SIZE)
+ file->f_pos = MIXART_BA0_SIZE;
+ return file->f_pos;
+}
+
+static long long snd_mixart_BA1_llseek(snd_info_entry_t *entry,
+ void *private_file_data,
+ struct file *file,
+ long long offset,
+ int orig)
+{
+ offset = offset & ~3; /* 4 bytes aligned */
+
+ switch(orig) {
+ case 0: /* SEEK_SET */
+ file->f_pos = offset;
+ break;
+ case 1: /* SEEK_CUR */
+ file->f_pos += offset;
+ break;
+ case 2: /* SEEK_END, offset is negative */
+ file->f_pos = MIXART_BA1_SIZE + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if(file->f_pos > MIXART_BA1_SIZE)
+ file->f_pos = MIXART_BA1_SIZE;
+ return file->f_pos;
+}
+
+/*
+ mixart_BA0 proc interface for BAR 0 - read callback
+ */
+static long snd_mixart_BA0_read(snd_info_entry_t *entry, void *file_private_data,
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
+{
+ mixart_mgr_t *mgr = entry->private_data;
+
+ count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
+ if(count <= 0)
+ return 0;
+ if(pos + count > MIXART_BA0_SIZE)
+ count = (long)(MIXART_BA0_SIZE - pos);
+ if(copy_to_user_fromio(buf, MIXART_MEM( mgr, pos ), count))
+ return -EFAULT;
+ return count;
+}
+
+/*
+ mixart_BA1 proc interface for BAR 1 - read callback
+ */
+static long snd_mixart_BA1_read(snd_info_entry_t *entry, void *file_private_data,
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
+{
+ mixart_mgr_t *mgr = entry->private_data;
+
+ count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
+ if(count <= 0)
+ return 0;
+ if(pos + count > MIXART_BA1_SIZE)
+ count = (long)(MIXART_BA1_SIZE - pos);
+ if(copy_to_user_fromio(buf, MIXART_REG( mgr, pos ), count))
+ return -EFAULT;
+ return count;
+}
+
+static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
+ .read = snd_mixart_BA0_read,
+ .llseek = snd_mixart_BA0_llseek
+};
+
+static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
+ .read = snd_mixart_BA1_read,
+ .llseek = snd_mixart_BA1_llseek
+};
+
+
+static void snd_mixart_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ mixart_t *chip = entry->private_data;
+ u32 ref;
+
+ snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx);
+
+ /* stats available when embedded OS is running */
+ if (chip->mgr->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) {
+ snd_iprintf(buffer, "- hardware -\n");
+ switch (chip->mgr->board_type ) {
+ case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break;
+ case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break;
+ case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break;
+ default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break;
+ }
+
+ snd_iprintf(buffer, "- system load -\n");
+
+ /* get perf reference */
+
+ ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET));
+
+ if (ref) {
+ u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref;
+ u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref;
+ u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref;
+
+ snd_iprintf(buffer, "\tstreaming : %d\n", streaming);
+ snd_iprintf(buffer, "\tmailbox : %d\n", mailbox);
+ snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr);
+ }
+ } /* endif elf loaded */
+}
+
+static void __devinit snd_mixart_proc_init(mixart_t *chip)
+{
+ snd_info_entry_t *entry;
+
+ /* text interface to read perf and temp meters */
+ if (! snd_card_proc_new(chip->card, "board_info", &entry)) {
+ entry->private_data = chip;
+ entry->c.text.read_size = 1024;
+ entry->c.text.read = snd_mixart_proc_read;
+ }
+
+ if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = chip->mgr;
+ entry->c.ops = &snd_mixart_proc_ops_BA0;
+ entry->size = MIXART_BA0_SIZE;
+ }
+ if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = chip->mgr;
+ entry->c.ops = &snd_mixart_proc_ops_BA1;
+ entry->size = MIXART_BA1_SIZE;
+ }
+}
+/* end of proc interface */
+
+
+/*
+ * probe function - creates the card manager
+ */
+static int __devinit snd_mixart_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ mixart_mgr_t *mgr;
+ unsigned int i;
+ int err;
+ size_t size;
+
+ /*
+ */
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (! enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ pci_set_master(pci);
+
+ /* check if we can restrict PCI DMA transfers to 32 bits */
+ if (pci_set_dma_mask(pci, 0xffffffff) < 0) {
+ snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ /*
+ */
+ mgr = kcalloc(1, sizeof(*mgr), GFP_KERNEL);
+ if (! mgr) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ mgr->pci = pci;
+ mgr->irq = -1;
+
+ /* resource assignment */
+ if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
+ kfree(mgr);
+ pci_disable_device(pci);
+ return err;
+ }
+ for (i = 0; i < 2; i++) {
+ mgr->mem[i].phys = pci_resource_start(pci, i);
+ mgr->mem[i].virt = ioremap_nocache(mgr->mem[i].phys,
+ pci_resource_len(pci, i));
+ }
+
+ if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ snd_mixart_free(mgr);
+ return -EBUSY;
+ }
+ mgr->irq = pci->irq;
+
+ sprintf(mgr->shortname, "Digigram miXart");
+ sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq);
+
+ /* ISR spinlock */
+ spin_lock_init(&mgr->lock);
+
+ /* init mailbox */
+ mgr->msg_fifo_readptr = 0;
+ mgr->msg_fifo_writeptr = 0;
+
+ spin_lock_init(&mgr->msg_lock);
+ init_MUTEX(&mgr->msg_mutex);
+ init_waitqueue_head(&mgr->msg_sleep);
+ atomic_set(&mgr->msg_processed, 0);
+
+ /* init setup mutex*/
+ init_MUTEX(&mgr->setup_mutex);
+
+ /* init message taslket */
+ tasklet_init( &mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr);
+
+ /* card assignment */
+ mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */
+ for (i = 0; i < mgr->num_cards; i++) {
+ snd_card_t *card;
+ char tmpid[16];
+ int idx;
+
+ if (index[dev] < 0)
+ idx = index[dev];
+ else
+ idx = index[dev] + i;
+ snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i);
+ card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
+
+ if (! card) {
+ snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
+ snd_mixart_free(mgr);
+ return -ENOMEM;
+ }
+
+ strcpy(card->driver, CARD_NAME);
+ sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i);
+ sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
+
+ if ((err = snd_mixart_create(mgr, card, i)) < 0) {
+ snd_mixart_free(mgr);
+ return err;
+ }
+
+ if(i==0) {
+ /* init proc interface only for chip0 */
+ snd_mixart_proc_init(mgr->chip[i]);
+ }
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_mixart_free(mgr);
+ return err;
+ }
+ }
+
+ /* init firmware status (mgr->dsp_loaded reset in hwdep_new) */
+ mgr->board_type = MIXART_DAUGHTER_TYPE_NONE;
+
+ /* create array of streaminfo */
+ size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) );
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ size, &mgr->flowinfo) < 0) {
+ snd_mixart_free(mgr);
+ return -ENOMEM;
+ }
+ /* init streaminfo_array */
+ memset(mgr->flowinfo.area, 0, size);
+
+ /* create array of bufferinfo */
+ size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) );
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ size, &mgr->bufferinfo) < 0) {
+ snd_mixart_free(mgr);
+ return -ENOMEM;
+ }
+ /* init bufferinfo_array */
+ memset(mgr->bufferinfo.area, 0, size);
+
+ /* set up firmware */
+ err = snd_mixart_setup_firmware(mgr);
+ if (err < 0) {
+ snd_mixart_free(mgr);
+ return err;
+ }
+
+ pci_set_drvdata(pci, mgr);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_mixart_remove(struct pci_dev *pci)
+{
+ snd_mixart_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Digigram miXart",
+ .id_table = snd_mixart_ids,
+ .probe = snd_mixart_probe,
+ .remove = __devexit_p(snd_mixart_remove),
+};
+
+static int __init alsa_card_mixart_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_mixart_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_mixart_init)
+module_exit(alsa_card_mixart_exit)
diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h
new file mode 100644
index 00000000000..f87152f94c0
--- /dev/null
+++ b/sound/pci/mixart/mixart.h
@@ -0,0 +1,242 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * main header file
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_H
+#define __SOUND_MIXART_H
+
+#include <linux/interrupt.h>
+#include <sound/pcm.h>
+
+#define MIXART_DRIVER_VERSION 0x000100 /* 0.1.0 */
+
+
+/*
+ */
+
+#define mixart_t_magic 0xa17a3e01
+#define mixart_mgr_t_magic 0xa17a3e02
+
+typedef struct snd_mixart mixart_t;
+typedef struct snd_mixart_mgr mixart_mgr_t;
+
+typedef struct snd_mixart_stream mixart_stream_t;
+typedef struct snd_mixart_pipe mixart_pipe_t;
+
+typedef struct mixart_bufferinfo mixart_bufferinfo_t;
+typedef struct mixart_flowinfo mixart_flowinfo_t;
+typedef struct mixart_uid mixart_uid_t;
+
+struct mixart_uid
+{
+ u32 object_id;
+ u32 desc;
+};
+
+struct mem_area {
+ unsigned long phys;
+ void __iomem *virt;
+ struct resource *res;
+};
+
+
+typedef struct mixart_route mixart_route_t;
+struct mixart_route {
+ unsigned char connected;
+ unsigned char phase_inv;
+ int volume;
+};
+
+
+/* firmware status codes */
+#define MIXART_MOTHERBOARD_XLX_INDEX 0
+#define MIXART_MOTHERBOARD_ELF_INDEX 1
+#define MIXART_AESEBUBOARD_XLX_INDEX 2
+#define MIXART_HARDW_FILES_MAX_INDEX 3 /* xilinx, elf, AESEBU xilinx */
+
+#define MIXART_MAX_CARDS 4
+#define MSG_FIFO_SIZE 16
+
+#define MIXART_MAX_PHYS_CONNECTORS (MIXART_MAX_CARDS * 2 * 2) /* 4 * stereo * (analog+digital) */
+
+struct snd_mixart_mgr {
+ unsigned int num_cards;
+ mixart_t *chip[MIXART_MAX_CARDS];
+
+ struct pci_dev *pci;
+
+ int irq;
+
+ /* memory-maps */
+ struct mem_area mem[2];
+
+ /* share the name */
+ char shortname[32]; /* short name of this soundcard */
+ char longname[80]; /* name of this soundcard */
+
+ /* message tasklet */
+ struct tasklet_struct msg_taskq;
+
+ /* one and only blocking message or notification may be pending */
+ u32 pending_event;
+ wait_queue_head_t msg_sleep;
+
+ /* messages stored for tasklet */
+ u32 msg_fifo[MSG_FIFO_SIZE];
+ int msg_fifo_readptr;
+ int msg_fifo_writeptr;
+ atomic_t msg_processed; /* number of messages to be processed in takslet */
+
+ spinlock_t lock; /* interrupt spinlock */
+ spinlock_t msg_lock; /* mailbox spinlock */
+ struct semaphore msg_mutex; /* mutex for blocking_requests */
+
+ struct semaphore setup_mutex; /* mutex used in hw_params, open and close */
+
+ /* hardware interface */
+ unsigned int dsp_loaded; /* bit flags of loaded dsp indices */
+ unsigned int board_type; /* read from embedded once elf file is loaded, 250 = miXart8, 251 = with AES, 252 = with Cobranet */
+
+ struct snd_dma_buffer flowinfo;
+ struct snd_dma_buffer bufferinfo;
+
+ mixart_uid_t uid_console_manager;
+ int sample_rate;
+ int ref_count_rate;
+
+ struct semaphore mixer_mutex; /* mutex for mixer */
+
+};
+
+
+#define MIXART_STREAM_STATUS_FREE 0
+#define MIXART_STREAM_STATUS_OPEN 1
+#define MIXART_STREAM_STATUS_RUNNING 2
+#define MIXART_STREAM_STATUS_DRAINING 3
+#define MIXART_STREAM_STATUS_PAUSE 4
+
+#define MIXART_PLAYBACK_STREAMS 4
+#define MIXART_CAPTURE_STREAMS 1
+
+#define MIXART_PCM_ANALOG 0
+#define MIXART_PCM_DIGITAL 1
+#define MIXART_PCM_TOTAL 2
+
+#define MIXART_MAX_STREAM_PER_CARD (MIXART_PCM_TOTAL * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS) )
+
+
+#define MIXART_NOTIFY_CARD_MASK 0xF000
+#define MIXART_NOTIFY_CARD_OFFSET 12
+#define MIXART_NOTIFY_PCM_MASK 0x0F00
+#define MIXART_NOTIFY_PCM_OFFSET 8
+#define MIXART_NOTIFY_CAPT_MASK 0x0080
+#define MIXART_NOTIFY_SUBS_MASK 0x007F
+
+
+struct snd_mixart_stream {
+ snd_pcm_substream_t *substream;
+ mixart_pipe_t *pipe;
+ int pcm_number;
+
+ int status; /* nothing, running, draining */
+
+ u64 abs_period_elapsed; /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */
+ u32 buf_periods; /* periods counter in the buffer (< runtime->periods) */
+ u32 buf_period_frag; /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */
+
+ int channels;
+};
+
+
+enum mixart_pipe_status {
+ PIPE_UNDEFINED,
+ PIPE_STOPPED,
+ PIPE_RUNNING,
+ PIPE_CLOCK_SET
+};
+
+struct snd_mixart_pipe {
+ mixart_uid_t group_uid; /* id of the pipe, as returned by embedded */
+ int stream_count;
+ mixart_uid_t uid_left_connector; /* UID's for the audio connectors */
+ mixart_uid_t uid_right_connector;
+ enum mixart_pipe_status status;
+ int references; /* number of subs openned */
+ int monitoring; /* pipe used for monitoring issue */
+};
+
+
+struct snd_mixart {
+ snd_card_t *card;
+ mixart_mgr_t *mgr;
+ int chip_idx; /* zero based */
+ snd_hwdep_t *hwdep; /* DSP loader, only for the first card */
+
+ snd_pcm_t *pcm; /* PCM analog i/o */
+ snd_pcm_t *pcm_dig; /* PCM digital i/o */
+
+ /* allocate stereo pipe for instance */
+ mixart_pipe_t pipe_in_ana;
+ mixart_pipe_t pipe_out_ana;
+
+ /* if AES/EBU daughter board is available, additional pipes possible on pcm_dig */
+ mixart_pipe_t pipe_in_dig;
+ mixart_pipe_t pipe_out_dig;
+
+ mixart_stream_t playback_stream[MIXART_PCM_TOTAL][MIXART_PLAYBACK_STREAMS]; /* 0 = pcm, 1 = pcm_dig */
+ mixart_stream_t capture_stream[MIXART_PCM_TOTAL]; /* 0 = pcm, 1 = pcm_dig */
+
+ /* UID's for the physical io's */
+ mixart_uid_t uid_out_analog_physio;
+ mixart_uid_t uid_in_analog_physio;
+
+ int analog_playback_active[2]; /* Mixer : Master Playback active (!mute) */
+ int analog_playback_volume[2]; /* Mixer : Master Playback Volume */
+ int analog_capture_volume[2]; /* Mixer : Master Capture Volume */
+ int digital_playback_active[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Active [(analog+AES output)*streams][stereo]*/
+ int digital_playback_volume[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Volume [(analog+AES output)*streams][stereo]*/
+ int digital_capture_volume[2][2]; /* Mixer : Digital Capture Volume [analog+AES output][stereo] */
+ int monitoring_active[2]; /* Mixer : Monitoring Active */
+ int monitoring_volume[2]; /* Mixer : Monitoring Volume */
+};
+
+struct mixart_bufferinfo
+{
+ u32 buffer_address;
+ u32 reserved[5];
+ u32 available_length;
+ u32 buffer_id;
+};
+
+struct mixart_flowinfo
+{
+ u32 bufferinfo_array_phy_address;
+ u32 reserved[11];
+ u32 bufferinfo_count;
+ u32 capture;
+};
+
+/* exported */
+int snd_mixart_create_pcm(mixart_t* chip);
+mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring);
+int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring);
+
+#endif /* __SOUND_MIXART_H */
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
new file mode 100644
index 00000000000..ba0027f5094
--- /dev/null
+++ b/sound/pci/mixart/mixart_core.c
@@ -0,0 +1,588 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * low level interface with interrupt handling and mail box implementation
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_hwdep.h"
+#include "mixart_core.h"
+
+
+#define MSG_TIMEOUT_JIFFIES (400 * HZ) / 1000 /* 400 ms */
+
+#define MSG_DESCRIPTOR_SIZE 0x24
+#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4)
+
+#define MSG_DEFAULT_SIZE 512
+
+#define MSG_TYPE_MASK 0x00000003 /* mask for following types */
+#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */
+#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */
+#define MSG_TYPE_REQUEST 2 /* driver -> embedded (request will get an answer back) */
+#define MSG_TYPE_ANSWER 3 /* embedded -> driver */
+#define MSG_CANCEL_NOTIFY_MASK 0x80000000 /* this bit is set for a notification that has been canceled */
+
+
+static int retrieve_msg_frame(mixart_mgr_t *mgr, u32 *msg_frame)
+{
+ /* read the message frame fifo */
+ u32 headptr, tailptr;
+
+ tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
+ headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD));
+
+ if (tailptr == headptr)
+ return 0; /* no message posted */
+
+ snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */
+ snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */
+
+ *msg_frame = readl_be(MIXART_MEM(mgr, tailptr));
+
+ /* increment the tail index */
+ tailptr += 4;
+ if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
+ tailptr = MSG_OUTBOUND_POST_STACK;
+ writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
+
+ return 1;
+}
+
+static int get_msg(mixart_mgr_t *mgr, mixart_msg_t *resp, u32 msg_frame_address )
+{
+ unsigned long flags;
+ u32 headptr;
+ u32 size;
+ int err;
+#ifndef __BIG_ENDIAN
+ unsigned int i;
+#endif
+
+ spin_lock_irqsave(&mgr->msg_lock, flags);
+ err = 0;
+
+ /* copy message descriptor from miXart to driver */
+ size = readl_be(MIXART_MEM(mgr, msg_frame_address)); /* size of descriptor + response */
+ resp->message_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 4)); /* dwMessageID */
+ resp->uid.object_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 8)); /* uidDest */
+ resp->uid.desc = readl_be(MIXART_MEM(mgr, msg_frame_address + 12)); /* */
+
+ if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) {
+ err = -EINVAL;
+ snd_printk(KERN_ERR "problem with response size = %d\n", size);
+ goto _clean_exit;
+ }
+ size -= MSG_DESCRIPTOR_SIZE;
+
+ memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size);
+ resp->size = size;
+
+ /* swap if necessary */
+#ifndef __BIG_ENDIAN
+ size /= 4; /* u32 size */
+ for(i=0; i < size; i++) {
+ ((u32*)resp->data)[i] = be32_to_cpu(((u32*)resp->data)[i]);
+ }
+#endif
+
+ /*
+ * free message frame address
+ */
+ headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
+
+ if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
+ err = -EINVAL;
+ goto _clean_exit;
+ }
+
+ /* give address back to outbound fifo */
+ writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
+
+ /* increment the outbound free head */
+ headptr += 4;
+ if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
+ headptr = MSG_OUTBOUND_FREE_STACK;
+
+ writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
+
+ _clean_exit:
+ spin_unlock_irqrestore(&mgr->msg_lock, flags);
+
+ return err;
+}
+
+
+/*
+ * send a message to miXart. return: the msg_frame used for this message
+ */
+/* call with mgr->msg_lock held! */
+static int send_msg( mixart_mgr_t *mgr,
+ mixart_msg_t *msg,
+ int max_answersize,
+ int mark_pending,
+ u32 *msg_event)
+{
+ u32 headptr, tailptr;
+ u32 msg_frame_address;
+ int err, i;
+
+ snd_assert(msg->size % 4 == 0, return -EINVAL);
+
+ err = 0;
+
+ /* get message frame address */
+ tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
+ headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD));
+
+ if (tailptr == headptr) {
+ snd_printk(KERN_ERR "error: no message frame available\n");
+ return -EBUSY;
+ }
+
+ if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
+ return -EINVAL;
+ }
+
+ msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr));
+ writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */
+
+ /* increment the inbound free tail */
+ tailptr += 4;
+ if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
+ tailptr = MSG_INBOUND_FREE_STACK;
+
+ writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
+
+ /* TODO : use memcpy_toio() with intermediate buffer to copy the message */
+
+ /* copy message descriptor to card memory */
+ writel_be( msg->size + MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address) ); /* size of descriptor + request */
+ writel_be( msg->message_id , MIXART_MEM(mgr, msg_frame_address + 4) ); /* dwMessageID */
+ writel_be( msg->uid.object_id, MIXART_MEM(mgr, msg_frame_address + 8) ); /* uidDest */
+ writel_be( msg->uid.desc, MIXART_MEM(mgr, msg_frame_address + 12) ); /* */
+ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */
+ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */
+ writel_be( msg->size, MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */
+ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */
+ writel_be( 0, MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */
+ writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */
+
+ /* copy message data to card memory */
+ for( i=0; i < msg->size; i+=4 ) {
+ writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i) );
+ }
+
+ if( mark_pending ) {
+ if( *msg_event ) {
+ /* the pending event is the notification we wait for ! */
+ mgr->pending_event = *msg_event;
+ }
+ else {
+ /* the pending event is the answer we wait for (same address than the request)! */
+ mgr->pending_event = msg_frame_address;
+
+ /* copy address back to caller */
+ *msg_event = msg_frame_address;
+ }
+ }
+
+ /* mark the frame as a request (will have an answer) */
+ msg_frame_address |= MSG_TYPE_REQUEST;
+
+ /* post the frame */
+ headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
+
+ if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) {
+ return -EINVAL;
+ }
+
+ writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
+
+ /* increment the inbound post head */
+ headptr += 4;
+ if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
+ headptr = MSG_INBOUND_POST_STACK;
+
+ writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
+
+ return 0;
+}
+
+
+int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data)
+{
+ mixart_msg_t resp;
+ u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */
+ int err;
+ wait_queue_t wait;
+ long timeout;
+
+ down(&mgr->msg_mutex);
+
+ init_waitqueue_entry(&wait, current);
+
+ spin_lock_irq(&mgr->msg_lock);
+ /* send the message */
+ err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */
+ if (err) {
+ spin_unlock_irq(&mgr->msg_lock);
+ up(&mgr->msg_mutex);
+ return err;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&mgr->msg_sleep, &wait);
+ spin_unlock_irq(&mgr->msg_lock);
+ timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
+ remove_wait_queue(&mgr->msg_sleep, &wait);
+
+ if (! timeout) {
+ /* error - no ack */
+ up(&mgr->msg_mutex);
+ snd_printk(KERN_ERR "error: no reponse on msg %x\n", msg_frame);
+ return -EIO;
+ }
+
+ /* retrieve the answer into the same mixart_msg_t */
+ resp.message_id = 0;
+ resp.uid = (mixart_uid_t){0,0};
+ resp.data = resp_data;
+ resp.size = max_resp_size;
+
+ err = get_msg(mgr, &resp, msg_frame);
+
+ if( request->message_id != resp.message_id )
+ snd_printk(KERN_ERR "REPONSE ERROR!\n");
+
+ up(&mgr->msg_mutex);
+ return err;
+}
+
+
+int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event)
+{
+ int err;
+ wait_queue_t wait;
+ long timeout;
+
+ snd_assert(notif_event != 0, return -EINVAL);
+ snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL);
+ snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL);
+
+ down(&mgr->msg_mutex);
+
+ init_waitqueue_entry(&wait, current);
+
+ spin_lock_irq(&mgr->msg_lock);
+ /* send the message */
+ err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, &notif_event); /* send and mark the notification event pending */
+ if(err) {
+ spin_unlock_irq(&mgr->msg_lock);
+ up(&mgr->msg_mutex);
+ return err;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&mgr->msg_sleep, &wait);
+ spin_unlock_irq(&mgr->msg_lock);
+ timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
+ remove_wait_queue(&mgr->msg_sleep, &wait);
+
+ if (! timeout) {
+ /* error - no ack */
+ up(&mgr->msg_mutex);
+ snd_printk(KERN_ERR "error: notification %x not received\n", notif_event);
+ return -EIO;
+ }
+
+ up(&mgr->msg_mutex);
+ return 0;
+}
+
+
+int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request)
+{
+ u32 message_frame;
+ unsigned long flags;
+ int err;
+
+ /* just send the message (do not mark it as a pending one) */
+ spin_lock_irqsave(&mgr->msg_lock, flags);
+ err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);
+ spin_unlock_irqrestore(&mgr->msg_lock, flags);
+
+ /* the answer will be handled by snd_mixart_msg_tasklet() */
+ atomic_inc(&mgr->msg_processed);
+
+ return err;
+}
+
+
+/* common buffer of tasklet and interrupt to send/receive messages */
+static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4];
+
+
+void snd_mixart_msg_tasklet( unsigned long arg)
+{
+ mixart_mgr_t *mgr = ( mixart_mgr_t*)(arg);
+ mixart_msg_t resp;
+ u32 msg, addr, type;
+ int err;
+
+ spin_lock(&mgr->lock);
+
+ while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) {
+ msg = mgr->msg_fifo[mgr->msg_fifo_readptr];
+ mgr->msg_fifo_readptr++;
+ mgr->msg_fifo_readptr %= MSG_FIFO_SIZE;
+
+ /* process the message ... */
+ addr = msg & ~MSG_TYPE_MASK;
+ type = msg & MSG_TYPE_MASK;
+
+ switch (type) {
+ case MSG_TYPE_ANSWER:
+ /* answer to a message on that we did not wait for (send_msg_nonblock) */
+ resp.message_id = 0;
+ resp.data = mixart_msg_data;
+ resp.size = sizeof(mixart_msg_data);
+ err = get_msg(mgr, &resp, addr);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "tasklet: error(%d) reading mf %x\n", err, msg);
+ break;
+ }
+
+ switch(resp.message_id) {
+ case MSG_STREAM_START_INPUT_STAGE_PACKET:
+ case MSG_STREAM_START_OUTPUT_STAGE_PACKET:
+ case MSG_STREAM_STOP_INPUT_STAGE_PACKET:
+ case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET:
+ if(mixart_msg_data[0])
+ snd_printk(KERN_ERR "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", mixart_msg_data[0]);
+ break;
+ default:
+ snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n",
+ msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size);
+ break;
+ }
+ break;
+ case MSG_TYPE_NOTIFY:
+ /* msg contains no address ! do not get_msg() ! */
+ case MSG_TYPE_COMMAND:
+ /* get_msg() necessary */
+ default:
+ snd_printk(KERN_ERR "tasklet doesn't know what to do with message %x\n", msg);
+ } /* switch type */
+
+ /* decrement counter */
+ atomic_dec(&mgr->msg_processed);
+
+ } /* while there is a msg in fifo */
+
+ spin_unlock(&mgr->lock);
+}
+
+
+irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ mixart_mgr_t *mgr = dev_id;
+ int err;
+ mixart_msg_t resp;
+
+ u32 msg;
+ u32 it_reg;
+
+ spin_lock(&mgr->lock);
+
+ it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET));
+ if( !(it_reg & MIXART_OIDI) ) {
+ /* this device did not cause the interrupt */
+ spin_unlock(&mgr->lock);
+ return IRQ_NONE;
+ }
+
+ /* mask all interrupts */
+ writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET));
+
+ /* outdoorbell register clear */
+ it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
+ writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
+
+ /* clear interrupt */
+ writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) );
+
+ /* process interrupt */
+ while (retrieve_msg_frame(mgr, &msg)) {
+
+ switch (msg & MSG_TYPE_MASK) {
+ case MSG_TYPE_COMMAND:
+ resp.message_id = 0;
+ resp.data = mixart_msg_data;
+ resp.size = sizeof(mixart_msg_data);
+ err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "interrupt: error(%d) reading mf %x\n", err, msg);
+ break;
+ }
+
+ if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) {
+ int i;
+ mixart_timer_notify_t *notify = (mixart_timer_notify_t*)mixart_msg_data;
+
+ for(i=0; i<notify->stream_count; i++) {
+
+ u32 buffer_id = notify->streams[i].buffer_id;
+ unsigned int chip_number = (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */
+ unsigned int pcm_number = (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET; /* pcm0 to 3 */
+ unsigned int sub_number = buffer_id & MIXART_NOTIFY_SUBS_MASK; /* 0 to MIXART_PLAYBACK_STREAMS */
+ unsigned int is_capture = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0); /* playback == 0 / capture == 1 */
+
+ mixart_t *chip = mgr->chip[chip_number];
+ mixart_stream_t *stream;
+
+ if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) {
+ snd_printk(KERN_ERR "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n",
+ buffer_id, notify->streams[i].sample_pos_low_part);
+ break;
+ }
+
+ if (is_capture)
+ stream = &chip->capture_stream[pcm_number];
+ else
+ stream = &chip->playback_stream[pcm_number][sub_number];
+
+ if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) {
+ snd_pcm_runtime_t *runtime = stream->substream->runtime;
+ int elapsed = 0;
+ u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32;
+ sample_count |= notify->streams[i].sample_pos_low_part;
+
+ while (1) {
+ u64 new_elapse_pos = stream->abs_period_elapsed + runtime->period_size;
+
+ if (new_elapse_pos > sample_count) {
+ break; /* while */
+ }
+ else {
+ elapsed = 1;
+ stream->buf_periods++;
+ if (stream->buf_periods >= runtime->periods)
+ stream->buf_periods = 0;
+
+ stream->abs_period_elapsed = new_elapse_pos;
+ }
+ }
+ stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed );
+
+ if(elapsed) {
+ spin_unlock(&mgr->lock);
+ snd_pcm_period_elapsed(stream->substream);
+ spin_lock(&mgr->lock);
+ }
+ }
+ }
+ break;
+ }
+ if(resp.message_id == MSG_SERVICES_REPORT_TRACES) {
+ if(resp.size > 1) {
+#ifndef __BIG_ENDIAN
+ /* Traces are text: the swapped msg_data has to be swapped back ! */
+ int i;
+ for(i=0; i<(resp.size/4); i++) {
+ (mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]);
+ }
+#endif
+ ((char*)mixart_msg_data)[resp.size - 1] = 0;
+ snd_printdd("MIXART TRACE : %s\n", (char*)mixart_msg_data);
+ }
+ break;
+ }
+
+ snd_printdd("command %x not handled\n", resp.message_id);
+ break;
+
+ case MSG_TYPE_NOTIFY:
+ if(msg & MSG_CANCEL_NOTIFY_MASK) {
+ msg &= ~MSG_CANCEL_NOTIFY_MASK;
+ snd_printk(KERN_ERR "canceled notification %x !\n", msg);
+ }
+ /* no break, continue ! */
+ case MSG_TYPE_ANSWER:
+ /* answer or notification to a message we are waiting for*/
+ spin_lock(&mgr->msg_lock);
+ if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {
+ wake_up(&mgr->msg_sleep);
+ mgr->pending_event = 0;
+ }
+ /* answer to a message we did't want to wait for */
+ else {
+ mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
+ mgr->msg_fifo_writeptr++;
+ mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
+ tasklet_hi_schedule(&mgr->msg_taskq);
+ }
+ spin_unlock(&mgr->msg_lock);
+ break;
+ case MSG_TYPE_REQUEST:
+ default:
+ snd_printdd("interrupt received request %x\n", msg);
+ /* TODO : are there things to do here ? */
+ break;
+ } /* switch on msg type */
+ } /* while there are msgs */
+
+ /* allow interrupt again */
+ writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+
+ spin_unlock(&mgr->lock);
+
+ return IRQ_HANDLED;
+}
+
+
+void snd_mixart_init_mailbox(mixart_mgr_t *mgr)
+{
+ writel( 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) );
+ writel( 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) );
+
+ /* allow outbound messagebox to generate interrupts */
+ if(mgr->irq >= 0) {
+ writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+ }
+ return;
+}
+
+void snd_mixart_exit_mailbox(mixart_mgr_t *mgr)
+{
+ /* no more interrupts on outbound messagebox */
+ writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+ return;
+}
+
+void snd_mixart_reset_board(mixart_mgr_t *mgr)
+{
+ /* reset miXart */
+ writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) );
+ return;
+}
diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h
new file mode 100644
index 00000000000..99450eba15c
--- /dev/null
+++ b/sound/pci/mixart/mixart_core.h
@@ -0,0 +1,607 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * low level interface with interrupt handling and mail box implementation
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_CORE_H
+#define __SOUND_MIXART_CORE_H
+
+
+enum mixart_message_id {
+ MSG_CONNECTOR_GET_AUDIO_INFO = 0x050008,
+ MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009,
+ MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A,
+
+ MSG_CONSOLE_MANAGER = 0x070000,
+ MSG_CONSOLE_GET_CLOCK_UID = 0x070003,
+
+ MSG_PHYSICALIO_SET_LEVEL = 0x0F0008,
+
+ MSG_STREAM_ADD_INPUT_GROUP = 0x130000,
+ MSG_STREAM_ADD_OUTPUT_GROUP = 0x130001,
+ MSG_STREAM_DELETE_GROUP = 0x130004,
+ MSG_STREAM_START_STREAM_GRP_PACKET = 0x130006,
+ MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130007,
+ MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130008,
+ MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130009,
+ MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x13000A,
+ MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x13000B,
+ MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F,
+ MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010,
+ MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015,
+ MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017,
+
+ MSG_SYSTEM_FIRST_ID = 0x160000,
+ MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E,
+ MSG_SYSTEM_ENUM_PLAY_CONNECTOR = 0x160017,
+ MSG_SYSTEM_ENUM_RECORD_CONNECTOR = 0x160018,
+ MSG_SYSTEM_WAIT_SYNCHRO_CMD = 0x16002C,
+ MSG_SYSTEM_SEND_SYNCHRO_CMD = 0x16002D,
+
+ MSG_SERVICES_TIMER_NOTIFY = 0x1D0404,
+ MSG_SERVICES_REPORT_TRACES = 0x1D0700,
+
+ MSG_CLOCK_CHECK_PROPERTIES = 0x200001,
+ MSG_CLOCK_SET_PROPERTIES = 0x200002,
+};
+
+
+typedef struct mixart_msg mixart_msg_t;
+struct mixart_msg
+{
+ u32 message_id;
+ mixart_uid_t uid;
+ void* data;
+ size_t size;
+};
+
+/* structs used to communicate with miXart */
+
+typedef struct mixart_enum_connector_resp mixart_enum_connector_resp_t;
+struct mixart_enum_connector_resp
+{
+ u32 error_code;
+ u32 first_uid_offset;
+ u32 uid_count;
+ u32 current_uid_index;
+ mixart_uid_t uid[MIXART_MAX_PHYS_CONNECTORS];
+} __attribute__((packed));
+
+
+/* used for following struct */
+#define MIXART_FLOAT_P_22_0_TO_HEX 0x41b00000 /* 22.0f */
+#define MIXART_FLOAT_M_20_0_TO_HEX 0xc1a00000 /* -20.0f */
+#define MIXART_FLOAT____0_0_TO_HEX 0x00000000 /* 0.0f */
+
+typedef struct mixart_audio_info_req mixart_audio_info_req_t;
+struct mixart_audio_info_req
+{
+ u32 line_max_level; /* float */
+ u32 micro_max_level; /* float */
+ u32 cd_max_level; /* float */
+} __attribute__((packed));
+
+typedef struct mixart_analog_hw_info mixart_analog_hw_info_t;
+struct mixart_analog_hw_info
+{
+ u32 is_present;
+ u32 hw_connection_type;
+ u32 max_level; /* float */
+ u32 min_var_level; /* float */
+ u32 max_var_level; /* float */
+ u32 step_var_level; /* float */
+ u32 fix_gain; /* float */
+ u32 zero_var; /* float */
+} __attribute__((packed));
+
+typedef struct mixart_digital_hw_info mixart_digital_hw_info_t;
+struct mixart_digital_hw_info
+{
+ u32 hw_connection_type;
+ u32 presence;
+ u32 clock;
+ u32 reserved;
+} __attribute__((packed));
+
+typedef struct mixart_analog_info mixart_analog_info_t;
+struct mixart_analog_info
+{
+ u32 type_mask;
+ mixart_analog_hw_info_t micro_info;
+ mixart_analog_hw_info_t line_info;
+ mixart_analog_hw_info_t cd_info;
+ u32 analog_level_present;
+} __attribute__((packed));
+
+typedef struct mixart_digital_info mixart_digital_info_t;
+struct mixart_digital_info
+{
+ u32 type_mask;
+ mixart_digital_hw_info_t aes_info;
+ mixart_digital_hw_info_t adat_info;
+} __attribute__((packed));
+
+typedef struct mixart_audio_info mixart_audio_info_t;
+struct mixart_audio_info
+{
+ u32 clock_type_mask;
+ mixart_analog_info_t analog_info;
+ mixart_digital_info_t digital_info;
+} __attribute__((packed));
+
+typedef struct mixart_audio_info_resp mixart_audio_info_resp_t;
+struct mixart_audio_info_resp
+{
+ u32 txx_status;
+ mixart_audio_info_t info;
+} __attribute__((packed));
+
+
+/* used for nb_bytes_max_per_sample */
+#define MIXART_FLOAT_P__4_0_TO_HEX 0x40800000 /* +4.0f */
+#define MIXART_FLOAT_P__8_0_TO_HEX 0x41000000 /* +8.0f */
+
+typedef struct mixart_stream_info mixart_stream_info_t;
+struct mixart_stream_info
+{
+ u32 size_max_byte_frame;
+ u32 size_max_sample_frame;
+ u32 nb_bytes_max_per_sample; /* float */
+} __attribute__((packed));
+
+/* MSG_STREAM_ADD_INPUT_GROUP */
+/* MSG_STREAM_ADD_OUTPUT_GROUP */
+
+typedef struct mixart_streaming_group_req mixart_streaming_group_req_t;
+struct mixart_streaming_group_req
+{
+ u32 stream_count;
+ u32 channel_count;
+ u32 user_grp_number;
+ u32 first_phys_audio;
+ u32 latency;
+ mixart_stream_info_t stream_info[32];
+ mixart_uid_t connector;
+ u32 flow_entry[32];
+} __attribute__((packed));
+
+typedef struct mixart_stream_desc mixart_stream_desc_t;
+struct mixart_stream_desc
+{
+ mixart_uid_t stream_uid;
+ u32 stream_desc;
+} __attribute__((packed));
+
+typedef struct mixart_streaming_group mixart_streaming_group_t;
+struct mixart_streaming_group
+{
+ u32 status;
+ mixart_uid_t group;
+ u32 pipe_desc;
+ u32 stream_count;
+ mixart_stream_desc_t stream[32];
+} __attribute__((packed));
+
+/* MSG_STREAM_DELETE_GROUP */
+
+/* request : mixart_uid_t group */
+
+typedef struct mixart_delete_group_resp mixart_delete_group_resp_t;
+struct mixart_delete_group_resp
+{
+ u32 status;
+ u32 unused[2];
+} __attribute__((packed));
+
+
+/* MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130000 + 7,
+ MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130000 + 8,
+ MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x130000 + 10,
+ MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x130000 + 11,
+ */
+
+typedef struct mixart_fx_couple_uid mixart_fx_couple_uid_t;
+struct mixart_fx_couple_uid
+{
+ mixart_uid_t uid_fx_code;
+ mixart_uid_t uid_fx_data;
+} __attribute__((packed));
+
+typedef struct mixart_txx_stream_desc mixart_txx_stream_desc_t;
+struct mixart_txx_stream_desc
+{
+ mixart_uid_t uid_pipe;
+ u32 stream_idx;
+ u32 fx_number;
+ mixart_fx_couple_uid_t uid_fx[4];
+} __attribute__((packed));
+
+typedef struct mixart_flow_info mixart_flow_info_t;
+struct mixart_flow_info
+{
+ mixart_txx_stream_desc_t stream_desc;
+ u32 flow_entry;
+ u32 flow_phy_addr;
+} __attribute__((packed));
+
+typedef struct mixart_stream_state_req mixart_stream_state_req_t;
+struct mixart_stream_state_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 reserved4np[3];
+ u32 stream_count; /* set to 1 for instance */
+ mixart_flow_info_t stream_info; /* could be an array[stream_count] */
+} __attribute__((packed));
+
+/* MSG_STREAM_START_STREAM_GRP_PACKET = 0x130000 + 6
+ MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130000 + 9
+ */
+
+typedef struct mixart_group_state_req mixart_group_state_req_t;
+struct mixart_group_state_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 reserved4np[2];
+ u32 pipe_count; /* set to 1 for instance */
+ mixart_uid_t pipe_uid[1]; /* could be an array[pipe_count] */
+} __attribute__((packed));
+
+typedef struct mixart_group_state_resp mixart_group_state_resp_t;
+struct mixart_group_state_resp
+{
+ u32 txx_status;
+ u64 scheduler;
+} __attribute__((packed));
+
+
+
+/* Structures used by the MSG_SERVICES_TIMER_NOTIFY command */
+
+typedef struct mixart_sample_pos mixart_sample_pos_t;
+struct mixart_sample_pos
+{
+ u32 buffer_id;
+ u32 validity;
+ u32 sample_pos_high_part;
+ u32 sample_pos_low_part;
+} __attribute__((packed));
+
+typedef struct mixart_timer_notify mixart_timer_notify_t;
+struct mixart_timer_notify
+{
+ u32 stream_count;
+ mixart_sample_pos_t streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS];
+} __attribute__((packed));
+
+
+/* MSG_CONSOLE_GET_CLOCK_UID = 0x070003,
+ */
+
+/* request is a uid with desc = MSG_CONSOLE_MANAGER | cardindex */
+
+typedef struct mixart_return_uid mixart_return_uid_t;
+struct mixart_return_uid
+{
+ u32 error_code;
+ mixart_uid_t uid;
+} __attribute__((packed));
+
+/* MSG_CLOCK_CHECK_PROPERTIES = 0x200001,
+ MSG_CLOCK_SET_PROPERTIES = 0x200002,
+*/
+
+enum mixart_clock_generic_type {
+ CGT_NO_CLOCK,
+ CGT_INTERNAL_CLOCK,
+ CGT_PROGRAMMABLE_CLOCK,
+ CGT_INTERNAL_ENSLAVED_CLOCK,
+ CGT_EXTERNAL_CLOCK,
+ CGT_CURRENT_CLOCK
+};
+
+enum mixart_clock_mode {
+ CM_UNDEFINED,
+ CM_MASTER,
+ CM_SLAVE,
+ CM_STANDALONE,
+ CM_NOT_CONCERNED
+};
+
+
+typedef struct mixart_clock_properties mixart_clock_properties_t;
+struct mixart_clock_properties
+{
+ u32 error_code;
+ u32 validation_mask;
+ u32 frequency;
+ u32 reference_frequency;
+ u32 clock_generic_type;
+ u32 clock_mode;
+ mixart_uid_t uid_clock_source;
+ mixart_uid_t uid_event_source;
+ u32 event_mode;
+ u32 synchro_signal_presence;
+ u32 format;
+ u32 board_mask;
+ u32 nb_callers; /* set to 1 (see below) */
+ mixart_uid_t uid_caller[1];
+} __attribute__((packed));
+
+typedef struct mixart_clock_properties_resp mixart_clock_properties_resp_t;
+struct mixart_clock_properties_resp
+{
+ u32 status;
+ u32 clock_mode;
+} __attribute__((packed));
+
+
+/* MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F */
+/* MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010 */
+
+enum mixart_coding_type {
+ CT_NOT_DEFINED,
+ CT_LINEAR,
+ CT_MPEG_L1,
+ CT_MPEG_L2,
+ CT_MPEG_L3,
+ CT_MPEG_L3_LSF,
+ CT_GSM
+};
+enum mixart_sample_type {
+ ST_NOT_DEFINED,
+ ST_FLOATING_POINT_32BE,
+ ST_FLOATING_POINT_32LE,
+ ST_FLOATING_POINT_64BE,
+ ST_FLOATING_POINT_64LE,
+ ST_FIXED_POINT_8,
+ ST_FIXED_POINT_16BE,
+ ST_FIXED_POINT_16LE,
+ ST_FIXED_POINT_24BE,
+ ST_FIXED_POINT_24LE,
+ ST_FIXED_POINT_32BE,
+ ST_FIXED_POINT_32LE,
+ ST_INTEGER_8,
+ ST_INTEGER_16BE,
+ ST_INTEGER_16LE,
+ ST_INTEGER_24BE,
+ ST_INTEGER_24LE,
+ ST_INTEGER_32BE,
+ ST_INTEGER_32LE
+};
+
+typedef struct mixart_stream_param_desc mixart_stream_param_desc_t;
+struct mixart_stream_param_desc
+{
+ u32 coding_type; /* use enum mixart_coding_type */
+ u32 sample_type; /* use enum mixart_sample_type */
+
+ union {
+ struct {
+ u32 linear_endian_ness;
+ u32 linear_bits;
+ u32 is_signed;
+ u32 is_float;
+ } linear_format_info;
+
+ struct {
+ u32 mpeg_layer;
+ u32 mpeg_mode;
+ u32 mpeg_mode_extension;
+ u32 mpeg_pre_emphasis;
+ u32 mpeg_has_padding_bit;
+ u32 mpeg_has_crc;
+ u32 mpeg_has_extension;
+ u32 mpeg_is_original;
+ u32 mpeg_has_copyright;
+ } mpeg_format_info;
+ } format_info;
+
+ u32 delayed;
+ u64 scheduler;
+ u32 sample_size;
+ u32 has_header;
+ u32 has_suffix;
+ u32 has_bitrate;
+ u32 samples_per_frame;
+ u32 bytes_per_frame;
+ u32 bytes_per_sample;
+ u32 sampling_freq;
+ u32 number_of_channel;
+ u32 stream_number;
+ u32 buffer_size;
+ u32 differed_time;
+ u32 reserved4np[3];
+ u32 pipe_count; /* set to 1 (array size !) */
+ u32 stream_count; /* set to 1 (array size !) */
+ mixart_txx_stream_desc_t stream_desc[1]; /* only one stream per command, but this could be an array */
+
+} __attribute__((packed));
+
+
+/* MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009,
+ */
+
+
+typedef struct mixart_get_out_audio_level mixart_get_out_audio_level_t;
+struct mixart_get_out_audio_level
+{
+ u32 txx_status;
+ u32 digital_level; /* float */
+ u32 analog_level; /* float */
+ u32 monitor_level; /* float */
+ u32 mute;
+ u32 monitor_mute1;
+ u32 monitor_mute2;
+} __attribute__((packed));
+
+
+/* MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A,
+ */
+
+/* used for valid_mask below */
+#define MIXART_AUDIO_LEVEL_ANALOG_MASK 0x01
+#define MIXART_AUDIO_LEVEL_DIGITAL_MASK 0x02
+#define MIXART_AUDIO_LEVEL_MONITOR_MASK 0x04
+#define MIXART_AUDIO_LEVEL_MUTE_MASK 0x08
+#define MIXART_AUDIO_LEVEL_MUTE_M1_MASK 0x10
+#define MIXART_AUDIO_LEVEL_MUTE_M2_MASK 0x20
+
+typedef struct mixart_set_out_audio_level mixart_set_out_audio_level_t;
+struct mixart_set_out_audio_level
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 valid_mask1;
+ u32 valid_mask2;
+ u32 digital_level; /* float */
+ u32 analog_level; /* float */
+ u32 monitor_level; /* float */
+ u32 mute;
+ u32 monitor_mute1;
+ u32 monitor_mute2;
+ u32 reserved4np;
+} __attribute__((packed));
+
+
+/* MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E,
+ */
+
+#define MIXART_MAX_PHYS_IO (MIXART_MAX_CARDS * 2 * 2) /* 4 * (analog+digital) * (playback+capture) */
+
+typedef struct mixart_uid_enumeration mixart_uid_enumeration_t;
+struct mixart_uid_enumeration
+{
+ u32 error_code;
+ u32 first_uid_offset;
+ u32 nb_uid;
+ u32 current_uid_index;
+ mixart_uid_t uid[MIXART_MAX_PHYS_IO];
+} __attribute__((packed));
+
+
+/* MSG_PHYSICALIO_SET_LEVEL = 0x0F0008,
+ MSG_PHYSICALIO_GET_LEVEL = 0x0F000C,
+*/
+
+typedef struct mixart_io_channel_level mixart_io_channel_level_t;
+struct mixart_io_channel_level
+{
+ u32 analog_level; /* float */
+ u32 unused[2];
+} __attribute__((packed));
+
+typedef struct mixart_io_level mixart_io_level_t;
+struct mixart_io_level
+{
+ s32 channel; /* 0=left, 1=right, -1=both, -2=both same */
+ mixart_io_channel_level_t level[2];
+} __attribute__((packed));
+
+
+/* MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015,
+ */
+
+typedef struct mixart_in_audio_level_info mixart_in_audio_level_info_t;
+struct mixart_in_audio_level_info
+{
+ mixart_uid_t connector;
+ u32 valid_mask1;
+ u32 valid_mask2;
+ u32 digital_level;
+ u32 analog_level;
+} __attribute__((packed));
+
+typedef struct mixart_set_in_audio_level_req mixart_set_in_audio_level_req_t;
+struct mixart_set_in_audio_level_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 audio_count; /* set to <= 2 */
+ u32 reserved4np;
+ mixart_in_audio_level_info_t level[2];
+} __attribute__((packed));
+
+/* response is a 32 bit status */
+
+
+/* MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017,
+ */
+
+/* defines used for valid_mask1 */
+#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 0x01
+#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO2 0x02
+#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO1 0x04
+#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2 0x08
+#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_1 0x10
+#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_2 0x20
+#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_1 0x40
+#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_2 0x80
+
+typedef struct mixart_out_stream_level_info mixart_out_stream_level_info_t;
+struct mixart_out_stream_level_info
+{
+ u32 valid_mask1;
+ u32 valid_mask2;
+ u32 left_to_out1_level;
+ u32 left_to_out2_level;
+ u32 right_to_out1_level;
+ u32 right_to_out2_level;
+ u32 digital_level1;
+ u32 digital_level2;
+ u32 mute1;
+ u32 mute2;
+} __attribute__((packed));
+
+typedef struct mixart_set_out_stream_level mixart_set_out_stream_level_t;
+struct mixart_set_out_stream_level
+{
+ mixart_txx_stream_desc_t desc;
+ mixart_out_stream_level_info_t out_level;
+} __attribute__((packed));
+
+typedef struct mixart_set_out_stream_level_req mixart_set_out_stream_level_req_t;
+struct mixart_set_out_stream_level_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 reserved4np[2];
+ u32 nb_of_stream; /* set to 1 */
+ mixart_set_out_stream_level_t stream_level; /* could be an array */
+} __attribute__((packed));
+
+/* response to this request is a u32 status value */
+
+
+/* exported */
+void snd_mixart_init_mailbox(mixart_mgr_t *mgr);
+void snd_mixart_exit_mailbox(mixart_mgr_t *mgr);
+
+int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data);
+int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event);
+int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request);
+
+irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+void snd_mixart_msg_tasklet( unsigned long arg);
+
+void snd_mixart_reset_board(mixart_mgr_t *mgr);
+
+#endif /* __SOUND_MIXART_CORE_H */
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
new file mode 100644
index 00000000000..edd1599fe45
--- /dev/null
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -0,0 +1,647 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * DSP firmware management
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_mixer.h"
+#include "mixart_core.h"
+#include "mixart_hwdep.h"
+
+
+/**
+ * wait for a value on a peudo register, exit with a timeout
+ *
+ * @param mgr pointer to miXart manager structure
+ * @param offset unsigned pseudo_register base + offset of value
+ * @param value value
+ * @param timeout timeout in centisenconds
+ */
+static int mixart_wait_nice_for_register_value(mixart_mgr_t *mgr, u32 offset, int is_egal, u32 value, unsigned long timeout)
+{
+ unsigned long end_time = jiffies + (timeout * HZ / 100);
+ u32 read;
+
+ do { /* we may take too long time in this loop.
+ * so give controls back to kernel if needed.
+ */
+ cond_resched();
+
+ read = readl_be( MIXART_MEM( mgr, offset ));
+ if(is_egal) {
+ if(read == value) return 0;
+ }
+ else { /* wait for different value */
+ if(read != value) return 0;
+ }
+ } while ( time_after_eq(end_time, jiffies) );
+
+ return -EBUSY;
+}
+
+
+/*
+ structures needed to upload elf code packets
+ */
+typedef struct snd_mixart_elf32_ehdr snd_mixart_elf32_ehdr_t;
+
+struct snd_mixart_elf32_ehdr {
+ u8 e_ident[16];
+ u16 e_type;
+ u16 e_machine;
+ u32 e_version;
+ u32 e_entry;
+ u32 e_phoff;
+ u32 e_shoff;
+ u32 e_flags;
+ u16 e_ehsize;
+ u16 e_phentsize;
+ u16 e_phnum;
+ u16 e_shentsize;
+ u16 e_shnum;
+ u16 e_shstrndx;
+};
+
+typedef struct snd_mixart_elf32_phdr snd_mixart_elf32_phdr_t;
+
+struct snd_mixart_elf32_phdr {
+ u32 p_type;
+ u32 p_offset;
+ u32 p_vaddr;
+ u32 p_paddr;
+ u32 p_filesz;
+ u32 p_memsz;
+ u32 p_flags;
+ u32 p_align;
+};
+
+static int mixart_load_elf(mixart_mgr_t *mgr, const struct firmware *dsp )
+{
+ char elf32_magic_number[4] = {0x7f,'E','L','F'};
+ snd_mixart_elf32_ehdr_t *elf_header;
+ int i;
+
+ elf_header = (snd_mixart_elf32_ehdr_t *)dsp->data;
+ for( i=0; i<4; i++ )
+ if ( elf32_magic_number[i] != elf_header->e_ident[i] )
+ return -EINVAL;
+
+ if( elf_header->e_phoff != 0 ) {
+ snd_mixart_elf32_phdr_t elf_programheader;
+
+ for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) {
+ u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize));
+
+ memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) );
+
+ if(elf_programheader.p_type != 0) {
+ if( elf_programheader.p_filesz != 0 ) {
+ memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
+ dsp->data + be32_to_cpu( elf_programheader.p_offset ),
+ be32_to_cpu( elf_programheader.p_filesz ));
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * get basic information and init miXart
+ */
+
+/* audio IDs for request to the board */
+#define MIXART_FIRST_ANA_AUDIO_ID 0
+#define MIXART_FIRST_DIG_AUDIO_ID 8
+
+static int mixart_enum_connectors(mixart_mgr_t *mgr)
+{
+ u32 k;
+ int err;
+ mixart_msg_t request;
+ mixart_enum_connector_resp_t *connector;
+ mixart_audio_info_req_t *audio_info_req;
+ mixart_audio_info_resp_t *audio_info;
+
+ connector = kmalloc(sizeof(*connector), GFP_KERNEL);
+ audio_info_req = kmalloc(sizeof(*audio_info_req), GFP_KERNEL);
+ audio_info = kmalloc(sizeof(*audio_info), GFP_KERNEL);
+ if (! connector || ! audio_info_req || ! audio_info) {
+ err = -ENOMEM;
+ goto __error;
+ }
+
+ audio_info_req->line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;
+ audio_info_req->micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX;
+ audio_info_req->cd_max_level = MIXART_FLOAT____0_0_TO_HEX;
+
+ request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR;
+ request.uid = (mixart_uid_t){0,0}; /* board num = 0 */
+ request.data = NULL;
+ request.size = 0;
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
+ if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
+ snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
+ err = -EINVAL;
+ goto __error;
+ }
+
+ for(k=0; k < connector->uid_count; k++) {
+ mixart_pipe_t* pipe;
+
+ if(k < MIXART_FIRST_DIG_AUDIO_ID) {
+ pipe = &mgr->chip[k/2]->pipe_out_ana;
+ } else {
+ pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;
+ }
+ if(k & 1) {
+ pipe->uid_right_connector = connector->uid[k]; /* odd */
+ } else {
+ pipe->uid_left_connector = connector->uid[k]; /* even */
+ }
+
+ /* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
+
+ /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
+ request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
+ request.uid = connector->uid[k];
+ request.data = audio_info_req;
+ request.size = sizeof(*audio_info_req);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+ goto __error;
+ }
+ /*snd_printk(KERN_DEBUG "play analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
+ }
+
+ request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;
+ request.uid = (mixart_uid_t){0,0}; /* board num = 0 */
+ request.data = NULL;
+ request.size = 0;
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
+ if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
+ snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
+ err = -EINVAL;
+ goto __error;
+ }
+
+ for(k=0; k < connector->uid_count; k++) {
+ mixart_pipe_t* pipe;
+
+ if(k < MIXART_FIRST_DIG_AUDIO_ID) {
+ pipe = &mgr->chip[k/2]->pipe_in_ana;
+ } else {
+ pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;
+ }
+ if(k & 1) {
+ pipe->uid_right_connector = connector->uid[k]; /* odd */
+ } else {
+ pipe->uid_left_connector = connector->uid[k]; /* even */
+ }
+
+ /* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
+
+ /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
+ request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
+ request.uid = connector->uid[k];
+ request.data = audio_info_req;
+ request.size = sizeof(*audio_info_req);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
+ if( err < 0 ) {
+ snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+ goto __error;
+ }
+ /*snd_printk(KERN_DEBUG "rec analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
+ }
+ err = 0;
+
+ __error:
+ kfree(connector);
+ kfree(audio_info_req);
+ kfree(audio_info);
+
+ return err;
+}
+
+static int mixart_enum_physio(mixart_mgr_t *mgr)
+{
+ u32 k;
+ int err;
+ mixart_msg_t request;
+ mixart_uid_t get_console_mgr;
+ mixart_return_uid_t console_mgr;
+ mixart_uid_enumeration_t phys_io;
+
+ /* get the uid for the console manager */
+ get_console_mgr.object_id = 0;
+ get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */
+
+ request.message_id = MSG_CONSOLE_GET_CLOCK_UID;
+ request.uid = get_console_mgr;
+ request.data = &get_console_mgr;
+ request.size = sizeof(get_console_mgr);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);
+
+ if( (err < 0) || (console_mgr.error_code != 0) ) {
+ snd_printk(KERN_DEBUG "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr.error_code);
+ return -EINVAL;
+ }
+
+ /* used later for clock issues ! */
+ mgr->uid_console_manager = console_mgr.uid;
+
+ request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = &console_mgr.uid;
+ request.size = sizeof(console_mgr.uid);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);
+ if( (err < 0) || ( phys_io.error_code != 0 ) ) {
+ snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err, phys_io.error_code );
+ return -EINVAL;
+ }
+
+ snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2), return -EINVAL); /* min 2 phys io per card (analog in + analog out) */
+
+ for(k=0; k<mgr->num_cards; k++) {
+ mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
+ mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k];
+ }
+
+ return 0;
+}
+
+
+static int mixart_first_init(mixart_mgr_t *mgr)
+{
+ u32 k;
+ int err;
+ mixart_msg_t request;
+
+ if((err = mixart_enum_connectors(mgr)) < 0) return err;
+
+ if((err = mixart_enum_physio(mgr)) < 0) return err;
+
+ /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
+ /* though why not here */
+ request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = NULL;
+ request.size = 0;
+ /* this command has no data. response is a 32 bit status */
+ err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);
+ if( (err < 0) || (k != 0) ) {
+ snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
+ return err == 0 ? -EINVAL : err;
+ }
+
+ return 0;
+}
+
+
+/* firmware base addresses (when hard coded) */
+#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000
+
+static int mixart_dsp_load(mixart_mgr_t* mgr, int index, const struct firmware *dsp)
+{
+ int err, card_index;
+ u32 status_xilinx, status_elf, status_daught;
+ u32 val;
+
+ /* read motherboard xilinx status */
+ status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+ /* read elf status */
+ status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+ /* read daughterboard xilinx status */
+ status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+ /* motherboard xilinx status 5 will say that the board is performing a reset */
+ if( status_xilinx == 5 ) {
+ snd_printk( KERN_ERR "miXart is resetting !\n");
+ return -EAGAIN; /* try again later */
+ }
+
+ switch (index) {
+ case MIXART_MOTHERBOARD_XLX_INDEX:
+
+ /* xilinx already loaded ? */
+ if( status_xilinx == 4 ) {
+ snd_printk( KERN_DEBUG "xilinx is already loaded !\n");
+ return 0;
+ }
+ /* the status should be 0 == "idle" */
+ if( status_xilinx != 0 ) {
+ snd_printk( KERN_ERR "xilinx load error ! status = %d\n", status_xilinx);
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* check xilinx validity */
+ snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
+ snd_assert(dsp->size % 4 == 0, return -EINVAL);
+
+ /* set xilinx status to copying */
+ writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+
+ /* setup xilinx base address */
+ writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));
+ /* setup code size for xilinx file */
+ writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));
+
+ /* copy xilinx code */
+ memcpy_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->data, dsp->size);
+
+ /* set xilinx status to copy finished */
+ writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+
+ /* return, because no further processing needed */
+ return 0;
+
+ case MIXART_MOTHERBOARD_ELF_INDEX:
+
+ if( status_elf == 4 ) {
+ snd_printk( KERN_DEBUG "elf file already loaded !\n");
+ return 0;
+ }
+
+ /* the status should be 0 == "idle" */
+ if( status_elf != 0 ) {
+ snd_printk( KERN_ERR "elf load error ! status = %d\n", status_elf);
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* wait for xilinx status == 4 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
+ if (err < 0) {
+ snd_printk( KERN_ERR "xilinx was not loaded or could not be started\n");
+ return err;
+ }
+
+ /* init some data on the card */
+ writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */
+ writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */
+
+ /* set elf status to copying */
+ writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+
+ /* process the copying of the elf packets */
+ err = mixart_load_elf( mgr, dsp );
+ if (err < 0) return err;
+
+ /* set elf status to copy finished */
+ writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+
+ /* wait for elf status == 4 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
+ if (err < 0) {
+ snd_printk( KERN_ERR "elf could not be started\n");
+ return err;
+ }
+
+ /* miXart waits at this point on the pointer to the flow table */
+ writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */
+
+ return 0; /* return, another xilinx file has to be loaded before */
+
+ case MIXART_AESEBUBOARD_XLX_INDEX:
+ default:
+
+ /* elf and xilinx should be loaded */
+ if( (status_elf != 4) || (status_xilinx != 4) ) {
+ printk( KERN_ERR "xilinx or elf not successfully loaded\n");
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* wait for daughter detection != 0 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
+ if (err < 0) {
+ snd_printk( KERN_ERR "error starting elf file\n");
+ return err;
+ }
+
+ /* the board type can now be retrieved */
+ mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET)));
+
+ if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE)
+ break; /* no daughter board; the file does not have to be loaded, continue after the switch */
+
+ /* only if aesebu daughter board presence (elf code must run) */
+ if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES )
+ return -EINVAL;
+
+ /* daughter should be idle */
+ if( status_daught != 0 ) {
+ printk( KERN_ERR "daughter load error ! status = %d\n", status_daught);
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* check daughterboard xilinx validity */
+ snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
+ snd_assert(dsp->size % 4 == 0, return -EINVAL);
+
+ /* inform mixart about the size of the file */
+ writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
+
+ /* set daughterboard status to 1 */
+ writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+ /* wait for status == 2 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
+ if (err < 0) {
+ snd_printk( KERN_ERR "daughter board load error\n");
+ return err;
+ }
+
+ /* get the address where to write the file */
+ val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));
+ snd_assert(val != 0, return -EINVAL);
+
+ /* copy daughterboard xilinx code */
+ memcpy_toio( MIXART_MEM( mgr, val), dsp->data, dsp->size);
+
+ /* set daughterboard status to 4 */
+ writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+ /* continue with init */
+ break;
+ } /* end of switch file index*/
+
+ /* wait for daughter status == 3 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
+ if (err < 0) {
+ snd_printk( KERN_ERR "daughter board could not be initialised\n");
+ return err;
+ }
+
+ /* init mailbox (communication with embedded) */
+ snd_mixart_init_mailbox(mgr);
+
+ /* first communication with embedded */
+ err = mixart_first_init(mgr);
+ if (err < 0) {
+ snd_printk( KERN_ERR "miXart could not be set up\n");
+ return err;
+ }
+
+ /* create devices and mixer in accordance with HW options*/
+ for (card_index = 0; card_index < mgr->num_cards; card_index++) {
+ mixart_t *chip = mgr->chip[card_index];
+
+ if ((err = snd_mixart_create_pcm(chip)) < 0)
+ return err;
+
+ if (card_index == 0) {
+ if ((err = snd_mixart_create_mixer(chip->mgr)) < 0)
+ return err;
+ }
+
+ if ((err = snd_card_register(chip->card)) < 0)
+ return err;
+ };
+
+ snd_printdd("miXart firmware downloaded and successfully set up\n");
+
+ return 0;
+}
+
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+#if !defined(CONFIG_USE_MIXARTLOADER) && !defined(CONFIG_SND_MIXART) /* built-in kernel */
+#define SND_MIXART_FW_LOADER /* use the standard firmware loader */
+#endif
+#endif
+
+#ifdef SND_MIXART_FW_LOADER
+
+int snd_mixart_setup_firmware(mixart_mgr_t *mgr)
+{
+ static char *fw_files[3] = {
+ "miXart8.xlx", "miXart8.elf", "miXart8AES.xlx"
+ };
+ char path[32];
+
+ const struct firmware *fw_entry;
+ int i, err;
+
+ for (i = 0; i < 3; i++) {
+ sprintf(path, "mixart/%s", fw_files[i]);
+ if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
+ snd_printk(KERN_ERR "miXart: can't load firmware %s\n", path);
+ return -ENOENT;
+ }
+ /* fake hwdep dsp record */
+ err = mixart_dsp_load(mgr, i, fw_entry);
+ release_firmware(fw_entry);
+ if (err < 0)
+ return err;
+ mgr->dsp_loaded |= 1 << i;
+ }
+ return 0;
+}
+
+
+#else /* old style firmware loading */
+
+/* miXart hwdep interface id string */
+#define SND_MIXART_HWDEP_ID "miXart Loader"
+
+static int mixart_hwdep_open(snd_hwdep_t *hw, struct file *file)
+{
+ return 0;
+}
+
+static int mixart_hwdep_release(snd_hwdep_t *hw, struct file *file)
+{
+ return 0;
+}
+
+static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
+{
+ mixart_mgr_t *mgr = hw->private_data;
+
+ strcpy(info->id, "miXart");
+ info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX;
+
+ if (mgr->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX))
+ info->chip_ready = 1;
+
+ info->version = MIXART_DRIVER_VERSION;
+ return 0;
+}
+
+static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
+{
+ mixart_mgr_t* mgr = hw->private_data;
+ struct firmware fw;
+ int err;
+
+ fw.size = dsp->length;
+ fw.data = vmalloc(dsp->length);
+ if (! fw.data) {
+ snd_printk(KERN_ERR "miXart: cannot allocate image size %d\n",
+ (int)dsp->length);
+ return -ENOMEM;
+ }
+ if (copy_from_user(fw.data, dsp->image, dsp->length)) {
+ vfree(fw.data);
+ return -EFAULT;
+ }
+ err = mixart_dsp_load(mgr, dsp->index, &fw);
+ vfree(fw.data);
+ if (err < 0)
+ return err;
+ mgr->dsp_loaded |= 1 << dsp->index;
+ return err;
+}
+
+int snd_mixart_setup_firmware(mixart_mgr_t *mgr)
+{
+ int err;
+ snd_hwdep_t *hw;
+
+ /* only create hwdep interface for first cardX (see "index" module parameter)*/
+ if ((err = snd_hwdep_new(mgr->chip[0]->card, SND_MIXART_HWDEP_ID, 0, &hw)) < 0)
+ return err;
+
+ hw->iface = SNDRV_HWDEP_IFACE_MIXART;
+ hw->private_data = mgr;
+ hw->ops.open = mixart_hwdep_open;
+ hw->ops.release = mixart_hwdep_release;
+ hw->ops.dsp_status = mixart_hwdep_dsp_status;
+ hw->ops.dsp_load = mixart_hwdep_dsp_load;
+ hw->exclusive = 1;
+ sprintf(hw->name, SND_MIXART_HWDEP_ID);
+ mgr->dsp_loaded = 0;
+
+ return snd_card_register(mgr->chip[0]->card);
+}
+
+#endif /* SND_MIXART_FW_LOADER */
diff --git a/sound/pci/mixart/mixart_hwdep.h b/sound/pci/mixart/mixart_hwdep.h
new file mode 100644
index 00000000000..25190cc88cf
--- /dev/null
+++ b/sound/pci/mixart/mixart_hwdep.h
@@ -0,0 +1,145 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * definitions and makros for basic card access
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_HWDEP_H
+#define __SOUND_MIXART_HWDEP_H
+
+#include <sound/hwdep.h>
+
+#define readl_be(x) be32_to_cpu(__raw_readl(x))
+#define writel_be(data,addr) __raw_writel(cpu_to_be32(data),addr)
+
+#define readl_le(x) le32_to_cpu(__raw_readl(x))
+#define writel_le(data,addr) __raw_writel(cpu_to_le32(data),addr)
+
+#define MIXART_MEM(mgr,x) ((mgr)->mem[0].virt + (x))
+#define MIXART_REG(mgr,x) ((mgr)->mem[1].virt + (x))
+
+
+/* Daughter board Type */
+#define DAUGHTER_TYPE_MASK 0x0F
+#define DAUGHTER_VER_MASK 0xF0
+#define DAUGHTER_TYPEVER_MASK (DAUGHTER_TYPE_MASK|DAUGHTER_VER_MASK)
+
+#define MIXART_DAUGHTER_TYPE_NONE 0x00
+#define MIXART_DAUGHTER_TYPE_COBRANET 0x08
+#define MIXART_DAUGHTER_TYPE_AES 0x0E
+
+
+
+#define MIXART_BA0_SIZE (16 * 1024 * 1024) /* 16M */
+#define MIXART_BA1_SIZE (4 * 1024) /* 4k */
+
+/*
+ * -----------BAR 0 --------------------------------------------------------------------------------------------------------
+ */
+#define MIXART_PSEUDOREG 0x2000 /* base address for pseudoregister */
+
+#define MIXART_PSEUDOREG_BOARDNUMBER MIXART_PSEUDOREG+0 /* board number */
+
+/* perfmeter (available when elf loaded)*/
+#define MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET MIXART_PSEUDOREG+0x70 /* streaming load */
+#define MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET MIXART_PSEUDOREG+0x78 /* system load (reference)*/
+#define MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET MIXART_PSEUDOREG+0x7C /* mailbox load */
+#define MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET MIXART_PSEUDOREG+0x74 /* interrupt handling load */
+
+/* motherboard xilinx loader info */
+#define MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x9C /* 0x00600000 */
+#define MIXART_PSEUDOREG_MXLX_SIZE_OFFSET MIXART_PSEUDOREG+0xA0 /* xilinx size in bytes */
+#define MIXART_PSEUDOREG_MXLX_STATUS_OFFSET MIXART_PSEUDOREG+0xA4 /* status = EMBEBBED_STAT_XXX */
+
+/* elf loader info */
+#define MIXART_PSEUDOREG_ELF_STATUS_OFFSET MIXART_PSEUDOREG+0xB0 /* status = EMBEBBED_STAT_XXX */
+
+/*
+* after the elf code is loaded, and the flowtable info was passed to it,
+* the driver polls on this address, until it shows 1 (presence) or 2 (absence)
+* once it is non-zero, the daughter board type may be read
+*/
+#define MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET MIXART_PSEUDOREG+0x990
+
+/* Global info structure */
+#define MIXART_PSEUDOREG_DBRD_TYPE_OFFSET MIXART_PSEUDOREG+0x994 /* Type and version of daughterboard */
+
+
+/* daughterboard xilinx loader info */
+#define MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x998 /* get the address here where to write the file */
+#define MIXART_PSEUDOREG_DXLX_SIZE_OFFSET MIXART_PSEUDOREG+0x99C /* xilinx size in bytes */
+#define MIXART_PSEUDOREG_DXLX_STATUS_OFFSET MIXART_PSEUDOREG+0x9A0 /* status = EMBEBBED_STAT_XXX */
+
+/* */
+#define MIXART_FLOWTABLE_PTR 0x3000 /* pointer to flow table */
+
+/* mailbox addresses */
+
+/* message DRV -> EMB */
+#define MSG_INBOUND_POST_HEAD 0x010008 /* DRV posts MF + increment4 */
+#define MSG_INBOUND_POST_TAIL 0x01000C /* EMB gets MF + increment4 */
+/* message EMB -> DRV */
+#define MSG_OUTBOUND_POST_TAIL 0x01001C /* DRV gets MF + increment4 */
+#define MSG_OUTBOUND_POST_HEAD 0x010018 /* EMB posts MF + increment4 */
+/* Get Free Frames */
+#define MSG_INBOUND_FREE_TAIL 0x010004 /* DRV gets MFA + increment4 */
+#define MSG_OUTBOUND_FREE_TAIL 0x010014 /* EMB gets MFA + increment4 */
+/* Put Free Frames */
+#define MSG_OUTBOUND_FREE_HEAD 0x010010 /* DRV puts MFA + increment4 */
+#define MSG_INBOUND_FREE_HEAD 0x010000 /* EMB puts MFA + increment4 */
+
+/* firmware addresses of the message fifos */
+#define MSG_BOUND_STACK_SIZE 0x004000 /* size of each following stack */
+/* posted messages */
+#define MSG_OUTBOUND_POST_STACK 0x108000 /* stack of messages to the DRV */
+#define MSG_INBOUND_POST_STACK 0x104000 /* stack of messages to the EMB */
+/* available empty messages */
+#define MSG_OUTBOUND_FREE_STACK 0x10C000 /* stack of free enveloped for EMB */
+#define MSG_INBOUND_FREE_STACK 0x100000 /* stack of free enveloped for DRV */
+
+
+/* defines for mailbox message frames */
+#define MSG_FRAME_OFFSET 0x64
+#define MSG_FRAME_SIZE 0x6400
+#define MSG_FRAME_NUMBER 32
+#define MSG_FROM_AGENT_ITMF_OFFSET (MSG_FRAME_OFFSET + (MSG_FRAME_SIZE * MSG_FRAME_NUMBER))
+#define MSG_TO_AGENT_ITMF_OFFSET (MSG_FROM_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
+#define MSG_HOST_RSC_PROTECTION (MSG_TO_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
+#define MSG_AGENT_RSC_PROTECTION (MSG_HOST_RSC_PROTECTION + 4)
+
+
+/*
+ * -----------BAR 1 --------------------------------------------------------------------------------------------------------
+ */
+
+/* interrupt addresses and constants */
+#define MIXART_PCI_OMIMR_OFFSET 0x34 /* outbound message interrupt mask register */
+#define MIXART_PCI_OMISR_OFFSET 0x30 /* outbound message interrupt status register */
+#define MIXART_PCI_ODBR_OFFSET 0x60 /* outbound doorbell register */
+
+#define MIXART_BA1_BRUTAL_RESET_OFFSET 0x68 /* write 1 in LSBit to reset board */
+
+#define MIXART_HOST_ALL_INTERRUPT_MASKED 0x02B /* 0000 0010 1011 */
+#define MIXART_ALLOW_OUTBOUND_DOORBELL 0x023 /* 0000 0010 0011 */
+#define MIXART_OIDI 0x008 /* 0000 0000 1000 */
+
+
+int snd_mixart_setup_firmware(mixart_mgr_t *mgr);
+
+#endif /* __SOUND_MIXART_HWDEP_H */
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
new file mode 100644
index 00000000000..39c15416fc8
--- /dev/null
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -0,0 +1,1136 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * mixer callbacks
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_core.h"
+#include "mixart_hwdep.h"
+#include <sound/control.h>
+#include "mixart_mixer.h"
+
+static u32 mixart_analog_level[256] = {
+ 0xc2c00000, /* [000] -96.0 dB */
+ 0xc2bf0000, /* [001] -95.5 dB */
+ 0xc2be0000, /* [002] -95.0 dB */
+ 0xc2bd0000, /* [003] -94.5 dB */
+ 0xc2bc0000, /* [004] -94.0 dB */
+ 0xc2bb0000, /* [005] -93.5 dB */
+ 0xc2ba0000, /* [006] -93.0 dB */
+ 0xc2b90000, /* [007] -92.5 dB */
+ 0xc2b80000, /* [008] -92.0 dB */
+ 0xc2b70000, /* [009] -91.5 dB */
+ 0xc2b60000, /* [010] -91.0 dB */
+ 0xc2b50000, /* [011] -90.5 dB */
+ 0xc2b40000, /* [012] -90.0 dB */
+ 0xc2b30000, /* [013] -89.5 dB */
+ 0xc2b20000, /* [014] -89.0 dB */
+ 0xc2b10000, /* [015] -88.5 dB */
+ 0xc2b00000, /* [016] -88.0 dB */
+ 0xc2af0000, /* [017] -87.5 dB */
+ 0xc2ae0000, /* [018] -87.0 dB */
+ 0xc2ad0000, /* [019] -86.5 dB */
+ 0xc2ac0000, /* [020] -86.0 dB */
+ 0xc2ab0000, /* [021] -85.5 dB */
+ 0xc2aa0000, /* [022] -85.0 dB */
+ 0xc2a90000, /* [023] -84.5 dB */
+ 0xc2a80000, /* [024] -84.0 dB */
+ 0xc2a70000, /* [025] -83.5 dB */
+ 0xc2a60000, /* [026] -83.0 dB */
+ 0xc2a50000, /* [027] -82.5 dB */
+ 0xc2a40000, /* [028] -82.0 dB */
+ 0xc2a30000, /* [029] -81.5 dB */
+ 0xc2a20000, /* [030] -81.0 dB */
+ 0xc2a10000, /* [031] -80.5 dB */
+ 0xc2a00000, /* [032] -80.0 dB */
+ 0xc29f0000, /* [033] -79.5 dB */
+ 0xc29e0000, /* [034] -79.0 dB */
+ 0xc29d0000, /* [035] -78.5 dB */
+ 0xc29c0000, /* [036] -78.0 dB */
+ 0xc29b0000, /* [037] -77.5 dB */
+ 0xc29a0000, /* [038] -77.0 dB */
+ 0xc2990000, /* [039] -76.5 dB */
+ 0xc2980000, /* [040] -76.0 dB */
+ 0xc2970000, /* [041] -75.5 dB */
+ 0xc2960000, /* [042] -75.0 dB */
+ 0xc2950000, /* [043] -74.5 dB */
+ 0xc2940000, /* [044] -74.0 dB */
+ 0xc2930000, /* [045] -73.5 dB */
+ 0xc2920000, /* [046] -73.0 dB */
+ 0xc2910000, /* [047] -72.5 dB */
+ 0xc2900000, /* [048] -72.0 dB */
+ 0xc28f0000, /* [049] -71.5 dB */
+ 0xc28e0000, /* [050] -71.0 dB */
+ 0xc28d0000, /* [051] -70.5 dB */
+ 0xc28c0000, /* [052] -70.0 dB */
+ 0xc28b0000, /* [053] -69.5 dB */
+ 0xc28a0000, /* [054] -69.0 dB */
+ 0xc2890000, /* [055] -68.5 dB */
+ 0xc2880000, /* [056] -68.0 dB */
+ 0xc2870000, /* [057] -67.5 dB */
+ 0xc2860000, /* [058] -67.0 dB */
+ 0xc2850000, /* [059] -66.5 dB */
+ 0xc2840000, /* [060] -66.0 dB */
+ 0xc2830000, /* [061] -65.5 dB */
+ 0xc2820000, /* [062] -65.0 dB */
+ 0xc2810000, /* [063] -64.5 dB */
+ 0xc2800000, /* [064] -64.0 dB */
+ 0xc27e0000, /* [065] -63.5 dB */
+ 0xc27c0000, /* [066] -63.0 dB */
+ 0xc27a0000, /* [067] -62.5 dB */
+ 0xc2780000, /* [068] -62.0 dB */
+ 0xc2760000, /* [069] -61.5 dB */
+ 0xc2740000, /* [070] -61.0 dB */
+ 0xc2720000, /* [071] -60.5 dB */
+ 0xc2700000, /* [072] -60.0 dB */
+ 0xc26e0000, /* [073] -59.5 dB */
+ 0xc26c0000, /* [074] -59.0 dB */
+ 0xc26a0000, /* [075] -58.5 dB */
+ 0xc2680000, /* [076] -58.0 dB */
+ 0xc2660000, /* [077] -57.5 dB */
+ 0xc2640000, /* [078] -57.0 dB */
+ 0xc2620000, /* [079] -56.5 dB */
+ 0xc2600000, /* [080] -56.0 dB */
+ 0xc25e0000, /* [081] -55.5 dB */
+ 0xc25c0000, /* [082] -55.0 dB */
+ 0xc25a0000, /* [083] -54.5 dB */
+ 0xc2580000, /* [084] -54.0 dB */
+ 0xc2560000, /* [085] -53.5 dB */
+ 0xc2540000, /* [086] -53.0 dB */
+ 0xc2520000, /* [087] -52.5 dB */
+ 0xc2500000, /* [088] -52.0 dB */
+ 0xc24e0000, /* [089] -51.5 dB */
+ 0xc24c0000, /* [090] -51.0 dB */
+ 0xc24a0000, /* [091] -50.5 dB */
+ 0xc2480000, /* [092] -50.0 dB */
+ 0xc2460000, /* [093] -49.5 dB */
+ 0xc2440000, /* [094] -49.0 dB */
+ 0xc2420000, /* [095] -48.5 dB */
+ 0xc2400000, /* [096] -48.0 dB */
+ 0xc23e0000, /* [097] -47.5 dB */
+ 0xc23c0000, /* [098] -47.0 dB */
+ 0xc23a0000, /* [099] -46.5 dB */
+ 0xc2380000, /* [100] -46.0 dB */
+ 0xc2360000, /* [101] -45.5 dB */
+ 0xc2340000, /* [102] -45.0 dB */
+ 0xc2320000, /* [103] -44.5 dB */
+ 0xc2300000, /* [104] -44.0 dB */
+ 0xc22e0000, /* [105] -43.5 dB */
+ 0xc22c0000, /* [106] -43.0 dB */
+ 0xc22a0000, /* [107] -42.5 dB */
+ 0xc2280000, /* [108] -42.0 dB */
+ 0xc2260000, /* [109] -41.5 dB */
+ 0xc2240000, /* [110] -41.0 dB */
+ 0xc2220000, /* [111] -40.5 dB */
+ 0xc2200000, /* [112] -40.0 dB */
+ 0xc21e0000, /* [113] -39.5 dB */
+ 0xc21c0000, /* [114] -39.0 dB */
+ 0xc21a0000, /* [115] -38.5 dB */
+ 0xc2180000, /* [116] -38.0 dB */
+ 0xc2160000, /* [117] -37.5 dB */
+ 0xc2140000, /* [118] -37.0 dB */
+ 0xc2120000, /* [119] -36.5 dB */
+ 0xc2100000, /* [120] -36.0 dB */
+ 0xc20e0000, /* [121] -35.5 dB */
+ 0xc20c0000, /* [122] -35.0 dB */
+ 0xc20a0000, /* [123] -34.5 dB */
+ 0xc2080000, /* [124] -34.0 dB */
+ 0xc2060000, /* [125] -33.5 dB */
+ 0xc2040000, /* [126] -33.0 dB */
+ 0xc2020000, /* [127] -32.5 dB */
+ 0xc2000000, /* [128] -32.0 dB */
+ 0xc1fc0000, /* [129] -31.5 dB */
+ 0xc1f80000, /* [130] -31.0 dB */
+ 0xc1f40000, /* [131] -30.5 dB */
+ 0xc1f00000, /* [132] -30.0 dB */
+ 0xc1ec0000, /* [133] -29.5 dB */
+ 0xc1e80000, /* [134] -29.0 dB */
+ 0xc1e40000, /* [135] -28.5 dB */
+ 0xc1e00000, /* [136] -28.0 dB */
+ 0xc1dc0000, /* [137] -27.5 dB */
+ 0xc1d80000, /* [138] -27.0 dB */
+ 0xc1d40000, /* [139] -26.5 dB */
+ 0xc1d00000, /* [140] -26.0 dB */
+ 0xc1cc0000, /* [141] -25.5 dB */
+ 0xc1c80000, /* [142] -25.0 dB */
+ 0xc1c40000, /* [143] -24.5 dB */
+ 0xc1c00000, /* [144] -24.0 dB */
+ 0xc1bc0000, /* [145] -23.5 dB */
+ 0xc1b80000, /* [146] -23.0 dB */
+ 0xc1b40000, /* [147] -22.5 dB */
+ 0xc1b00000, /* [148] -22.0 dB */
+ 0xc1ac0000, /* [149] -21.5 dB */
+ 0xc1a80000, /* [150] -21.0 dB */
+ 0xc1a40000, /* [151] -20.5 dB */
+ 0xc1a00000, /* [152] -20.0 dB */
+ 0xc19c0000, /* [153] -19.5 dB */
+ 0xc1980000, /* [154] -19.0 dB */
+ 0xc1940000, /* [155] -18.5 dB */
+ 0xc1900000, /* [156] -18.0 dB */
+ 0xc18c0000, /* [157] -17.5 dB */
+ 0xc1880000, /* [158] -17.0 dB */
+ 0xc1840000, /* [159] -16.5 dB */
+ 0xc1800000, /* [160] -16.0 dB */
+ 0xc1780000, /* [161] -15.5 dB */
+ 0xc1700000, /* [162] -15.0 dB */
+ 0xc1680000, /* [163] -14.5 dB */
+ 0xc1600000, /* [164] -14.0 dB */
+ 0xc1580000, /* [165] -13.5 dB */
+ 0xc1500000, /* [166] -13.0 dB */
+ 0xc1480000, /* [167] -12.5 dB */
+ 0xc1400000, /* [168] -12.0 dB */
+ 0xc1380000, /* [169] -11.5 dB */
+ 0xc1300000, /* [170] -11.0 dB */
+ 0xc1280000, /* [171] -10.5 dB */
+ 0xc1200000, /* [172] -10.0 dB */
+ 0xc1180000, /* [173] -9.5 dB */
+ 0xc1100000, /* [174] -9.0 dB */
+ 0xc1080000, /* [175] -8.5 dB */
+ 0xc1000000, /* [176] -8.0 dB */
+ 0xc0f00000, /* [177] -7.5 dB */
+ 0xc0e00000, /* [178] -7.0 dB */
+ 0xc0d00000, /* [179] -6.5 dB */
+ 0xc0c00000, /* [180] -6.0 dB */
+ 0xc0b00000, /* [181] -5.5 dB */
+ 0xc0a00000, /* [182] -5.0 dB */
+ 0xc0900000, /* [183] -4.5 dB */
+ 0xc0800000, /* [184] -4.0 dB */
+ 0xc0600000, /* [185] -3.5 dB */
+ 0xc0400000, /* [186] -3.0 dB */
+ 0xc0200000, /* [187] -2.5 dB */
+ 0xc0000000, /* [188] -2.0 dB */
+ 0xbfc00000, /* [189] -1.5 dB */
+ 0xbf800000, /* [190] -1.0 dB */
+ 0xbf000000, /* [191] -0.5 dB */
+ 0x00000000, /* [192] 0.0 dB */
+ 0x3f000000, /* [193] 0.5 dB */
+ 0x3f800000, /* [194] 1.0 dB */
+ 0x3fc00000, /* [195] 1.5 dB */
+ 0x40000000, /* [196] 2.0 dB */
+ 0x40200000, /* [197] 2.5 dB */
+ 0x40400000, /* [198] 3.0 dB */
+ 0x40600000, /* [199] 3.5 dB */
+ 0x40800000, /* [200] 4.0 dB */
+ 0x40900000, /* [201] 4.5 dB */
+ 0x40a00000, /* [202] 5.0 dB */
+ 0x40b00000, /* [203] 5.5 dB */
+ 0x40c00000, /* [204] 6.0 dB */
+ 0x40d00000, /* [205] 6.5 dB */
+ 0x40e00000, /* [206] 7.0 dB */
+ 0x40f00000, /* [207] 7.5 dB */
+ 0x41000000, /* [208] 8.0 dB */
+ 0x41080000, /* [209] 8.5 dB */
+ 0x41100000, /* [210] 9.0 dB */
+ 0x41180000, /* [211] 9.5 dB */
+ 0x41200000, /* [212] 10.0 dB */
+ 0x41280000, /* [213] 10.5 dB */
+ 0x41300000, /* [214] 11.0 dB */
+ 0x41380000, /* [215] 11.5 dB */
+ 0x41400000, /* [216] 12.0 dB */
+ 0x41480000, /* [217] 12.5 dB */
+ 0x41500000, /* [218] 13.0 dB */
+ 0x41580000, /* [219] 13.5 dB */
+ 0x41600000, /* [220] 14.0 dB */
+ 0x41680000, /* [221] 14.5 dB */
+ 0x41700000, /* [222] 15.0 dB */
+ 0x41780000, /* [223] 15.5 dB */
+ 0x41800000, /* [224] 16.0 dB */
+ 0x41840000, /* [225] 16.5 dB */
+ 0x41880000, /* [226] 17.0 dB */
+ 0x418c0000, /* [227] 17.5 dB */
+ 0x41900000, /* [228] 18.0 dB */
+ 0x41940000, /* [229] 18.5 dB */
+ 0x41980000, /* [230] 19.0 dB */
+ 0x419c0000, /* [231] 19.5 dB */
+ 0x41a00000, /* [232] 20.0 dB */
+ 0x41a40000, /* [233] 20.5 dB */
+ 0x41a80000, /* [234] 21.0 dB */
+ 0x41ac0000, /* [235] 21.5 dB */
+ 0x41b00000, /* [236] 22.0 dB */
+ 0x41b40000, /* [237] 22.5 dB */
+ 0x41b80000, /* [238] 23.0 dB */
+ 0x41bc0000, /* [239] 23.5 dB */
+ 0x41c00000, /* [240] 24.0 dB */
+ 0x41c40000, /* [241] 24.5 dB */
+ 0x41c80000, /* [242] 25.0 dB */
+ 0x41cc0000, /* [243] 25.5 dB */
+ 0x41d00000, /* [244] 26.0 dB */
+ 0x41d40000, /* [245] 26.5 dB */
+ 0x41d80000, /* [246] 27.0 dB */
+ 0x41dc0000, /* [247] 27.5 dB */
+ 0x41e00000, /* [248] 28.0 dB */
+ 0x41e40000, /* [249] 28.5 dB */
+ 0x41e80000, /* [250] 29.0 dB */
+ 0x41ec0000, /* [251] 29.5 dB */
+ 0x41f00000, /* [252] 30.0 dB */
+ 0x41f40000, /* [253] 30.5 dB */
+ 0x41f80000, /* [254] 31.0 dB */
+ 0x41fc0000, /* [255] 31.5 dB */
+};
+
+#define MIXART_ANALOG_CAPTURE_LEVEL_MIN 0 /* -96.0 dB + 8.0 dB = -88.0 dB */
+#define MIXART_ANALOG_CAPTURE_LEVEL_MAX 255 /* 31.5 dB + 8.0 dB = 39.5 dB */
+#define MIXART_ANALOG_CAPTURE_ZERO_LEVEL 176 /* -8.0 dB + 8.0 dB = 0.0 dB */
+
+#define MIXART_ANALOG_PLAYBACK_LEVEL_MIN 0 /* -96.0 dB + 1.5 dB = -94.5 dB (possible is down to (-114.0+1.5)dB) */
+#define MIXART_ANALOG_PLAYBACK_LEVEL_MAX 192 /* 0.0 dB + 1.5 dB = 1.5 dB */
+#define MIXART_ANALOG_PLAYBACK_ZERO_LEVEL 189 /* -1.5 dB + 1.5 dB = 0.0 dB */
+
+static int mixart_update_analog_audio_level(mixart_t* chip, int is_capture)
+{
+ int i, err;
+ mixart_msg_t request;
+ mixart_io_level_t io_level;
+ mixart_return_uid_t resp;
+
+ memset(&io_level, 0, sizeof(io_level));
+ io_level.channel = -1; /* left and right */
+
+ for(i=0; i<2; i++) {
+ if(is_capture) {
+ io_level.level[i].analog_level = mixart_analog_level[chip->analog_capture_volume[i]];
+ } else {
+ if(chip->analog_playback_active[i])
+ io_level.level[i].analog_level = mixart_analog_level[chip->analog_playback_volume[i]];
+ else
+ io_level.level[i].analog_level = mixart_analog_level[MIXART_ANALOG_PLAYBACK_LEVEL_MIN];
+ }
+ }
+
+ if(is_capture) request.uid = chip->uid_in_analog_physio;
+ else request.uid = chip->uid_out_analog_physio;
+ request.message_id = MSG_PHYSICALIO_SET_LEVEL;
+ request.data = &io_level;
+ request.size = sizeof(io_level);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+ if((err<0) || (resp.error_code)) {
+ snd_printk(KERN_DEBUG "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n", chip->chip_idx, is_capture, resp.error_code);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * analog level control
+ */
+static int mixart_analog_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ if(kcontrol->private_value == 0) { /* playback */
+ uinfo->value.integer.min = MIXART_ANALOG_PLAYBACK_LEVEL_MIN; /* -96 dB */
+ uinfo->value.integer.max = MIXART_ANALOG_PLAYBACK_LEVEL_MAX; /* 0 dB */
+ } else { /* capture */
+ uinfo->value.integer.min = MIXART_ANALOG_CAPTURE_LEVEL_MIN; /* -96 dB */
+ uinfo->value.integer.max = MIXART_ANALOG_CAPTURE_LEVEL_MAX; /* 31.5 dB */
+ }
+ return 0;
+}
+
+static int mixart_analog_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ down(&chip->mgr->mixer_mutex);
+ if(kcontrol->private_value == 0) { /* playback */
+ ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
+ ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
+ } else { /* capture */
+ ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
+ ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
+ }
+ up(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_analog_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int is_capture, i;
+
+ down(&chip->mgr->mixer_mutex);
+ is_capture = (kcontrol->private_value != 0);
+ for(i=0; i<2; i++) {
+ int new_volume = ucontrol->value.integer.value[i];
+ int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i];
+ if(*stored_volume != new_volume) {
+ *stored_volume = new_volume;
+ changed = 1;
+ }
+ }
+ if(changed) mixart_update_analog_audio_level(chip, is_capture);
+ up(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_analog_level = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* name will be filled later */
+ .info = mixart_analog_vol_info,
+ .get = mixart_analog_vol_get,
+ .put = mixart_analog_vol_put,
+};
+
+/* shared */
+static int mixart_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int mixart_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+
+ down(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->analog_playback_active[0];
+ ucontrol->value.integer.value[1] = chip->analog_playback_active[1];
+ up(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int i, changed = 0;
+ down(&chip->mgr->mixer_mutex);
+ for(i=0; i<2; i++) {
+ if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
+ chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
+ changed = 1;
+ }
+ }
+ if(changed) mixart_update_analog_audio_level(chip, 0); /* update playback levels */
+ up(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_output_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = mixart_sw_info, /* shared */
+ .get = mixart_audio_sw_get,
+ .put = mixart_audio_sw_put
+};
+
+static u32 mixart_digital_level[256] = {
+ 0x00000000, /* [000] = 0.00e+000 = mute if <= -109.5dB */
+ 0x366e1c7a, /* [001] = 3.55e-006 = pow(10.0, 0.05 * -109.0dB) */
+ 0x367c3860, /* [002] = 3.76e-006 = pow(10.0, 0.05 * -108.5dB) */
+ 0x36859525, /* [003] = 3.98e-006 = pow(10.0, 0.05 * -108.0dB) */
+ 0x368d7f74, /* [004] = 4.22e-006 = pow(10.0, 0.05 * -107.5dB) */
+ 0x3695e1d4, /* [005] = 4.47e-006 = pow(10.0, 0.05 * -107.0dB) */
+ 0x369ec362, /* [006] = 4.73e-006 = pow(10.0, 0.05 * -106.5dB) */
+ 0x36a82ba8, /* [007] = 5.01e-006 = pow(10.0, 0.05 * -106.0dB) */
+ 0x36b222a0, /* [008] = 5.31e-006 = pow(10.0, 0.05 * -105.5dB) */
+ 0x36bcb0c1, /* [009] = 5.62e-006 = pow(10.0, 0.05 * -105.0dB) */
+ 0x36c7defd, /* [010] = 5.96e-006 = pow(10.0, 0.05 * -104.5dB) */
+ 0x36d3b6d3, /* [011] = 6.31e-006 = pow(10.0, 0.05 * -104.0dB) */
+ 0x36e0424e, /* [012] = 6.68e-006 = pow(10.0, 0.05 * -103.5dB) */
+ 0x36ed8c14, /* [013] = 7.08e-006 = pow(10.0, 0.05 * -103.0dB) */
+ 0x36fb9f6c, /* [014] = 7.50e-006 = pow(10.0, 0.05 * -102.5dB) */
+ 0x37054423, /* [015] = 7.94e-006 = pow(10.0, 0.05 * -102.0dB) */
+ 0x370d29a5, /* [016] = 8.41e-006 = pow(10.0, 0.05 * -101.5dB) */
+ 0x371586f0, /* [017] = 8.91e-006 = pow(10.0, 0.05 * -101.0dB) */
+ 0x371e631b, /* [018] = 9.44e-006 = pow(10.0, 0.05 * -100.5dB) */
+ 0x3727c5ac, /* [019] = 1.00e-005 = pow(10.0, 0.05 * -100.0dB) */
+ 0x3731b69a, /* [020] = 1.06e-005 = pow(10.0, 0.05 * -99.5dB) */
+ 0x373c3e53, /* [021] = 1.12e-005 = pow(10.0, 0.05 * -99.0dB) */
+ 0x374765c8, /* [022] = 1.19e-005 = pow(10.0, 0.05 * -98.5dB) */
+ 0x3753366f, /* [023] = 1.26e-005 = pow(10.0, 0.05 * -98.0dB) */
+ 0x375fba4f, /* [024] = 1.33e-005 = pow(10.0, 0.05 * -97.5dB) */
+ 0x376cfc07, /* [025] = 1.41e-005 = pow(10.0, 0.05 * -97.0dB) */
+ 0x377b06d5, /* [026] = 1.50e-005 = pow(10.0, 0.05 * -96.5dB) */
+ 0x3784f352, /* [027] = 1.58e-005 = pow(10.0, 0.05 * -96.0dB) */
+ 0x378cd40b, /* [028] = 1.68e-005 = pow(10.0, 0.05 * -95.5dB) */
+ 0x37952c42, /* [029] = 1.78e-005 = pow(10.0, 0.05 * -95.0dB) */
+ 0x379e030e, /* [030] = 1.88e-005 = pow(10.0, 0.05 * -94.5dB) */
+ 0x37a75fef, /* [031] = 2.00e-005 = pow(10.0, 0.05 * -94.0dB) */
+ 0x37b14ad5, /* [032] = 2.11e-005 = pow(10.0, 0.05 * -93.5dB) */
+ 0x37bbcc2c, /* [033] = 2.24e-005 = pow(10.0, 0.05 * -93.0dB) */
+ 0x37c6ecdd, /* [034] = 2.37e-005 = pow(10.0, 0.05 * -92.5dB) */
+ 0x37d2b65a, /* [035] = 2.51e-005 = pow(10.0, 0.05 * -92.0dB) */
+ 0x37df32a3, /* [036] = 2.66e-005 = pow(10.0, 0.05 * -91.5dB) */
+ 0x37ec6c50, /* [037] = 2.82e-005 = pow(10.0, 0.05 * -91.0dB) */
+ 0x37fa6e9b, /* [038] = 2.99e-005 = pow(10.0, 0.05 * -90.5dB) */
+ 0x3804a2b3, /* [039] = 3.16e-005 = pow(10.0, 0.05 * -90.0dB) */
+ 0x380c7ea4, /* [040] = 3.35e-005 = pow(10.0, 0.05 * -89.5dB) */
+ 0x3814d1cc, /* [041] = 3.55e-005 = pow(10.0, 0.05 * -89.0dB) */
+ 0x381da33c, /* [042] = 3.76e-005 = pow(10.0, 0.05 * -88.5dB) */
+ 0x3826fa6f, /* [043] = 3.98e-005 = pow(10.0, 0.05 * -88.0dB) */
+ 0x3830df51, /* [044] = 4.22e-005 = pow(10.0, 0.05 * -87.5dB) */
+ 0x383b5a49, /* [045] = 4.47e-005 = pow(10.0, 0.05 * -87.0dB) */
+ 0x3846743b, /* [046] = 4.73e-005 = pow(10.0, 0.05 * -86.5dB) */
+ 0x38523692, /* [047] = 5.01e-005 = pow(10.0, 0.05 * -86.0dB) */
+ 0x385eab48, /* [048] = 5.31e-005 = pow(10.0, 0.05 * -85.5dB) */
+ 0x386bdcf1, /* [049] = 5.62e-005 = pow(10.0, 0.05 * -85.0dB) */
+ 0x3879d6bc, /* [050] = 5.96e-005 = pow(10.0, 0.05 * -84.5dB) */
+ 0x38845244, /* [051] = 6.31e-005 = pow(10.0, 0.05 * -84.0dB) */
+ 0x388c2971, /* [052] = 6.68e-005 = pow(10.0, 0.05 * -83.5dB) */
+ 0x3894778d, /* [053] = 7.08e-005 = pow(10.0, 0.05 * -83.0dB) */
+ 0x389d43a4, /* [054] = 7.50e-005 = pow(10.0, 0.05 * -82.5dB) */
+ 0x38a6952c, /* [055] = 7.94e-005 = pow(10.0, 0.05 * -82.0dB) */
+ 0x38b0740f, /* [056] = 8.41e-005 = pow(10.0, 0.05 * -81.5dB) */
+ 0x38bae8ac, /* [057] = 8.91e-005 = pow(10.0, 0.05 * -81.0dB) */
+ 0x38c5fbe2, /* [058] = 9.44e-005 = pow(10.0, 0.05 * -80.5dB) */
+ 0x38d1b717, /* [059] = 1.00e-004 = pow(10.0, 0.05 * -80.0dB) */
+ 0x38de2440, /* [060] = 1.06e-004 = pow(10.0, 0.05 * -79.5dB) */
+ 0x38eb4de8, /* [061] = 1.12e-004 = pow(10.0, 0.05 * -79.0dB) */
+ 0x38f93f3a, /* [062] = 1.19e-004 = pow(10.0, 0.05 * -78.5dB) */
+ 0x39040206, /* [063] = 1.26e-004 = pow(10.0, 0.05 * -78.0dB) */
+ 0x390bd472, /* [064] = 1.33e-004 = pow(10.0, 0.05 * -77.5dB) */
+ 0x39141d84, /* [065] = 1.41e-004 = pow(10.0, 0.05 * -77.0dB) */
+ 0x391ce445, /* [066] = 1.50e-004 = pow(10.0, 0.05 * -76.5dB) */
+ 0x39263027, /* [067] = 1.58e-004 = pow(10.0, 0.05 * -76.0dB) */
+ 0x3930090d, /* [068] = 1.68e-004 = pow(10.0, 0.05 * -75.5dB) */
+ 0x393a7753, /* [069] = 1.78e-004 = pow(10.0, 0.05 * -75.0dB) */
+ 0x394583d2, /* [070] = 1.88e-004 = pow(10.0, 0.05 * -74.5dB) */
+ 0x395137ea, /* [071] = 2.00e-004 = pow(10.0, 0.05 * -74.0dB) */
+ 0x395d9d8a, /* [072] = 2.11e-004 = pow(10.0, 0.05 * -73.5dB) */
+ 0x396abf37, /* [073] = 2.24e-004 = pow(10.0, 0.05 * -73.0dB) */
+ 0x3978a814, /* [074] = 2.37e-004 = pow(10.0, 0.05 * -72.5dB) */
+ 0x3983b1f8, /* [075] = 2.51e-004 = pow(10.0, 0.05 * -72.0dB) */
+ 0x398b7fa6, /* [076] = 2.66e-004 = pow(10.0, 0.05 * -71.5dB) */
+ 0x3993c3b2, /* [077] = 2.82e-004 = pow(10.0, 0.05 * -71.0dB) */
+ 0x399c8521, /* [078] = 2.99e-004 = pow(10.0, 0.05 * -70.5dB) */
+ 0x39a5cb5f, /* [079] = 3.16e-004 = pow(10.0, 0.05 * -70.0dB) */
+ 0x39af9e4d, /* [080] = 3.35e-004 = pow(10.0, 0.05 * -69.5dB) */
+ 0x39ba063f, /* [081] = 3.55e-004 = pow(10.0, 0.05 * -69.0dB) */
+ 0x39c50c0b, /* [082] = 3.76e-004 = pow(10.0, 0.05 * -68.5dB) */
+ 0x39d0b90a, /* [083] = 3.98e-004 = pow(10.0, 0.05 * -68.0dB) */
+ 0x39dd1726, /* [084] = 4.22e-004 = pow(10.0, 0.05 * -67.5dB) */
+ 0x39ea30db, /* [085] = 4.47e-004 = pow(10.0, 0.05 * -67.0dB) */
+ 0x39f81149, /* [086] = 4.73e-004 = pow(10.0, 0.05 * -66.5dB) */
+ 0x3a03621b, /* [087] = 5.01e-004 = pow(10.0, 0.05 * -66.0dB) */
+ 0x3a0b2b0d, /* [088] = 5.31e-004 = pow(10.0, 0.05 * -65.5dB) */
+ 0x3a136a16, /* [089] = 5.62e-004 = pow(10.0, 0.05 * -65.0dB) */
+ 0x3a1c2636, /* [090] = 5.96e-004 = pow(10.0, 0.05 * -64.5dB) */
+ 0x3a2566d5, /* [091] = 6.31e-004 = pow(10.0, 0.05 * -64.0dB) */
+ 0x3a2f33cd, /* [092] = 6.68e-004 = pow(10.0, 0.05 * -63.5dB) */
+ 0x3a399570, /* [093] = 7.08e-004 = pow(10.0, 0.05 * -63.0dB) */
+ 0x3a44948c, /* [094] = 7.50e-004 = pow(10.0, 0.05 * -62.5dB) */
+ 0x3a503a77, /* [095] = 7.94e-004 = pow(10.0, 0.05 * -62.0dB) */
+ 0x3a5c9112, /* [096] = 8.41e-004 = pow(10.0, 0.05 * -61.5dB) */
+ 0x3a69a2d7, /* [097] = 8.91e-004 = pow(10.0, 0.05 * -61.0dB) */
+ 0x3a777ada, /* [098] = 9.44e-004 = pow(10.0, 0.05 * -60.5dB) */
+ 0x3a83126f, /* [099] = 1.00e-003 = pow(10.0, 0.05 * -60.0dB) */
+ 0x3a8ad6a8, /* [100] = 1.06e-003 = pow(10.0, 0.05 * -59.5dB) */
+ 0x3a9310b1, /* [101] = 1.12e-003 = pow(10.0, 0.05 * -59.0dB) */
+ 0x3a9bc784, /* [102] = 1.19e-003 = pow(10.0, 0.05 * -58.5dB) */
+ 0x3aa50287, /* [103] = 1.26e-003 = pow(10.0, 0.05 * -58.0dB) */
+ 0x3aaec98e, /* [104] = 1.33e-003 = pow(10.0, 0.05 * -57.5dB) */
+ 0x3ab924e5, /* [105] = 1.41e-003 = pow(10.0, 0.05 * -57.0dB) */
+ 0x3ac41d56, /* [106] = 1.50e-003 = pow(10.0, 0.05 * -56.5dB) */
+ 0x3acfbc31, /* [107] = 1.58e-003 = pow(10.0, 0.05 * -56.0dB) */
+ 0x3adc0b51, /* [108] = 1.68e-003 = pow(10.0, 0.05 * -55.5dB) */
+ 0x3ae91528, /* [109] = 1.78e-003 = pow(10.0, 0.05 * -55.0dB) */
+ 0x3af6e4c6, /* [110] = 1.88e-003 = pow(10.0, 0.05 * -54.5dB) */
+ 0x3b02c2f2, /* [111] = 2.00e-003 = pow(10.0, 0.05 * -54.0dB) */
+ 0x3b0a8276, /* [112] = 2.11e-003 = pow(10.0, 0.05 * -53.5dB) */
+ 0x3b12b782, /* [113] = 2.24e-003 = pow(10.0, 0.05 * -53.0dB) */
+ 0x3b1b690d, /* [114] = 2.37e-003 = pow(10.0, 0.05 * -52.5dB) */
+ 0x3b249e76, /* [115] = 2.51e-003 = pow(10.0, 0.05 * -52.0dB) */
+ 0x3b2e5f8f, /* [116] = 2.66e-003 = pow(10.0, 0.05 * -51.5dB) */
+ 0x3b38b49f, /* [117] = 2.82e-003 = pow(10.0, 0.05 * -51.0dB) */
+ 0x3b43a669, /* [118] = 2.99e-003 = pow(10.0, 0.05 * -50.5dB) */
+ 0x3b4f3e37, /* [119] = 3.16e-003 = pow(10.0, 0.05 * -50.0dB) */
+ 0x3b5b85e0, /* [120] = 3.35e-003 = pow(10.0, 0.05 * -49.5dB) */
+ 0x3b6887cf, /* [121] = 3.55e-003 = pow(10.0, 0.05 * -49.0dB) */
+ 0x3b764f0e, /* [122] = 3.76e-003 = pow(10.0, 0.05 * -48.5dB) */
+ 0x3b8273a6, /* [123] = 3.98e-003 = pow(10.0, 0.05 * -48.0dB) */
+ 0x3b8a2e77, /* [124] = 4.22e-003 = pow(10.0, 0.05 * -47.5dB) */
+ 0x3b925e89, /* [125] = 4.47e-003 = pow(10.0, 0.05 * -47.0dB) */
+ 0x3b9b0ace, /* [126] = 4.73e-003 = pow(10.0, 0.05 * -46.5dB) */
+ 0x3ba43aa2, /* [127] = 5.01e-003 = pow(10.0, 0.05 * -46.0dB) */
+ 0x3badf5d1, /* [128] = 5.31e-003 = pow(10.0, 0.05 * -45.5dB) */
+ 0x3bb8449c, /* [129] = 5.62e-003 = pow(10.0, 0.05 * -45.0dB) */
+ 0x3bc32fc3, /* [130] = 5.96e-003 = pow(10.0, 0.05 * -44.5dB) */
+ 0x3bcec08a, /* [131] = 6.31e-003 = pow(10.0, 0.05 * -44.0dB) */
+ 0x3bdb00c0, /* [132] = 6.68e-003 = pow(10.0, 0.05 * -43.5dB) */
+ 0x3be7facc, /* [133] = 7.08e-003 = pow(10.0, 0.05 * -43.0dB) */
+ 0x3bf5b9b0, /* [134] = 7.50e-003 = pow(10.0, 0.05 * -42.5dB) */
+ 0x3c02248a, /* [135] = 7.94e-003 = pow(10.0, 0.05 * -42.0dB) */
+ 0x3c09daac, /* [136] = 8.41e-003 = pow(10.0, 0.05 * -41.5dB) */
+ 0x3c1205c6, /* [137] = 8.91e-003 = pow(10.0, 0.05 * -41.0dB) */
+ 0x3c1aacc8, /* [138] = 9.44e-003 = pow(10.0, 0.05 * -40.5dB) */
+ 0x3c23d70a, /* [139] = 1.00e-002 = pow(10.0, 0.05 * -40.0dB) */
+ 0x3c2d8c52, /* [140] = 1.06e-002 = pow(10.0, 0.05 * -39.5dB) */
+ 0x3c37d4dd, /* [141] = 1.12e-002 = pow(10.0, 0.05 * -39.0dB) */
+ 0x3c42b965, /* [142] = 1.19e-002 = pow(10.0, 0.05 * -38.5dB) */
+ 0x3c4e4329, /* [143] = 1.26e-002 = pow(10.0, 0.05 * -38.0dB) */
+ 0x3c5a7bf1, /* [144] = 1.33e-002 = pow(10.0, 0.05 * -37.5dB) */
+ 0x3c676e1e, /* [145] = 1.41e-002 = pow(10.0, 0.05 * -37.0dB) */
+ 0x3c7524ac, /* [146] = 1.50e-002 = pow(10.0, 0.05 * -36.5dB) */
+ 0x3c81d59f, /* [147] = 1.58e-002 = pow(10.0, 0.05 * -36.0dB) */
+ 0x3c898712, /* [148] = 1.68e-002 = pow(10.0, 0.05 * -35.5dB) */
+ 0x3c91ad39, /* [149] = 1.78e-002 = pow(10.0, 0.05 * -35.0dB) */
+ 0x3c9a4efc, /* [150] = 1.88e-002 = pow(10.0, 0.05 * -34.5dB) */
+ 0x3ca373af, /* [151] = 2.00e-002 = pow(10.0, 0.05 * -34.0dB) */
+ 0x3cad2314, /* [152] = 2.11e-002 = pow(10.0, 0.05 * -33.5dB) */
+ 0x3cb76563, /* [153] = 2.24e-002 = pow(10.0, 0.05 * -33.0dB) */
+ 0x3cc24350, /* [154] = 2.37e-002 = pow(10.0, 0.05 * -32.5dB) */
+ 0x3ccdc614, /* [155] = 2.51e-002 = pow(10.0, 0.05 * -32.0dB) */
+ 0x3cd9f773, /* [156] = 2.66e-002 = pow(10.0, 0.05 * -31.5dB) */
+ 0x3ce6e1c6, /* [157] = 2.82e-002 = pow(10.0, 0.05 * -31.0dB) */
+ 0x3cf49003, /* [158] = 2.99e-002 = pow(10.0, 0.05 * -30.5dB) */
+ 0x3d0186e2, /* [159] = 3.16e-002 = pow(10.0, 0.05 * -30.0dB) */
+ 0x3d0933ac, /* [160] = 3.35e-002 = pow(10.0, 0.05 * -29.5dB) */
+ 0x3d1154e1, /* [161] = 3.55e-002 = pow(10.0, 0.05 * -29.0dB) */
+ 0x3d19f169, /* [162] = 3.76e-002 = pow(10.0, 0.05 * -28.5dB) */
+ 0x3d231090, /* [163] = 3.98e-002 = pow(10.0, 0.05 * -28.0dB) */
+ 0x3d2cba15, /* [164] = 4.22e-002 = pow(10.0, 0.05 * -27.5dB) */
+ 0x3d36f62b, /* [165] = 4.47e-002 = pow(10.0, 0.05 * -27.0dB) */
+ 0x3d41cd81, /* [166] = 4.73e-002 = pow(10.0, 0.05 * -26.5dB) */
+ 0x3d4d494a, /* [167] = 5.01e-002 = pow(10.0, 0.05 * -26.0dB) */
+ 0x3d597345, /* [168] = 5.31e-002 = pow(10.0, 0.05 * -25.5dB) */
+ 0x3d6655c3, /* [169] = 5.62e-002 = pow(10.0, 0.05 * -25.0dB) */
+ 0x3d73fbb4, /* [170] = 5.96e-002 = pow(10.0, 0.05 * -24.5dB) */
+ 0x3d813856, /* [171] = 6.31e-002 = pow(10.0, 0.05 * -24.0dB) */
+ 0x3d88e078, /* [172] = 6.68e-002 = pow(10.0, 0.05 * -23.5dB) */
+ 0x3d90fcbf, /* [173] = 7.08e-002 = pow(10.0, 0.05 * -23.0dB) */
+ 0x3d99940e, /* [174] = 7.50e-002 = pow(10.0, 0.05 * -22.5dB) */
+ 0x3da2adad, /* [175] = 7.94e-002 = pow(10.0, 0.05 * -22.0dB) */
+ 0x3dac5156, /* [176] = 8.41e-002 = pow(10.0, 0.05 * -21.5dB) */
+ 0x3db68738, /* [177] = 8.91e-002 = pow(10.0, 0.05 * -21.0dB) */
+ 0x3dc157fb, /* [178] = 9.44e-002 = pow(10.0, 0.05 * -20.5dB) */
+ 0x3dcccccd, /* [179] = 1.00e-001 = pow(10.0, 0.05 * -20.0dB) */
+ 0x3dd8ef67, /* [180] = 1.06e-001 = pow(10.0, 0.05 * -19.5dB) */
+ 0x3de5ca15, /* [181] = 1.12e-001 = pow(10.0, 0.05 * -19.0dB) */
+ 0x3df367bf, /* [182] = 1.19e-001 = pow(10.0, 0.05 * -18.5dB) */
+ 0x3e00e9f9, /* [183] = 1.26e-001 = pow(10.0, 0.05 * -18.0dB) */
+ 0x3e088d77, /* [184] = 1.33e-001 = pow(10.0, 0.05 * -17.5dB) */
+ 0x3e10a4d3, /* [185] = 1.41e-001 = pow(10.0, 0.05 * -17.0dB) */
+ 0x3e1936ec, /* [186] = 1.50e-001 = pow(10.0, 0.05 * -16.5dB) */
+ 0x3e224b06, /* [187] = 1.58e-001 = pow(10.0, 0.05 * -16.0dB) */
+ 0x3e2be8d7, /* [188] = 1.68e-001 = pow(10.0, 0.05 * -15.5dB) */
+ 0x3e361887, /* [189] = 1.78e-001 = pow(10.0, 0.05 * -15.0dB) */
+ 0x3e40e2bb, /* [190] = 1.88e-001 = pow(10.0, 0.05 * -14.5dB) */
+ 0x3e4c509b, /* [191] = 2.00e-001 = pow(10.0, 0.05 * -14.0dB) */
+ 0x3e586bd9, /* [192] = 2.11e-001 = pow(10.0, 0.05 * -13.5dB) */
+ 0x3e653ebb, /* [193] = 2.24e-001 = pow(10.0, 0.05 * -13.0dB) */
+ 0x3e72d424, /* [194] = 2.37e-001 = pow(10.0, 0.05 * -12.5dB) */
+ 0x3e809bcc, /* [195] = 2.51e-001 = pow(10.0, 0.05 * -12.0dB) */
+ 0x3e883aa8, /* [196] = 2.66e-001 = pow(10.0, 0.05 * -11.5dB) */
+ 0x3e904d1c, /* [197] = 2.82e-001 = pow(10.0, 0.05 * -11.0dB) */
+ 0x3e98da02, /* [198] = 2.99e-001 = pow(10.0, 0.05 * -10.5dB) */
+ 0x3ea1e89b, /* [199] = 3.16e-001 = pow(10.0, 0.05 * -10.0dB) */
+ 0x3eab8097, /* [200] = 3.35e-001 = pow(10.0, 0.05 * -9.5dB) */
+ 0x3eb5aa1a, /* [201] = 3.55e-001 = pow(10.0, 0.05 * -9.0dB) */
+ 0x3ec06dc3, /* [202] = 3.76e-001 = pow(10.0, 0.05 * -8.5dB) */
+ 0x3ecbd4b4, /* [203] = 3.98e-001 = pow(10.0, 0.05 * -8.0dB) */
+ 0x3ed7e89b, /* [204] = 4.22e-001 = pow(10.0, 0.05 * -7.5dB) */
+ 0x3ee4b3b6, /* [205] = 4.47e-001 = pow(10.0, 0.05 * -7.0dB) */
+ 0x3ef240e2, /* [206] = 4.73e-001 = pow(10.0, 0.05 * -6.5dB) */
+ 0x3f004dce, /* [207] = 5.01e-001 = pow(10.0, 0.05 * -6.0dB) */
+ 0x3f07e80b, /* [208] = 5.31e-001 = pow(10.0, 0.05 * -5.5dB) */
+ 0x3f0ff59a, /* [209] = 5.62e-001 = pow(10.0, 0.05 * -5.0dB) */
+ 0x3f187d50, /* [210] = 5.96e-001 = pow(10.0, 0.05 * -4.5dB) */
+ 0x3f21866c, /* [211] = 6.31e-001 = pow(10.0, 0.05 * -4.0dB) */
+ 0x3f2b1896, /* [212] = 6.68e-001 = pow(10.0, 0.05 * -3.5dB) */
+ 0x3f353bef, /* [213] = 7.08e-001 = pow(10.0, 0.05 * -3.0dB) */
+ 0x3f3ff911, /* [214] = 7.50e-001 = pow(10.0, 0.05 * -2.5dB) */
+ 0x3f4b5918, /* [215] = 7.94e-001 = pow(10.0, 0.05 * -2.0dB) */
+ 0x3f5765ac, /* [216] = 8.41e-001 = pow(10.0, 0.05 * -1.5dB) */
+ 0x3f642905, /* [217] = 8.91e-001 = pow(10.0, 0.05 * -1.0dB) */
+ 0x3f71adf9, /* [218] = 9.44e-001 = pow(10.0, 0.05 * -0.5dB) */
+ 0x3f800000, /* [219] = 1.00e+000 = pow(10.0, 0.05 * 0.0dB) */
+ 0x3f8795a0, /* [220] = 1.06e+000 = pow(10.0, 0.05 * 0.5dB) */
+ 0x3f8f9e4d, /* [221] = 1.12e+000 = pow(10.0, 0.05 * 1.0dB) */
+ 0x3f9820d7, /* [222] = 1.19e+000 = pow(10.0, 0.05 * 1.5dB) */
+ 0x3fa12478, /* [223] = 1.26e+000 = pow(10.0, 0.05 * 2.0dB) */
+ 0x3faab0d5, /* [224] = 1.33e+000 = pow(10.0, 0.05 * 2.5dB) */
+ 0x3fb4ce08, /* [225] = 1.41e+000 = pow(10.0, 0.05 * 3.0dB) */
+ 0x3fbf84a6, /* [226] = 1.50e+000 = pow(10.0, 0.05 * 3.5dB) */
+ 0x3fcaddc8, /* [227] = 1.58e+000 = pow(10.0, 0.05 * 4.0dB) */
+ 0x3fd6e30d, /* [228] = 1.68e+000 = pow(10.0, 0.05 * 4.5dB) */
+ 0x3fe39ea9, /* [229] = 1.78e+000 = pow(10.0, 0.05 * 5.0dB) */
+ 0x3ff11b6a, /* [230] = 1.88e+000 = pow(10.0, 0.05 * 5.5dB) */
+ 0x3fff64c1, /* [231] = 2.00e+000 = pow(10.0, 0.05 * 6.0dB) */
+ 0x40074368, /* [232] = 2.11e+000 = pow(10.0, 0.05 * 6.5dB) */
+ 0x400f4735, /* [233] = 2.24e+000 = pow(10.0, 0.05 * 7.0dB) */
+ 0x4017c496, /* [234] = 2.37e+000 = pow(10.0, 0.05 * 7.5dB) */
+ 0x4020c2bf, /* [235] = 2.51e+000 = pow(10.0, 0.05 * 8.0dB) */
+ 0x402a4952, /* [236] = 2.66e+000 = pow(10.0, 0.05 * 8.5dB) */
+ 0x40346063, /* [237] = 2.82e+000 = pow(10.0, 0.05 * 9.0dB) */
+ 0x403f1082, /* [238] = 2.99e+000 = pow(10.0, 0.05 * 9.5dB) */
+ 0x404a62c2, /* [239] = 3.16e+000 = pow(10.0, 0.05 * 10.0dB) */
+ 0x405660bd, /* [240] = 3.35e+000 = pow(10.0, 0.05 * 10.5dB) */
+ 0x406314a0, /* [241] = 3.55e+000 = pow(10.0, 0.05 * 11.0dB) */
+ 0x40708933, /* [242] = 3.76e+000 = pow(10.0, 0.05 * 11.5dB) */
+ 0x407ec9e1, /* [243] = 3.98e+000 = pow(10.0, 0.05 * 12.0dB) */
+ 0x4086f161, /* [244] = 4.22e+000 = pow(10.0, 0.05 * 12.5dB) */
+ 0x408ef052, /* [245] = 4.47e+000 = pow(10.0, 0.05 * 13.0dB) */
+ 0x4097688d, /* [246] = 4.73e+000 = pow(10.0, 0.05 * 13.5dB) */
+ 0x40a06142, /* [247] = 5.01e+000 = pow(10.0, 0.05 * 14.0dB) */
+ 0x40a9e20e, /* [248] = 5.31e+000 = pow(10.0, 0.05 * 14.5dB) */
+ 0x40b3f300, /* [249] = 5.62e+000 = pow(10.0, 0.05 * 15.0dB) */
+ 0x40be9ca5, /* [250] = 5.96e+000 = pow(10.0, 0.05 * 15.5dB) */
+ 0x40c9e807, /* [251] = 6.31e+000 = pow(10.0, 0.05 * 16.0dB) */
+ 0x40d5debc, /* [252] = 6.68e+000 = pow(10.0, 0.05 * 16.5dB) */
+ 0x40e28aeb, /* [253] = 7.08e+000 = pow(10.0, 0.05 * 17.0dB) */
+ 0x40eff755, /* [254] = 7.50e+000 = pow(10.0, 0.05 * 17.5dB) */
+ 0x40fe2f5e, /* [255] = 7.94e+000 = pow(10.0, 0.05 * 18.0dB) */
+};
+
+#define MIXART_DIGITAL_LEVEL_MIN 0 /* -109.5 dB */
+#define MIXART_DIGITAL_LEVEL_MAX 255 /* 18.0 dB */
+#define MIXART_DIGITAL_ZERO_LEVEL 219 /* 0.0 dB */
+
+
+int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx)
+{
+ int err, i;
+ int volume[2];
+ mixart_msg_t request;
+ mixart_set_out_stream_level_req_t set_level;
+ u32 status;
+ mixart_pipe_t *pipe;
+
+ memset(&set_level, 0, sizeof(set_level));
+ set_level.nb_of_stream = 1;
+ set_level.stream_level.desc.stream_idx = idx;
+
+ if(is_aes) {
+ pipe = &chip->pipe_out_dig; /* AES playback */
+ idx += MIXART_PLAYBACK_STREAMS;
+ } else {
+ pipe = &chip->pipe_out_ana; /* analog playback */
+ }
+
+ /* only when pipe exists ! */
+ if(pipe->status == PIPE_UNDEFINED)
+ return 0;
+
+ set_level.stream_level.desc.uid_pipe = pipe->group_uid;
+
+ for(i=0; i<2; i++) {
+ if(chip->digital_playback_active[idx][i])
+ volume[i] = chip->digital_playback_volume[idx][i];
+ else
+ volume[i] = MIXART_DIGITAL_LEVEL_MIN;
+ }
+
+ set_level.stream_level.out_level.valid_mask1 = MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 | MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2;
+ set_level.stream_level.out_level.left_to_out1_level = mixart_digital_level[volume[0]];
+ set_level.stream_level.out_level.right_to_out2_level = mixart_digital_level[volume[1]];
+
+ request.message_id = MSG_STREAM_SET_OUT_STREAM_LEVEL;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = &set_level;
+ request.size = sizeof(set_level);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
+ if((err<0) || status) {
+ snd_printk(KERN_DEBUG "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int mixart_update_capture_stream_level(mixart_t* chip, int is_aes)
+{
+ int err, i, idx;
+ mixart_pipe_t* pipe;
+ mixart_msg_t request;
+ mixart_set_in_audio_level_req_t set_level;
+ u32 status;
+
+ if(is_aes) {
+ idx = 1;
+ pipe = &chip->pipe_in_dig;
+ } else {
+ idx = 0;
+ pipe = &chip->pipe_in_ana;
+ }
+
+ /* only when pipe exists ! */
+ if(pipe->status == PIPE_UNDEFINED)
+ return 0;
+
+ memset(&set_level, 0, sizeof(set_level));
+ set_level.audio_count = 2;
+ set_level.level[0].connector = pipe->uid_left_connector;
+ set_level.level[1].connector = pipe->uid_right_connector;
+
+ for(i=0; i<2; i++) {
+ set_level.level[i].valid_mask1 = MIXART_AUDIO_LEVEL_DIGITAL_MASK;
+ set_level.level[i].digital_level = mixart_digital_level[chip->digital_capture_volume[idx][i]];
+ }
+
+ request.message_id = MSG_STREAM_SET_IN_AUDIO_LEVEL;
+ request.uid = (mixart_uid_t){0,0};
+ request.data = &set_level;
+ request.size = sizeof(set_level);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
+ if((err<0) || status) {
+ snd_printk(KERN_DEBUG "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/* shared */
+static int mixart_digital_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = MIXART_DIGITAL_LEVEL_MIN; /* -109.5 dB */
+ uinfo->value.integer.max = MIXART_DIGITAL_LEVEL_MAX; /* 18.0 dB */
+ return 0;
+}
+
+#define MIXART_VOL_REC_MASK 1
+#define MIXART_VOL_AES_MASK 2
+
+static int mixart_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int *stored_volume;
+ int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
+ int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+ down(&chip->mgr->mixer_mutex);
+ if(is_capture) {
+ if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
+ else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
+ } else {
+ snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
+ else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */
+ }
+ ucontrol->value.integer.value[0] = stored_volume[0];
+ ucontrol->value.integer.value[1] = stored_volume[1];
+ up(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int changed = 0;
+ int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
+ int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+ int* stored_volume;
+ int i;
+ down(&chip->mgr->mixer_mutex);
+ if(is_capture) {
+ if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
+ else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
+ } else {
+ snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
+ else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */
+ }
+ for(i=0; i<2; i++) {
+ if(stored_volume[i] != ucontrol->value.integer.value[i]) {
+ stored_volume[i] = ucontrol->value.integer.value[i];
+ changed = 1;
+ }
+ }
+ if(changed) {
+ if(is_capture) mixart_update_capture_stream_level(chip, is_aes);
+ else mixart_update_playback_stream_level(chip, is_aes, idx);
+ }
+ up(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static snd_kcontrol_new_t snd_mixart_pcm_vol =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* name will be filled later */
+ /* count will be filled later */
+ .info = mixart_digital_vol_info, /* shared */
+ .get = mixart_pcm_vol_get,
+ .put = mixart_pcm_vol_put,
+};
+
+
+static int mixart_pcm_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ down(&chip->mgr->mixer_mutex);
+ if(kcontrol->private_value & MIXART_VOL_AES_MASK) /* AES playback */
+ idx += MIXART_PLAYBACK_STREAMS;
+ ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0];
+ ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1];
+ up(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_pcm_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int i, j;
+ snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ down(&chip->mgr->mixer_mutex);
+ j = idx;
+ if(is_aes) j += MIXART_PLAYBACK_STREAMS;
+ for(i=0; i<2; i++) {
+ if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
+ chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
+ changed = 1;
+ }
+ }
+ if(changed) mixart_update_playback_stream_level(chip, is_aes, idx);
+ up(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_pcm_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* name will be filled later */
+ .count = MIXART_PLAYBACK_STREAMS,
+ .info = mixart_sw_info, /* shared */
+ .get = mixart_pcm_sw_get,
+ .put = mixart_pcm_sw_put
+};
+
+static int mixart_update_monitoring(mixart_t* chip, int channel)
+{
+ int err;
+ mixart_msg_t request;
+ mixart_set_out_audio_level_t audio_level;
+ u32 resp;
+
+ if(chip->pipe_out_ana.status == PIPE_UNDEFINED)
+ return -EINVAL; /* no pipe defined */
+
+ if(!channel) request.uid = chip->pipe_out_ana.uid_left_connector;
+ else request.uid = chip->pipe_out_ana.uid_right_connector;
+ request.message_id = MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL;
+ request.data = &audio_level;
+ request.size = sizeof(audio_level);
+
+ memset(&audio_level, 0, sizeof(audio_level));
+ audio_level.valid_mask1 = MIXART_AUDIO_LEVEL_MONITOR_MASK | MIXART_AUDIO_LEVEL_MUTE_M1_MASK;
+ audio_level.monitor_level = mixart_digital_level[chip->monitoring_volume[channel!=0]];
+ audio_level.monitor_mute1 = !chip->monitoring_active[channel!=0];
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+ if((err<0) || resp) {
+ snd_printk(KERN_DEBUG "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n", chip->chip_idx, resp);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * monitoring level control
+ */
+
+static int mixart_monitor_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ down(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->monitoring_volume[0];
+ ucontrol->value.integer.value[1] = chip->monitoring_volume[1];
+ up(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_monitor_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int i;
+ down(&chip->mgr->mixer_mutex);
+ for(i=0; i<2; i++) {
+ if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
+ chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
+ mixart_update_monitoring(chip, i);
+ changed = 1;
+ }
+ }
+ up(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_monitor_vol = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitoring Volume",
+ .info = mixart_digital_vol_info, /* shared */
+ .get = mixart_monitor_vol_get,
+ .put = mixart_monitor_vol_put,
+};
+
+/*
+ * monitoring switch control
+ */
+
+static int mixart_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ down(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->monitoring_active[0];
+ ucontrol->value.integer.value[1] = chip->monitoring_active[1];
+ up(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ mixart_t *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int i;
+ down(&chip->mgr->mixer_mutex);
+ for(i=0; i<2; i++) {
+ if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
+ chip->monitoring_active[i] = ucontrol->value.integer.value[i];
+ changed |= (1<<i); /* mask 0x01 ans 0x02 */
+ }
+ }
+ if(changed) {
+ /* allocate or release resources for monitoring */
+ int allocate = chip->monitoring_active[0] || chip->monitoring_active[1];
+ if(allocate) {
+ snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1); /* allocate the playback pipe for monitoring */
+ snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1); /* allocate the capture pipe for monitoring */
+ }
+ if(changed & 0x01) mixart_update_monitoring(chip, 0);
+ if(changed & 0x02) mixart_update_monitoring(chip, 1);
+ if(!allocate) {
+ snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1); /* release the capture pipe for monitoring */
+ snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1); /* release the playback pipe for monitoring */
+ }
+ }
+
+ up(&chip->mgr->mixer_mutex);
+ return (changed != 0);
+}
+
+static snd_kcontrol_new_t mixart_control_monitor_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitoring Switch",
+ .info = mixart_sw_info, /* shared */
+ .get = mixart_monitor_sw_get,
+ .put = mixart_monitor_sw_put
+};
+
+
+static void mixart_reset_audio_levels(mixart_t *chip)
+{
+ /* analog volumes can be set even if there is no pipe */
+ mixart_update_analog_audio_level(chip, 0);
+ /* analog levels for capture only on the first two chips */
+ if(chip->chip_idx < 2) {
+ mixart_update_analog_audio_level(chip, 1);
+ }
+ return;
+}
+
+
+int snd_mixart_create_mixer(mixart_mgr_t *mgr)
+{
+ mixart_t *chip;
+ int err, i;
+
+ init_MUTEX(&mgr->mixer_mutex); /* can be in another place */
+
+ for(i=0; i<mgr->num_cards; i++) {
+ snd_kcontrol_new_t temp;
+ chip = mgr->chip[i];
+
+ /* analog output level control */
+ temp = mixart_control_analog_level;
+ temp.name = "Master Playback Volume";
+ temp.private_value = 0; /* playback */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+ /* output mute controls */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip))) < 0)
+ return err;
+
+ /* analog input level control only on first two chips !*/
+ if(i<2) {
+ temp = mixart_control_analog_level;
+ temp.name = "Master Capture Volume";
+ temp.private_value = 1; /* capture */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+ }
+
+ temp = snd_mixart_pcm_vol;
+ temp.name = "PCM Playback Volume";
+ temp.count = MIXART_PLAYBACK_STREAMS;
+ temp.private_value = 0; /* playback analog */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+
+ temp.name = "PCM Capture Volume";
+ temp.count = 1;
+ temp.private_value = MIXART_VOL_REC_MASK; /* capture analog */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+
+ if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+ temp.name = "AES Playback Volume";
+ temp.count = MIXART_PLAYBACK_STREAMS;
+ temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+
+ temp.name = "AES Capture Volume";
+ temp.count = 0;
+ temp.private_value = MIXART_VOL_REC_MASK | MIXART_VOL_AES_MASK; /* capture AES/EBU */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+ }
+ temp = mixart_control_pcm_switch;
+ temp.name = "PCM Playback Switch";
+ temp.private_value = 0; /* playback analog */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+
+ if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+ temp.name = "AES Playback Switch";
+ temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ return err;
+ }
+
+ /* monitoring */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip))) < 0)
+ return err;
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip))) < 0)
+ return err;
+
+ /* init all mixer data and program the master volumes/switches */
+ mixart_reset_audio_levels(chip);
+ }
+ return 0;
+}
diff --git a/sound/pci/mixart/mixart_mixer.h b/sound/pci/mixart/mixart_mixer.h
new file mode 100644
index 00000000000..b4d9535087c
--- /dev/null
+++ b/sound/pci/mixart/mixart_mixer.h
@@ -0,0 +1,31 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * include file for mixer
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_MIXER_H
+#define __SOUND_MIXART_MIXER_H
+
+/* exported */
+int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx);
+int mixart_update_capture_stream_level(mixart_t* chip, int is_aes);
+int snd_mixart_create_mixer(mixart_mgr_t* mgr);
+
+#endif /* __SOUND_MIXART_MIXER_H */
diff --git a/sound/pci/nm256/Makefile b/sound/pci/nm256/Makefile
new file mode 100644
index 00000000000..d91d8c51921
--- /dev/null
+++ b/sound/pci/nm256/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-nm256-objs := nm256.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_NM256) += snd-nm256.o
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
new file mode 100644
index 00000000000..356fbeac6f9
--- /dev/null
+++ b/sound/pci/nm256/nm256.c
@@ -0,0 +1,1657 @@
+/*
+ * Driver for NeoMagic 256AV and 256ZX chipsets.
+ * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ * Based on nm256_audio.c OSS driver in linux kernel.
+ * The original author of OSS nm256 driver wishes to remain anonymous,
+ * so I just put my acknoledgment to him/her here.
+ * The original author's web page is found at
+ * http://www.uglx.org/sony.html
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#define CARD_NAME "NeoMagic 256AV/ZX"
+#define DRIVER_NAME "NM256"
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("NeoMagic NM256AV/ZX");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{NeoMagic,NM256AV},"
+ "{NeoMagic,NM256ZX}}");
+
+/*
+ * some compile conditions.
+ */
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static int playback_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16};
+static int capture_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16};
+static int force_ac97[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled as default */
+static int buffer_top[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* not specified */
+static int use_cache[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */
+static int vaio_hack[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */
+static int reset_workaround[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this soundcard.");
+module_param_array(playback_bufsize, int, NULL, 0444);
+MODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard.");
+module_param_array(capture_bufsize, int, NULL, 0444);
+MODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard.");
+module_param_array(force_ac97, bool, NULL, 0444);
+MODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard.");
+module_param_array(buffer_top, int, NULL, 0444);
+MODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard.");
+module_param_array(use_cache, bool, NULL, 0444);
+MODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access.");
+module_param_array(vaio_hack, bool, NULL, 0444);
+MODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks.");
+module_param_array(reset_workaround, bool, NULL, 0444);
+MODULE_PARM_DESC(reset_workaround, "Enable AC97 RESET workaround for some laptops.");
+
+/*
+ * hw definitions
+ */
+
+/* The BIOS signature. */
+#define NM_SIGNATURE 0x4e4d0000
+/* Signature mask. */
+#define NM_SIG_MASK 0xffff0000
+
+/* Size of the second memory area. */
+#define NM_PORT2_SIZE 4096
+
+/* The base offset of the mixer in the second memory area. */
+#define NM_MIXER_OFFSET 0x600
+
+/* The maximum size of a coefficient entry. */
+#define NM_MAX_PLAYBACK_COEF_SIZE 0x5000
+#define NM_MAX_RECORD_COEF_SIZE 0x1260
+
+/* The interrupt register. */
+#define NM_INT_REG 0xa04
+/* And its bits. */
+#define NM_PLAYBACK_INT 0x40
+#define NM_RECORD_INT 0x100
+#define NM_MISC_INT_1 0x4000
+#define NM_MISC_INT_2 0x1
+#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1)
+
+/* The AV's "mixer ready" status bit and location. */
+#define NM_MIXER_STATUS_OFFSET 0xa04
+#define NM_MIXER_READY_MASK 0x0800
+#define NM_MIXER_PRESENCE 0xa06
+#define NM_PRESENCE_MASK 0x0050
+#define NM_PRESENCE_VALUE 0x0040
+
+/*
+ * For the ZX. It uses the same interrupt register, but it holds 32
+ * bits instead of 16.
+ */
+#define NM2_PLAYBACK_INT 0x10000
+#define NM2_RECORD_INT 0x80000
+#define NM2_MISC_INT_1 0x8
+#define NM2_MISC_INT_2 0x2
+#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X))
+
+/* The ZX's "mixer ready" status bit and location. */
+#define NM2_MIXER_STATUS_OFFSET 0xa06
+#define NM2_MIXER_READY_MASK 0x0800
+
+/* The playback registers start from here. */
+#define NM_PLAYBACK_REG_OFFSET 0x0
+/* The record registers start from here. */
+#define NM_RECORD_REG_OFFSET 0x200
+
+/* The rate register is located 2 bytes from the start of the register area. */
+#define NM_RATE_REG_OFFSET 2
+
+/* Mono/stereo flag, number of bits on playback, and rate mask. */
+#define NM_RATE_STEREO 1
+#define NM_RATE_BITS_16 2
+#define NM_RATE_MASK 0xf0
+
+/* Playback enable register. */
+#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1)
+#define NM_PLAYBACK_ENABLE_FLAG 1
+#define NM_PLAYBACK_ONESHOT 2
+#define NM_PLAYBACK_FREERUN 4
+
+/* Mutes the audio output. */
+#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18)
+#define NM_AUDIO_MUTE_LEFT 0x8000
+#define NM_AUDIO_MUTE_RIGHT 0x0080
+
+/* Recording enable register. */
+#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
+#define NM_RECORD_ENABLE_FLAG 1
+#define NM_RECORD_FREERUN 2
+
+/* coefficient buffer pointer */
+#define NM_COEFF_START_OFFSET 0x1c
+#define NM_COEFF_END_OFFSET 0x20
+
+/* DMA buffer offsets */
+#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4)
+#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10)
+#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc)
+#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8)
+
+#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4)
+#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14)
+#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc)
+#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8)
+
+/*
+ * type definitions
+ */
+
+typedef struct snd_nm256 nm256_t;
+typedef struct snd_nm256_stream nm256_stream_t;
+
+struct snd_nm256_stream {
+
+ nm256_t *chip;
+ snd_pcm_substream_t *substream;
+ int running;
+
+ u32 buf; /* offset from chip->buffer */
+ int bufsize; /* buffer size in bytes */
+ void __iomem *bufptr; /* mapped pointer */
+ unsigned long bufptr_addr; /* physical address of the mapped pointer */
+
+ int dma_size; /* buffer size of the substream in bytes */
+ int period_size; /* period size in bytes */
+ int periods; /* # of periods */
+ int shift; /* bit shifts */
+ int cur_period; /* current period # */
+
+};
+
+struct snd_nm256 {
+
+ snd_card_t *card;
+
+ void __iomem *cport; /* control port */
+ struct resource *res_cport; /* its resource */
+ unsigned long cport_addr; /* physical address */
+
+ void __iomem *buffer; /* buffer */
+ struct resource *res_buffer; /* its resource */
+ unsigned long buffer_addr; /* buffer phyiscal address */
+
+ u32 buffer_start; /* start offset from pci resource 0 */
+ u32 buffer_end; /* end offset */
+ u32 buffer_size; /* total buffer size */
+
+ u32 all_coeff_buf; /* coefficient buffer */
+ u32 coeff_buf[2]; /* coefficient buffer for each stream */
+
+ unsigned int coeffs_current: 1; /* coeff. table is loaded? */
+ unsigned int use_cache: 1; /* use one big coef. table */
+ unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */
+
+ int mixer_base; /* register offset of ac97 mixer */
+ int mixer_status_offset; /* offset of mixer status reg. */
+ int mixer_status_mask; /* bit mask to test the mixer status */
+
+ int irq;
+ irqreturn_t (*interrupt)(int, void *, struct pt_regs *);
+ int badintrcount; /* counter to check bogus interrupts */
+
+ nm256_stream_t streams[2];
+
+ ac97_t *ac97;
+
+ snd_pcm_t *pcm;
+
+ struct pci_dev *pci;
+
+ spinlock_t reg_lock;
+
+};
+
+
+/*
+ * include coefficient table
+ */
+#include "nm256_coef.c"
+
+
+/*
+ * PCI ids
+ */
+
+#ifndef PCI_VENDOR_ID_NEOMAGIC
+#define PCI_VENDOR_ID_NEOMEGIC 0x10c8
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016
+#endif
+
+
+static struct pci_device_id snd_nm256_ids[] = {
+ {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, snd_nm256_ids);
+
+
+/*
+ * lowlvel stuffs
+ */
+
+inline static u8
+snd_nm256_readb(nm256_t *chip, int offset)
+{
+ return readb(chip->cport + offset);
+}
+
+inline static u16
+snd_nm256_readw(nm256_t *chip, int offset)
+{
+ return readw(chip->cport + offset);
+}
+
+inline static u32
+snd_nm256_readl(nm256_t *chip, int offset)
+{
+ return readl(chip->cport + offset);
+}
+
+inline static void
+snd_nm256_writeb(nm256_t *chip, int offset, u8 val)
+{
+ writeb(val, chip->cport + offset);
+}
+
+inline static void
+snd_nm256_writew(nm256_t *chip, int offset, u16 val)
+{
+ writew(val, chip->cport + offset);
+}
+
+inline static void
+snd_nm256_writel(nm256_t *chip, int offset, u32 val)
+{
+ writel(val, chip->cport + offset);
+}
+
+inline static void
+snd_nm256_write_buffer(nm256_t *chip, void *src, int offset, int size)
+{
+ offset -= chip->buffer_start;
+#ifdef SNDRV_CONFIG_DEBUG
+ if (offset < 0 || offset >= chip->buffer_size) {
+ snd_printk("write_buffer invalid offset = %d size = %d\n", offset, size);
+ return;
+ }
+#endif
+ memcpy_toio(chip->buffer + offset, src, size);
+}
+
+/*
+ * coefficient handlers -- what a magic!
+ */
+
+static u16
+snd_nm256_get_start_offset(int which)
+{
+ u16 offset = 0;
+ while (which-- > 0)
+ offset += coefficient_sizes[which];
+ return offset;
+}
+
+static void
+snd_nm256_load_one_coefficient(nm256_t *chip, int stream, u32 port, int which)
+{
+ u32 coeff_buf = chip->coeff_buf[stream];
+ u16 offset = snd_nm256_get_start_offset(which);
+ u16 size = coefficient_sizes[which];
+
+ snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size);
+ snd_nm256_writel(chip, port, coeff_buf);
+ /* ??? Record seems to behave differently than playback. */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ size--;
+ snd_nm256_writel(chip, port + 4, coeff_buf + size);
+}
+
+static void
+snd_nm256_load_coefficient(nm256_t *chip, int stream, int number)
+{
+ /* The enable register for the specified engine. */
+ u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG);
+ u32 addr = NM_COEFF_START_OFFSET;
+
+ addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET);
+
+ if (snd_nm256_readb(chip, poffset) & 1) {
+ snd_printd("NM256: Engine was enabled while loading coefficients!\n");
+ return;
+ }
+
+ /* The recording engine uses coefficient values 8-15. */
+ number &= 7;
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ number += 8;
+
+ if (! chip->use_cache) {
+ snd_nm256_load_one_coefficient(chip, stream, addr, number);
+ return;
+ }
+ if (! chip->coeffs_current) {
+ snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf,
+ NM_TOTAL_COEFF_COUNT * 4);
+ chip->coeffs_current = 1;
+ } else {
+ u32 base = chip->all_coeff_buf;
+ u32 offset = snd_nm256_get_start_offset(number);
+ u32 end_offset = offset + coefficient_sizes[number];
+ snd_nm256_writel(chip, addr, base + offset);
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ end_offset--;
+ snd_nm256_writel(chip, addr + 4, base + end_offset);
+ }
+}
+
+
+/* The actual rates supported by the card. */
+static unsigned int samplerates[8] = {
+ 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
+};
+static snd_pcm_hw_constraint_list_t constraints_rates = {
+ .count = ARRAY_SIZE(samplerates),
+ .list = samplerates,
+ .mask = 0,
+};
+
+/*
+ * return the index of the target rate
+ */
+static int
+snd_nm256_fixed_rate(unsigned int rate)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
+ if (rate == samplerates[i])
+ return i;
+ }
+ snd_BUG();
+ return 0;
+}
+
+/*
+ * set sample rate and format
+ */
+static void
+snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int rate_index = snd_nm256_fixed_rate(runtime->rate);
+ unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK;
+
+ s->shift = 0;
+ if (snd_pcm_format_width(runtime->format) == 16) {
+ ratebits |= NM_RATE_BITS_16;
+ s->shift++;
+ }
+ if (runtime->channels > 1) {
+ ratebits |= NM_RATE_STEREO;
+ s->shift++;
+ }
+
+ runtime->rate = samplerates[rate_index];
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */
+ snd_nm256_writeb(chip,
+ NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET,
+ ratebits);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */
+ snd_nm256_writeb(chip,
+ NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET,
+ ratebits);
+ break;
+ }
+}
+
+/*
+ * start / stop
+ */
+
+/* update the watermark (current period) */
+static void snd_nm256_pcm_mark(nm256_t *chip, nm256_stream_t *s, int reg)
+{
+ s->cur_period++;
+ s->cur_period %= s->periods;
+ snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size);
+}
+
+#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK)
+#define snd_nm256_capture_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK)
+
+static void
+snd_nm256_playback_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream)
+{
+ /* program buffer pointers */
+ snd_nm256_writel(chip, NM_PBUFFER_START, s->buf);
+ snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift));
+ snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf);
+ snd_nm256_playback_mark(chip, s);
+
+ /* Enable playback engine and interrupts. */
+ snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG,
+ NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN);
+ /* Enable both channels. */
+ snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0);
+}
+
+static void
+snd_nm256_capture_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream)
+{
+ /* program buffer pointers */
+ snd_nm256_writel(chip, NM_RBUFFER_START, s->buf);
+ snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size);
+ snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf);
+ snd_nm256_capture_mark(chip, s);
+
+ /* Enable playback engine and interrupts. */
+ snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG,
+ NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN);
+}
+
+/* Stop the play engine. */
+static void
+snd_nm256_playback_stop(nm256_t *chip)
+{
+ /* Shut off sound from both channels. */
+ snd_nm256_writew(chip, NM_AUDIO_MUTE_REG,
+ NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT);
+ /* Disable play engine. */
+ snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0);
+}
+
+static void
+snd_nm256_capture_stop(nm256_t *chip)
+{
+ /* Disable recording engine. */
+ snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0);
+}
+
+static int
+snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ nm256_t *chip = snd_pcm_substream_chip(substream);
+ nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+ int err = 0;
+
+ snd_assert(s != NULL, return -ENXIO);
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (! s->running) {
+ snd_nm256_playback_start(chip, s, substream);
+ s->running = 1;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (s->running) {
+ snd_nm256_playback_stop(chip);
+ s->running = 0;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock(&chip->reg_lock);
+ return err;
+}
+
+static int
+snd_nm256_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ nm256_t *chip = snd_pcm_substream_chip(substream);
+ nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+ int err = 0;
+
+ snd_assert(s != NULL, return -ENXIO);
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (! s->running) {
+ snd_nm256_capture_start(chip, s, substream);
+ s->running = 1;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (s->running) {
+ snd_nm256_capture_stop(chip);
+ s->running = 0;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock(&chip->reg_lock);
+ return err;
+}
+
+
+/*
+ * prepare playback/capture channel
+ */
+static int snd_nm256_pcm_prepare(snd_pcm_substream_t *substream)
+{
+ nm256_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+
+ snd_assert(s, return -ENXIO);
+ s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size);
+ s->period_size = frames_to_bytes(runtime, substream->runtime->period_size);
+ s->periods = substream->runtime->periods;
+ s->cur_period = 0;
+
+ spin_lock_irq(&chip->reg_lock);
+ s->running = 0;
+ snd_nm256_set_format(chip, s, substream);
+ spin_unlock_irq(&chip->reg_lock);
+
+ return 0;
+}
+
+
+/*
+ * get the current pointer
+ */
+static snd_pcm_uframes_t
+snd_nm256_playback_pointer(snd_pcm_substream_t * substream)
+{
+ nm256_t *chip = snd_pcm_substream_chip(substream);
+ nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+ unsigned long curp;
+
+ snd_assert(s, return 0);
+ curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf;
+ curp %= s->dma_size;
+ return bytes_to_frames(substream->runtime, curp);
+}
+
+static snd_pcm_uframes_t
+snd_nm256_capture_pointer(snd_pcm_substream_t * substream)
+{
+ nm256_t *chip = snd_pcm_substream_chip(substream);
+ nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+ unsigned long curp;
+
+ snd_assert(s != NULL, return 0);
+ curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf;
+ curp %= s->dma_size;
+ return bytes_to_frames(substream->runtime, curp);
+}
+
+/* Remapped I/O space can be accessible as pointer on i386 */
+/* This might be changed in the future */
+#ifndef __i386__
+/*
+ * silence / copy for playback
+ */
+static int
+snd_nm256_playback_silence(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+ count = frames_to_bytes(runtime, count);
+ pos = frames_to_bytes(runtime, pos);
+ memset_io(s->bufptr + pos, 0, count);
+ return 0;
+}
+
+static int
+snd_nm256_playback_copy(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *src,
+ snd_pcm_uframes_t count)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+ count = frames_to_bytes(runtime, count);
+ pos = frames_to_bytes(runtime, pos);
+ if (copy_from_user_toio(s->bufptr + pos, src, count))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * copy to user
+ */
+static int
+snd_nm256_capture_copy(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *dst,
+ snd_pcm_uframes_t count)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+ count = frames_to_bytes(runtime, count);
+ pos = frames_to_bytes(runtime, pos);
+ if (copy_to_user_fromio(dst, s->bufptr + pos, count))
+ return -EFAULT;
+ return 0;
+}
+
+#endif /* !__i386__ */
+
+
+/*
+ * update playback/capture watermarks
+ */
+
+/* spinlock held! */
+static void
+snd_nm256_playback_update(nm256_t *chip)
+{
+ nm256_stream_t *s;
+
+ s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (s->running && s->substream) {
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(s->substream);
+ spin_lock(&chip->reg_lock);
+ snd_nm256_playback_mark(chip, s);
+ }
+}
+
+/* spinlock held! */
+static void
+snd_nm256_capture_update(nm256_t *chip)
+{
+ nm256_stream_t *s;
+
+ s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (s->running && s->substream) {
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(s->substream);
+ spin_lock(&chip->reg_lock);
+ snd_nm256_capture_mark(chip, s);
+ }
+}
+
+/*
+ * hardware info
+ */
+static snd_pcm_hardware_t snd_nm256_playback =
+{
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ /*SNDRV_PCM_INFO_PAUSE |*/
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 256,
+ .period_bytes_max = 128 * 1024,
+};
+
+static snd_pcm_hardware_t snd_nm256_capture =
+{
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ /*SNDRV_PCM_INFO_PAUSE |*/
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 256,
+ .period_bytes_max = 128 * 1024,
+};
+
+
+/* set dma transfer size */
+static int snd_nm256_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params)
+{
+ /* area and addr are already set and unchanged */
+ substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
+ return 0;
+}
+
+/*
+ * open
+ */
+static void snd_nm256_setup_stream(nm256_t *chip, nm256_stream_t *s,
+ snd_pcm_substream_t *substream,
+ snd_pcm_hardware_t *hw_ptr)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ s->running = 0;
+ runtime->hw = *hw_ptr;
+ runtime->hw.buffer_bytes_max = s->bufsize;
+ runtime->hw.period_bytes_max = s->bufsize / 2;
+ runtime->dma_area = (void*) s->bufptr;
+ runtime->dma_addr = s->bufptr_addr;
+ runtime->dma_bytes = s->bufsize;
+ runtime->private_data = s;
+ s->substream = substream;
+
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+}
+
+static int
+snd_nm256_playback_open(snd_pcm_substream_t *substream)
+{
+ nm256_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK],
+ substream, &snd_nm256_playback);
+ return 0;
+}
+
+static int
+snd_nm256_capture_open(snd_pcm_substream_t *substream)
+{
+ nm256_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE],
+ substream, &snd_nm256_capture);
+ return 0;
+}
+
+/*
+ * close - we don't have to do special..
+ */
+static int
+snd_nm256_playback_close(snd_pcm_substream_t *substream)
+{
+ return 0;
+}
+
+
+static int
+snd_nm256_capture_close(snd_pcm_substream_t *substream)
+{
+ return 0;
+}
+
+/*
+ * create a pcm instance
+ */
+static snd_pcm_ops_t snd_nm256_playback_ops = {
+ .open = snd_nm256_playback_open,
+ .close = snd_nm256_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_nm256_pcm_hw_params,
+ .prepare = snd_nm256_pcm_prepare,
+ .trigger = snd_nm256_playback_trigger,
+ .pointer = snd_nm256_playback_pointer,
+#ifndef __i386__
+ .copy = snd_nm256_playback_copy,
+ .silence = snd_nm256_playback_silence,
+#endif
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static snd_pcm_ops_t snd_nm256_capture_ops = {
+ .open = snd_nm256_capture_open,
+ .close = snd_nm256_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_nm256_pcm_hw_params,
+ .prepare = snd_nm256_pcm_prepare,
+ .trigger = snd_nm256_capture_trigger,
+ .pointer = snd_nm256_capture_pointer,
+#ifndef __i386__
+ .copy = snd_nm256_capture_copy,
+#endif
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit
+snd_nm256_pcm(nm256_t *chip, int device)
+{
+ snd_pcm_t *pcm;
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ nm256_stream_t *s = &chip->streams[i];
+ s->bufptr = chip->buffer + (s->buf - chip->buffer_start);
+ s->bufptr_addr = chip->buffer_addr + (s->buf - chip->buffer_start);
+ }
+
+ err = snd_pcm_new(chip->card, chip->card->driver, device,
+ 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops);
+
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ chip->pcm = pcm;
+
+ return 0;
+}
+
+
+/*
+ * Initialize the hardware.
+ */
+static void
+snd_nm256_init_chip(nm256_t *chip)
+{
+ spin_lock_irq(&chip->reg_lock);
+ /* Reset everything. */
+ snd_nm256_writeb(chip, 0x0, 0x11);
+ snd_nm256_writew(chip, 0x214, 0);
+ /* stop sounds.. */
+ //snd_nm256_playback_stop(chip);
+ //snd_nm256_capture_stop(chip);
+ spin_unlock_irq(&chip->reg_lock);
+}
+
+
+inline static void
+snd_nm256_intr_check(nm256_t *chip)
+{
+ if (chip->badintrcount++ > 1000) {
+ /*
+ * I'm not sure if the best thing is to stop the card from
+ * playing or just release the interrupt (after all, we're in
+ * a bad situation, so doing fancy stuff may not be such a good
+ * idea).
+ *
+ * I worry about the card engine continuing to play noise
+ * over and over, however--that could become a very
+ * obnoxious problem. And we know that when this usually
+ * happens things are fairly safe, it just means the user's
+ * inserted a PCMCIA card and someone's spamming us with IRQ 9s.
+ */
+ if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
+ snd_nm256_playback_stop(chip);
+ if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
+ snd_nm256_capture_stop(chip);
+ chip->badintrcount = 0;
+ }
+}
+
+/*
+ * Handle a potential interrupt for the device referred to by DEV_ID.
+ *
+ * I don't like the cut-n-paste job here either between the two routines,
+ * but there are sufficient differences between the two interrupt handlers
+ * that parameterizing it isn't all that great either. (Could use a macro,
+ * I suppose...yucky bleah.)
+ */
+
+static irqreturn_t
+snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy)
+{
+ nm256_t *chip = dev_id;
+ u16 status;
+ u8 cbyte;
+
+ status = snd_nm256_readw(chip, NM_INT_REG);
+
+ /* Not ours. */
+ if (status == 0) {
+ snd_nm256_intr_check(chip);
+ return IRQ_NONE;
+ }
+
+ chip->badintrcount = 0;
+
+ /* Rather boring; check for individual interrupts and process them. */
+
+ spin_lock(&chip->reg_lock);
+ if (status & NM_PLAYBACK_INT) {
+ status &= ~NM_PLAYBACK_INT;
+ NM_ACK_INT(chip, NM_PLAYBACK_INT);
+ snd_nm256_playback_update(chip);
+ }
+
+ if (status & NM_RECORD_INT) {
+ status &= ~NM_RECORD_INT;
+ NM_ACK_INT(chip, NM_RECORD_INT);
+ snd_nm256_capture_update(chip);
+ }
+
+ if (status & NM_MISC_INT_1) {
+ status &= ~NM_MISC_INT_1;
+ NM_ACK_INT(chip, NM_MISC_INT_1);
+ snd_printd("NM256: Got misc interrupt #1\n");
+ snd_nm256_writew(chip, NM_INT_REG, 0x8000);
+ cbyte = snd_nm256_readb(chip, 0x400);
+ snd_nm256_writeb(chip, 0x400, cbyte | 2);
+ }
+
+ if (status & NM_MISC_INT_2) {
+ status &= ~NM_MISC_INT_2;
+ NM_ACK_INT(chip, NM_MISC_INT_2);
+ snd_printd("NM256: Got misc interrupt #2\n");
+ cbyte = snd_nm256_readb(chip, 0x400);
+ snd_nm256_writeb(chip, 0x400, cbyte & ~2);
+ }
+
+ /* Unknown interrupt. */
+ if (status) {
+ snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+ status);
+ /* Pray. */
+ NM_ACK_INT(chip, status);
+ }
+
+ spin_unlock(&chip->reg_lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Handle a potential interrupt for the device referred to by DEV_ID.
+ * This handler is for the 256ZX, and is very similar to the non-ZX
+ * routine.
+ */
+
+static irqreturn_t
+snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy)
+{
+ nm256_t *chip = dev_id;
+ u32 status;
+ u8 cbyte;
+
+ status = snd_nm256_readl(chip, NM_INT_REG);
+
+ /* Not ours. */
+ if (status == 0) {
+ snd_nm256_intr_check(chip);
+ return IRQ_NONE;
+ }
+
+ chip->badintrcount = 0;
+
+ /* Rather boring; check for individual interrupts and process them. */
+
+ spin_lock(&chip->reg_lock);
+ if (status & NM2_PLAYBACK_INT) {
+ status &= ~NM2_PLAYBACK_INT;
+ NM2_ACK_INT(chip, NM2_PLAYBACK_INT);
+ snd_nm256_playback_update(chip);
+ }
+
+ if (status & NM2_RECORD_INT) {
+ status &= ~NM2_RECORD_INT;
+ NM2_ACK_INT(chip, NM2_RECORD_INT);
+ snd_nm256_capture_update(chip);
+ }
+
+ if (status & NM2_MISC_INT_1) {
+ status &= ~NM2_MISC_INT_1;
+ NM2_ACK_INT(chip, NM2_MISC_INT_1);
+ snd_printd("NM256: Got misc interrupt #1\n");
+ cbyte = snd_nm256_readb(chip, 0x400);
+ snd_nm256_writeb(chip, 0x400, cbyte | 2);
+ }
+
+ if (status & NM2_MISC_INT_2) {
+ status &= ~NM2_MISC_INT_2;
+ NM2_ACK_INT(chip, NM2_MISC_INT_2);
+ snd_printd("NM256: Got misc interrupt #2\n");
+ cbyte = snd_nm256_readb(chip, 0x400);
+ snd_nm256_writeb(chip, 0x400, cbyte & ~2);
+ }
+
+ /* Unknown interrupt. */
+ if (status) {
+ snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+ status);
+ /* Pray. */
+ NM2_ACK_INT(chip, status);
+ }
+
+ spin_unlock(&chip->reg_lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * AC97 interface
+ */
+
+/*
+ * Waits for the mixer to become ready to be written; returns a zero value
+ * if it timed out.
+ */
+static int
+snd_nm256_ac97_ready(nm256_t *chip)
+{
+ int timeout = 10;
+ u32 testaddr;
+ u16 testb;
+
+ testaddr = chip->mixer_status_offset;
+ testb = chip->mixer_status_mask;
+
+ /*
+ * Loop around waiting for the mixer to become ready.
+ */
+ while (timeout-- > 0) {
+ if ((snd_nm256_readw(chip, testaddr) & testb) == 0)
+ return 1;
+ udelay(100);
+ }
+ return 0;
+}
+
+/*
+ */
+static unsigned short
+snd_nm256_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+ nm256_t *chip = ac97->private_data;
+ int res;
+
+ if (reg >= 128)
+ return 0;
+
+ if (! snd_nm256_ac97_ready(chip))
+ return 0;
+ res = snd_nm256_readw(chip, chip->mixer_base + reg);
+ /* Magic delay. Bleah yucky. */
+ msleep(1);
+ return res;
+}
+
+/*
+ */
+static void
+snd_nm256_ac97_write(ac97_t *ac97,
+ unsigned short reg, unsigned short val)
+{
+ nm256_t *chip = ac97->private_data;
+ int tries = 2;
+ u32 base;
+
+ base = chip->mixer_base;
+
+ snd_nm256_ac97_ready(chip);
+
+ /* Wait for the write to take, too. */
+ while (tries-- > 0) {
+ snd_nm256_writew(chip, base + reg, val);
+ msleep(1); /* a little delay here seems better.. */
+ if (snd_nm256_ac97_ready(chip))
+ return;
+ }
+ snd_printd("nm256: ac97 codec not ready..\n");
+}
+
+/* initialize the ac97 into a known state */
+static void
+snd_nm256_ac97_reset(ac97_t *ac97)
+{
+ nm256_t *chip = ac97->private_data;
+
+ /* Reset the mixer. 'Tis magic! */
+ snd_nm256_writeb(chip, 0x6c0, 1);
+ if (! chip->reset_workaround) {
+ /* Dell latitude LS will lock up by this */
+ snd_nm256_writeb(chip, 0x6cc, 0x87);
+ }
+ snd_nm256_writeb(chip, 0x6cc, 0x80);
+ snd_nm256_writeb(chip, 0x6cc, 0x0);
+}
+
+/* create an ac97 mixer interface */
+static int __devinit
+snd_nm256_mixer(nm256_t *chip)
+{
+ ac97_bus_t *pbus;
+ ac97_template_t ac97;
+ int i, err;
+ static ac97_bus_ops_t ops = {
+ .reset = snd_nm256_ac97_reset,
+ .write = snd_nm256_ac97_write,
+ .read = snd_nm256_ac97_read,
+ };
+ /* looks like nm256 hangs up when unexpected registers are touched... */
+ static int mixer_regs[] = {
+ AC97_MASTER, AC97_HEADPHONE, AC97_MASTER_MONO,
+ AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD,
+ AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL,
+ AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL,
+ AC97_EXTENDED_ID,
+ AC97_VENDOR_ID1, AC97_VENDOR_ID2,
+ -1
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ return err;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */
+ ac97.limited_regs = 1;
+ for (i = 0; mixer_regs[i] >= 0; i++)
+ set_bit(mixer_regs[i], ac97.reg_accessed);
+ ac97.private_data = chip;
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+ if (err < 0)
+ return err;
+ if (! (chip->ac97->id & (0xf0000000))) {
+ /* looks like an invalid id */
+ sprintf(chip->card->mixername, "%s AC97", chip->card->driver);
+ }
+ return 0;
+}
+
+/*
+ * See if the signature left by the NM256 BIOS is intact; if so, we use
+ * the associated address as the end of our audio buffer in the video
+ * RAM.
+ */
+
+static int __devinit
+snd_nm256_peek_for_sig(nm256_t *chip)
+{
+ /* The signature is located 1K below the end of video RAM. */
+ void __iomem *temp;
+ /* Default buffer end is 5120 bytes below the top of RAM. */
+ unsigned long pointer_found = chip->buffer_end - 0x1400;
+ u32 sig;
+
+ temp = ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16);
+ if (temp == NULL) {
+ snd_printk("Unable to scan for card signature in video RAM\n");
+ return -EBUSY;
+ }
+
+ sig = readl(temp);
+ if ((sig & NM_SIG_MASK) == NM_SIGNATURE) {
+ u32 pointer = readl(temp + 4);
+
+ /*
+ * If it's obviously invalid, don't use it
+ */
+ if (pointer == 0xffffffff ||
+ pointer < chip->buffer_size ||
+ pointer > chip->buffer_end) {
+ snd_printk("invalid signature found: 0x%x\n", pointer);
+ iounmap(temp);
+ return -ENODEV;
+ } else {
+ pointer_found = pointer;
+ printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n", pointer);
+ }
+ }
+
+ iounmap(temp);
+ chip->buffer_end = pointer_found;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * APM event handler, so the card is properly reinitialized after a power
+ * event.
+ */
+static int nm256_suspend(snd_card_t *card, pm_message_t state)
+{
+ nm256_t *chip = card->pm_private_data;
+
+ snd_pcm_suspend_all(chip->pcm);
+ snd_ac97_suspend(chip->ac97);
+ chip->coeffs_current = 0;
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int nm256_resume(snd_card_t *card)
+{
+ nm256_t *chip = card->pm_private_data;
+
+ /* Perform a full reset on the hardware */
+ pci_enable_device(chip->pci);
+ snd_nm256_init_chip(chip);
+
+ /* restore ac97 */
+ snd_ac97_resume(chip->ac97);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_nm256_free(nm256_t *chip)
+{
+ if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
+ snd_nm256_playback_stop(chip);
+ if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
+ snd_nm256_capture_stop(chip);
+
+ if (chip->irq >= 0)
+ synchronize_irq(chip->irq);
+
+ if (chip->cport)
+ iounmap(chip->cport);
+ if (chip->buffer)
+ iounmap(chip->buffer);
+ if (chip->res_cport) {
+ release_resource(chip->res_cport);
+ kfree_nocheck(chip->res_cport);
+ }
+ if (chip->res_buffer) {
+ release_resource(chip->res_buffer);
+ kfree_nocheck(chip->res_buffer);
+ }
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void*)chip);
+
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_nm256_dev_free(snd_device_t *device)
+{
+ nm256_t *chip = device->device_data;
+ return snd_nm256_free(chip);
+}
+
+static int __devinit
+snd_nm256_create(snd_card_t *card, struct pci_dev *pci,
+ int play_bufsize, int capt_bufsize,
+ int force_load,
+ u32 buffertop,
+ int usecache,
+ nm256_t **chip_ret)
+{
+ nm256_t *chip;
+ int err, pval;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_nm256_dev_free,
+ };
+ u32 addr;
+
+ *chip_ret = NULL;
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ chip->card = card;
+ chip->pci = pci;
+ chip->use_cache = usecache;
+ spin_lock_init(&chip->reg_lock);
+ chip->irq = -1;
+
+ chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize;
+ chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize;
+
+ /*
+ * The NM256 has two memory ports. The first port is nothing
+ * more than a chunk of video RAM, which is used as the I/O ring
+ * buffer. The second port has the actual juicy stuff (like the
+ * mixer and the playback engine control registers).
+ */
+
+ chip->buffer_addr = pci_resource_start(pci, 0);
+ chip->cport_addr = pci_resource_start(pci, 1);
+
+ /* Init the memory port info. */
+ /* remap control port (#2) */
+ chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE,
+ card->driver);
+ if (chip->res_cport == NULL) {
+ snd_printk("memory region 0x%lx (size 0x%x) busy\n",
+ chip->cport_addr, NM_PORT2_SIZE);
+ err = -EBUSY;
+ goto __error;
+ }
+ chip->cport = ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE);
+ if (chip->cport == NULL) {
+ snd_printk("unable to map control port %lx\n", chip->cport_addr);
+ err = -ENOMEM;
+ goto __error;
+ }
+
+ if (!strcmp(card->driver, "NM256AV")) {
+ /* Ok, try to see if this is a non-AC97 version of the hardware. */
+ pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE);
+ if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
+ if (! force_load) {
+ printk(KERN_ERR "nm256: no ac97 is found!\n");
+ printk(KERN_ERR " force the driver to load by passing in the module parameter\n");
+ printk(KERN_ERR " force_ac97=1\n");
+ printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n");
+ err = -ENXIO;
+ goto __error;
+ }
+ }
+ chip->buffer_end = 2560 * 1024;
+ chip->interrupt = snd_nm256_interrupt;
+ chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET;
+ chip->mixer_status_mask = NM_MIXER_READY_MASK;
+ } else {
+ /* Not sure if there is any relevant detect for the ZX or not. */
+ if (snd_nm256_readb(chip, 0xa0b) != 0)
+ chip->buffer_end = 6144 * 1024;
+ else
+ chip->buffer_end = 4096 * 1024;
+
+ chip->interrupt = snd_nm256_interrupt_zx;
+ chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET;
+ chip->mixer_status_mask = NM2_MIXER_READY_MASK;
+ }
+
+ chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
+ if (chip->use_cache)
+ chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4;
+ else
+ chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE;
+
+ if (buffertop >= chip->buffer_size && buffertop < chip->buffer_end)
+ chip->buffer_end = buffertop;
+ else {
+ /* get buffer end pointer from signature */
+ if ((err = snd_nm256_peek_for_sig(chip)) < 0)
+ goto __error;
+ }
+
+ chip->buffer_start = chip->buffer_end - chip->buffer_size;
+ chip->buffer_addr += chip->buffer_start;
+
+ printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n",
+ chip->buffer_start, chip->buffer_end);
+
+ chip->res_buffer = request_mem_region(chip->buffer_addr,
+ chip->buffer_size,
+ card->driver);
+ if (chip->res_buffer == NULL) {
+ snd_printk("nm256: buffer 0x%lx (size 0x%x) busy\n",
+ chip->buffer_addr, chip->buffer_size);
+ err = -EBUSY;
+ goto __error;
+ }
+ chip->buffer = ioremap_nocache(chip->buffer_addr, chip->buffer_size);
+ if (chip->buffer == NULL) {
+ err = -ENOMEM;
+ snd_printk("unable to map ring buffer at %lx\n", chip->buffer_addr);
+ goto __error;
+ }
+
+ /* set offsets */
+ addr = chip->buffer_start;
+ chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr;
+ addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize;
+ chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr;
+ addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
+ if (chip->use_cache) {
+ chip->all_coeff_buf = addr;
+ } else {
+ chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr;
+ addr += NM_MAX_PLAYBACK_COEF_SIZE;
+ chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr;
+ }
+
+ /* acquire interrupt */
+ if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ,
+ card->driver, (void*)chip)) {
+ err = -EBUSY;
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ goto __error;
+ }
+ chip->irq = pci->irq;
+
+ /* Fixed setting. */
+ chip->mixer_base = NM_MIXER_OFFSET;
+
+ chip->coeffs_current = 0;
+
+ snd_nm256_init_chip(chip);
+
+ // pci_set_master(pci); /* needed? */
+
+ snd_card_set_pm_callback(card, nm256_suspend, nm256_resume, chip);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+ goto __error;
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *chip_ret = chip;
+ return 0;
+
+__error:
+ snd_nm256_free(chip);
+ return err;
+}
+
+
+struct nm256_quirk {
+ unsigned short vendor;
+ unsigned short device;
+ int type;
+};
+
+enum { NM_BLACKLISTED, NM_RESET_WORKAROUND };
+
+static struct nm256_quirk nm256_quirks[] __devinitdata = {
+ /* HP omnibook 4150 has cs4232 codec internally */
+ { .vendor = 0x103c, .device = 0x0007, .type = NM_BLACKLISTED },
+ /* Sony PCG-F305 */
+ { .vendor = 0x104d, .device = 0x8041, .type = NM_RESET_WORKAROUND },
+ /* Dell Latitude LS */
+ { .vendor = 0x1028, .device = 0x0080, .type = NM_RESET_WORKAROUND },
+ { } /* terminator */
+};
+
+
+static int __devinit snd_nm256_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ nm256_t *chip;
+ int err;
+ unsigned int xbuffer_top;
+ struct nm256_quirk *q;
+ u16 subsystem_vendor, subsystem_device;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+
+ for (q = nm256_quirks; q->vendor; q++) {
+ if (q->vendor == subsystem_vendor && q->device == subsystem_device) {
+ switch (q->type) {
+ case NM_BLACKLISTED:
+ printk(KERN_INFO "nm256: The device is blacklisted. Loading stopped\n");
+ return -ENODEV;
+ case NM_RESET_WORKAROUND:
+ reset_workaround[dev] = 1;
+ break;
+ }
+ }
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ switch (pci->device) {
+ case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO:
+ strcpy(card->driver, "NM256AV");
+ break;
+ case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO:
+ strcpy(card->driver, "NM256ZX");
+ break;
+ case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO:
+ strcpy(card->driver, "NM256XL+");
+ break;
+ default:
+ snd_printk("invalid device id 0x%x\n", pci->device);
+ snd_card_free(card);
+ return -EINVAL;
+ }
+
+ if (vaio_hack[dev])
+ xbuffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */
+ else
+ xbuffer_top = buffer_top[dev];
+
+ if (playback_bufsize[dev] < 4)
+ playback_bufsize[dev] = 4;
+ if (playback_bufsize[dev] > 128)
+ playback_bufsize[dev] = 128;
+ if (capture_bufsize[dev] < 4)
+ capture_bufsize[dev] = 4;
+ if (capture_bufsize[dev] > 128)
+ capture_bufsize[dev] = 128;
+ if ((err = snd_nm256_create(card, pci,
+ playback_bufsize[dev] * 1024, /* in bytes */
+ capture_bufsize[dev] * 1024, /* in bytes */
+ force_ac97[dev],
+ xbuffer_top,
+ use_cache[dev],
+ &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if (reset_workaround[dev]) {
+ snd_printdd(KERN_INFO "nm256: reset_workaround activated\n");
+ chip->reset_workaround = 1;
+ }
+
+ if ((err = snd_nm256_pcm(chip, 0)) < 0 ||
+ (err = snd_nm256_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ sprintf(card->shortname, "NeoMagic %s", card->driver);
+ sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d",
+ card->shortname,
+ chip->buffer_addr, chip->cport_addr, chip->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_nm256_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+
+static struct pci_driver driver = {
+ .name = "NeoMagic 256",
+ .id_table = snd_nm256_ids,
+ .probe = snd_nm256_probe,
+ .remove = __devexit_p(snd_nm256_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+
+static int __init alsa_card_nm256_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_nm256_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_nm256_init)
+module_exit(alsa_card_nm256_exit)
diff --git a/sound/pci/nm256/nm256_coef.c b/sound/pci/nm256/nm256_coef.c
new file mode 100644
index 00000000000..747d5d6ccfa
--- /dev/null
+++ b/sound/pci/nm256/nm256_coef.c
@@ -0,0 +1,4607 @@
+#define NM_TOTAL_COEFF_COUNT 0x3158
+
+static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = {
+ 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21,
+ 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01,
+ 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD,
+ 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06,
+ 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1,
+ 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF,
+ 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E,
+ 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC,
+ 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05,
+ 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+ 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+ 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+ 0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9,
+ 0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01,
+ 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84,
+ 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+ 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+ 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+ 0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C,
+ 0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00,
+ 0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5,
+ 0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33,
+ 0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24,
+ 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00,
+ 0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1,
+ 0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00,
+ 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38,
+ 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA,
+ 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD,
+ 0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38,
+ 0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00,
+ 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F,
+ 0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05,
+ 0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06,
+ 0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32,
+ 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+ 0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93,
+ 0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC,
+ 0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+ 0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA,
+ 0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9,
+ 0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+ 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+ 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+ 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68,
+ 0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32,
+ 0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51,
+ 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00,
+ 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76,
+ 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF,
+ 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53,
+ 0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04,
+ 0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01,
+ 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20,
+ 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF,
+ 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43,
+ 0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1,
+ 0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+ 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD,
+ 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8,
+ 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5,
+ 0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04,
+ 0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04,
+ 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D,
+ 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+ 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE,
+ 0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD,
+ 0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19,
+ 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2,
+ 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD,
+ 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF,
+ 0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D,
+ 0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01,
+ 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70,
+ 0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07,
+ 0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9,
+ 0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+ 0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66,
+ 0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF,
+ 0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F,
+ 0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F,
+ 0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C,
+ 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00,
+ 0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF,
+ 0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00,
+ 0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18,
+ 0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB,
+ 0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC,
+ 0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98,
+ 0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00,
+ 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A,
+ 0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06,
+ 0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01,
+ 0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05,
+ 0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A,
+ 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+ 0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76,
+ 0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD,
+ 0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+ 0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8,
+ 0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40,
+ 0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+ 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+ 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+ 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85,
+ 0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36,
+ 0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42,
+ 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+ 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D,
+ 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF,
+ 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39,
+ 0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48,
+ 0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95,
+ 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01,
+ 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F,
+ 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF,
+ 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F,
+ 0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1,
+ 0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD,
+ 0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE,
+ 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A,
+ 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+ 0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A,
+ 0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03,
+ 0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05,
+ 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E,
+ 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+ 0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01,
+ 0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC,
+ 0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B,
+ 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2,
+ 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6,
+ 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF,
+ 0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48,
+ 0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01,
+ 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+ 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+ 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+ 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+ 0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11,
+ 0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF,
+ 0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B,
+ 0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A,
+ 0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32,
+ 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF,
+ 0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C,
+ 0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE,
+ 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01,
+ 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C,
+ 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC,
+ 0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1,
+ 0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00,
+ 0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16,
+ 0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06,
+ 0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04,
+ 0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04,
+ 0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B,
+ 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00,
+ 0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3,
+ 0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD,
+ 0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+ 0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7,
+ 0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3,
+ 0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+ 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+ 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+ 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9,
+ 0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A,
+ 0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39,
+ 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00,
+ 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD,
+ 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF,
+ 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A,
+ 0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48,
+ 0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB,
+ 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01,
+ 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91,
+ 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00,
+ 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8,
+ 0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1,
+ 0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD,
+ 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF,
+ 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA,
+ 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD,
+ 0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD,
+ 0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE,
+ 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06,
+ 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF,
+ 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+ 0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94,
+ 0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC,
+ 0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB,
+ 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4,
+ 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2,
+ 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF,
+ 0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A,
+ 0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01,
+ 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D,
+ 0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10,
+ 0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68,
+ 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+ 0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B,
+ 0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF,
+ 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28,
+ 0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25,
+ 0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35,
+ 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF,
+ 0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC,
+ 0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90,
+ 0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE,
+ 0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16,
+ 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC,
+ 0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE,
+ 0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00,
+ 0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0,
+ 0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07,
+ 0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08,
+ 0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03,
+ 0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91,
+ 0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+ 0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73,
+ 0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE,
+ 0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33,
+ 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5,
+ 0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20,
+ 0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+ 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+ 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+ 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3,
+ 0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E,
+ 0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36,
+ 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00,
+ 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47,
+ 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF,
+ 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96,
+ 0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46,
+ 0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1,
+ 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01,
+ 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74,
+ 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B,
+ 0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2,
+ 0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+ 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF,
+ 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D,
+ 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A,
+ 0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE,
+ 0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE,
+ 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06,
+ 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23,
+ 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF,
+ 0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81,
+ 0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC,
+ 0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9,
+ 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5,
+ 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2,
+ 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF,
+ 0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED,
+ 0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01,
+ 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+ 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+ 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+ 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF,
+ 0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25,
+ 0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF,
+ 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9,
+ 0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20,
+ 0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF,
+ 0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2,
+ 0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B,
+ 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB,
+ 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10,
+ 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC,
+ 0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA,
+ 0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56,
+ 0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07,
+ 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+ 0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02,
+ 0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB,
+ 0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E,
+ 0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01,
+ 0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36,
+ 0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4,
+ 0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85,
+ 0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+ 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+ 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+ 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02,
+ 0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41,
+ 0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39,
+ 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00,
+ 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07,
+ 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF,
+ 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA,
+ 0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44,
+ 0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6,
+ 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01,
+ 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5,
+ 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00,
+ 0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A,
+ 0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3,
+ 0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00,
+ 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A,
+ 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C,
+ 0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00,
+ 0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD,
+ 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06,
+ 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D,
+ 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+ 0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0,
+ 0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC,
+ 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5,
+ 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8,
+ 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7,
+ 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00,
+ 0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64,
+ 0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE,
+ 0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39,
+ 0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A,
+ 0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE,
+ 0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF,
+ 0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD,
+ 0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF,
+ 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D,
+ 0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B,
+ 0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36,
+ 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE,
+ 0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54,
+ 0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3,
+ 0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8,
+ 0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C,
+ 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC,
+ 0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63,
+ 0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+ 0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8,
+ 0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06,
+ 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+ 0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00,
+ 0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34,
+ 0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C,
+ 0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00,
+ 0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+ 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3,
+ 0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0,
+ 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+ 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+ 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+ 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36,
+ 0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44,
+ 0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41,
+ 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF,
+ 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA,
+ 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00,
+ 0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4,
+ 0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42,
+ 0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA,
+ 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01,
+ 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E,
+ 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00,
+ 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31,
+ 0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4,
+ 0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+ 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01,
+ 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86,
+ 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D,
+ 0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01,
+ 0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD,
+ 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07,
+ 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11,
+ 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88,
+ 0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC,
+ 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF,
+ 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA,
+ 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92,
+ 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+ 0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA,
+ 0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE,
+ 0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+ 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+ 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+ 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF,
+ 0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5,
+ 0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF,
+ 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17,
+ 0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16,
+ 0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01,
+ 0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C,
+ 0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF,
+ 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C,
+ 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6,
+ 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07,
+ 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC,
+ 0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8,
+ 0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF,
+ 0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8,
+ 0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06,
+ 0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16,
+ 0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF,
+ 0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99,
+ 0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7,
+ 0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00,
+ 0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+ 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2,
+ 0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F,
+ 0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00,
+ 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+ 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+ 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D,
+ 0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46,
+ 0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E,
+ 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF,
+ 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57,
+ 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00,
+ 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3,
+ 0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F,
+ 0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC,
+ 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01,
+ 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A,
+ 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00,
+ 0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF,
+ 0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5,
+ 0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+ 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02,
+ 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9,
+ 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8,
+ 0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03,
+ 0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD,
+ 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07,
+ 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0,
+ 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+ 0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0,
+ 0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC,
+ 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9,
+ 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD,
+ 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66,
+ 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+ 0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36,
+ 0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF,
+ 0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39,
+ 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24,
+ 0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6,
+ 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF,
+ 0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11,
+ 0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF,
+ 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9,
+ 0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11,
+ 0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01,
+ 0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74,
+ 0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF,
+ 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57,
+ 0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4,
+ 0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04,
+ 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC,
+ 0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A,
+ 0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF,
+ 0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8,
+ 0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06,
+ 0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C,
+ 0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD,
+ 0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08,
+ 0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A,
+ 0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF,
+ 0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32,
+ 0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1,
+ 0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70,
+ 0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00,
+ 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+ 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+ 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5,
+ 0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47,
+ 0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E,
+ 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF,
+ 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26,
+ 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00,
+ 0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6,
+ 0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B,
+ 0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01,
+ 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E,
+ 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00,
+ 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0,
+ 0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7,
+ 0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD,
+ 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F,
+ 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00,
+ 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66,
+ 0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04,
+ 0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD,
+ 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06,
+ 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A,
+ 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00,
+ 0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48,
+ 0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC,
+ 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93,
+ 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01,
+ 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34,
+ 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+ 0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA,
+ 0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF,
+ 0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+ 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+ 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+ 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF,
+ 0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10,
+ 0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+ 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35,
+ 0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C,
+ 0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01,
+ 0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30,
+ 0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF,
+ 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38,
+ 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3,
+ 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01,
+ 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC,
+ 0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A,
+ 0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+ 0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B,
+ 0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05,
+ 0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03,
+ 0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04,
+ 0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF,
+ 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1,
+ 0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE,
+ 0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B,
+ 0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1,
+ 0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3,
+ 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF,
+ 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+ 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+ 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE,
+ 0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48,
+ 0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71,
+ 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF,
+ 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C,
+ 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00,
+ 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB,
+ 0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37,
+ 0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01,
+ 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2,
+ 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+ 0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70,
+ 0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8,
+ 0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD,
+ 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9,
+ 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00,
+ 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0,
+ 0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05,
+ 0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE,
+ 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06,
+ 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40,
+ 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00,
+ 0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53,
+ 0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC,
+ 0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20,
+ 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC,
+ 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D,
+ 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+ 0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6,
+ 0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00,
+ 0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53,
+ 0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E,
+ 0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66,
+ 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF,
+ 0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06,
+ 0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF,
+ 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF,
+ 0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08,
+ 0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01,
+ 0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57,
+ 0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF,
+ 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32,
+ 0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2,
+ 0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD,
+ 0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B,
+ 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17,
+ 0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04,
+ 0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03,
+ 0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24,
+ 0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+ 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8,
+ 0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD,
+ 0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23,
+ 0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1,
+ 0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8,
+ 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF,
+ 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+ 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+ 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83,
+ 0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04,
+ 0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C,
+ 0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+ 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02,
+ 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00,
+ 0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3,
+ 0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33,
+ 0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25,
+ 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00,
+ 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB,
+ 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00,
+ 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B,
+ 0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA,
+ 0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD,
+ 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58,
+ 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00,
+ 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14,
+ 0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06,
+ 0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06,
+ 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31,
+ 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+ 0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0,
+ 0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC,
+ 0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+ 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA,
+ 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4,
+ 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+ 0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA,
+ 0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00,
+ 0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+ 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+ 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+ 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00,
+ 0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06,
+ 0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF,
+ 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49,
+ 0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04,
+ 0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01,
+ 0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE,
+ 0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF,
+ 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46,
+ 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1,
+ 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE,
+ 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD,
+ 0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0,
+ 0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92,
+ 0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04,
+ 0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04,
+ 0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B,
+ 0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+ 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA,
+ 0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD,
+ 0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18,
+ 0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2,
+ 0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE,
+ 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF,
+ 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+ 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+ 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E,
+ 0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08,
+ 0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4,
+ 0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+ 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F,
+ 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF,
+ 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B,
+ 0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E,
+ 0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D,
+ 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00,
+ 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E,
+ 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+ 0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C,
+ 0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC,
+ 0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC,
+ 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6,
+ 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00,
+ 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B,
+ 0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06,
+ 0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01,
+ 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05,
+ 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C,
+ 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+ 0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC,
+ 0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD,
+ 0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+ 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8,
+ 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A,
+ 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+ 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2,
+ 0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01,
+ 0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88,
+ 0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37,
+ 0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41,
+ 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00,
+ 0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20,
+ 0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF,
+ 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D,
+ 0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48,
+ 0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96,
+ 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01,
+ 0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7,
+ 0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF,
+ 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75,
+ 0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1,
+ 0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD,
+ 0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE,
+ 0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F,
+ 0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+ 0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5,
+ 0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03,
+ 0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05,
+ 0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D,
+ 0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+ 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3,
+ 0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC,
+ 0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+ 0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2,
+ 0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6,
+ 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+ 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+ 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+ 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B,
+ 0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C,
+ 0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B,
+ 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+ 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0,
+ 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF,
+ 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66,
+ 0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29,
+ 0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32,
+ 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF,
+ 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E,
+ 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4,
+ 0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01,
+ 0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B,
+ 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC,
+ 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB,
+ 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00,
+ 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12,
+ 0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06,
+ 0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04,
+ 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04,
+ 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F,
+ 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+ 0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12,
+ 0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD,
+ 0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+ 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7,
+ 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD,
+ 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+ 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0,
+ 0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01,
+ 0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+ 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+ 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+ 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00,
+ 0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64,
+ 0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF,
+ 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E,
+ 0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47,
+ 0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD,
+ 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+ 0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73,
+ 0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00,
+ 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF,
+ 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1,
+ 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD,
+ 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF,
+ 0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC,
+ 0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1,
+ 0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD,
+ 0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE,
+ 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06,
+ 0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF,
+ 0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+ 0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E,
+ 0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC,
+ 0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9,
+ 0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4,
+ 0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1,
+ 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+ 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+ 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+ 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C,
+ 0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11,
+ 0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63,
+ 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+ 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2,
+ 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF,
+ 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22,
+ 0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25,
+ 0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF,
+ 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20,
+ 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87,
+ 0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE,
+ 0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15,
+ 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC,
+ 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3,
+ 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00,
+ 0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7,
+ 0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07,
+ 0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+ 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03,
+ 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97,
+ 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+ 0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA,
+ 0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02,
+ 0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+ 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5,
+ 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A,
+ 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+ 0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE,
+ 0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01,
+ 0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7,
+ 0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E,
+ 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36,
+ 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00,
+ 0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4,
+ 0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF,
+ 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99,
+ 0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46,
+ 0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3,
+ 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01,
+ 0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60,
+ 0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25,
+ 0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2,
+ 0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD,
+ 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF,
+ 0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D,
+ 0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD,
+ 0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE,
+ 0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE,
+ 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06,
+ 0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15,
+ 0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+ 0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84,
+ 0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC,
+ 0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7,
+ 0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6,
+ 0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0,
+ 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+ 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+ 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+ 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+ 0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15,
+ 0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C,
+ 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF,
+ 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3,
+ 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF,
+ 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1,
+ 0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F,
+ 0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF,
+ 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8,
+ 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23,
+ 0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA,
+ 0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10,
+ 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC,
+ 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB,
+ 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49,
+ 0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07,
+ 0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+ 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01,
+ 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2,
+ 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07,
+ 0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01,
+ 0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+ 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4,
+ 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E,
+ 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+ 0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B,
+ 0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01,
+ 0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+ 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+ 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+ 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00,
+ 0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB,
+ 0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF,
+ 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC,
+ 0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44,
+ 0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8,
+ 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01,
+ 0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB,
+ 0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00,
+ 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6,
+ 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3,
+ 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00,
+ 0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48,
+ 0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E,
+ 0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00,
+ 0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD,
+ 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06,
+ 0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81,
+ 0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+ 0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC,
+ 0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC,
+ 0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3,
+ 0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8,
+ 0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4,
+ 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+ 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+ 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+ 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+ 0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A,
+ 0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9,
+ 0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF,
+ 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44,
+ 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF,
+ 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74,
+ 0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A,
+ 0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36,
+ 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE,
+ 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B,
+ 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC,
+ 0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8,
+ 0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B,
+ 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC,
+ 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F,
+ 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF,
+ 0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96,
+ 0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06,
+ 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+ 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00,
+ 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C,
+ 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56,
+ 0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00,
+ 0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+ 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3,
+ 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8,
+ 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00,
+ 0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6,
+ 0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+ 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A,
+ 0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44,
+ 0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42,
+ 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+ 0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1,
+ 0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00,
+ 0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6,
+ 0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41,
+ 0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC,
+ 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01,
+ 0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D,
+ 0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00,
+ 0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F,
+ 0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4,
+ 0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+ 0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01,
+ 0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73,
+ 0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D,
+ 0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01,
+ 0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD,
+ 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07,
+ 0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08,
+ 0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E,
+ 0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC,
+ 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD,
+ 0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA,
+ 0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F,
+ 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+ 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+ 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+ 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+ 0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F,
+ 0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB,
+ 0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF,
+ 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89,
+ 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF,
+ 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D,
+ 0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15,
+ 0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01,
+ 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6,
+ 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF,
+ 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86,
+ 0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6,
+ 0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07,
+ 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC,
+ 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0,
+ 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF,
+ 0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2,
+ 0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06,
+ 0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+ 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE,
+ 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3,
+ 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3,
+ 0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF,
+ 0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+ 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2,
+ 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35,
+ 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00,
+ 0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD,
+ 0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01,
+ 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+ 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+ 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+ 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF,
+ 0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41,
+ 0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00,
+ 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+ 0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E,
+ 0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE,
+ 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01,
+ 0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1,
+ 0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00,
+ 0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF,
+ 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5,
+ 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE,
+ 0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02,
+ 0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95,
+ 0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00,
+ 0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6,
+ 0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03,
+ 0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD,
+ 0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07,
+ 0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9,
+ 0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00,
+ 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF,
+ 0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC,
+ 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7,
+ 0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE,
+ 0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62,
+ 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+ 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+ 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+ 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A,
+ 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25,
+ 0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2,
+ 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF,
+ 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0,
+ 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF,
+ 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F,
+ 0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11,
+ 0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01,
+ 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26,
+ 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF,
+ 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53,
+ 0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4,
+ 0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04,
+ 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC,
+ 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D,
+ 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+ 0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D,
+ 0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06,
+ 0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+ 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD,
+ 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12,
+ 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29,
+ 0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF,
+ 0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+ 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1,
+ 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75,
+ 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00,
+ 0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0,
+ 0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01,
+ 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA,
+ 0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47,
+ 0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60,
+ 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF,
+ 0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02,
+ 0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00,
+ 0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5,
+ 0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B,
+ 0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01,
+ 0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C,
+ 0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00,
+ 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2,
+ 0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7,
+ 0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD,
+ 0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64,
+ 0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+ 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70,
+ 0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04,
+ 0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD,
+ 0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06,
+ 0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65,
+ 0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+ 0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71,
+ 0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC,
+ 0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91,
+ 0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01,
+ 0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F,
+ 0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+ 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+ 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+ 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44,
+ 0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29,
+ 0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80,
+ 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF,
+ 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E,
+ 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+ 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B,
+ 0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C,
+ 0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01,
+ 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB,
+ 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF,
+ 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37,
+ 0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2,
+ 0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01,
+ 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC,
+ 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29,
+ 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+ 0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D,
+ 0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05,
+ 0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03,
+ 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0,
+ 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+ 0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3,
+ 0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE,
+ 0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B,
+ 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1,
+ 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7,
+ 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF,
+ 0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF,
+ 0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01,
+ 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+ 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+ 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+ 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF,
+ 0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E,
+ 0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00,
+ 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA,
+ 0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37,
+ 0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B,
+ 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01,
+ 0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7,
+ 0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+ 0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83,
+ 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8,
+ 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD,
+ 0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC,
+ 0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00,
+ 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8,
+ 0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05,
+ 0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE,
+ 0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06,
+ 0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E,
+ 0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+ 0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86,
+ 0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC,
+ 0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+ 0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC,
+ 0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68,
+ 0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+ 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+ 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+ 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55,
+ 0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E,
+ 0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63,
+ 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF,
+ 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94,
+ 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF,
+ 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4,
+ 0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08,
+ 0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01,
+ 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C,
+ 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF,
+ 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33,
+ 0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2,
+ 0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD,
+ 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06,
+ 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6,
+ 0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04,
+ 0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04,
+ 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10,
+ 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+ 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F,
+ 0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD,
+ 0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22,
+ 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1,
+ 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA,
+ 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF,
+ 0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0,
+ 0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01,
+ 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81,
+ 0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04,
+ 0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07,
+ 0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+ 0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7,
+ 0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00,
+ 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0,
+ 0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32,
+ 0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25,
+ 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00,
+ 0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5,
+ 0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00,
+ 0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F,
+ 0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA,
+ 0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC,
+ 0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78,
+ 0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00,
+ 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17,
+ 0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06,
+ 0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06,
+ 0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31,
+ 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+ 0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D,
+ 0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD,
+ 0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+ 0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA,
+ 0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE,
+ 0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+ 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+ 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+ 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D,
+ 0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33,
+ 0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E,
+ 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00,
+ 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96,
+ 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF,
+ 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E,
+ 0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04,
+ 0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01,
+ 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD,
+ 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF,
+ 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49,
+ 0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1,
+ 0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE,
+ 0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD,
+ 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8,
+ 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E,
+ 0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03,
+ 0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04,
+ 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38,
+ 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF,
+ 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97,
+ 0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD,
+ 0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16,
+ 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2,
+ 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF,
+ 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+ 0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5,
+ 0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01,
+ 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C,
+ 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+ 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+ 0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+ 0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7,
+ 0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF,
+ 0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97,
+ 0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E,
+ 0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D,
+ 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00,
+ 0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C,
+ 0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+ 0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F,
+ 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC,
+ 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC,
+ 0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3,
+ 0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00,
+ 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A,
+ 0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06,
+ 0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02,
+ 0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05,
+ 0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E,
+ 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00,
+ 0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03,
+ 0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD,
+ 0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C,
+ 0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8,
+ 0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54,
+ 0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+ 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+ 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+ 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B,
+ 0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37,
+ 0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40,
+ 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00,
+ 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2,
+ 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF,
+ 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42,
+ 0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48,
+ 0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98,
+ 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01,
+ 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0,
+ 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF,
+ 0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A,
+ 0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1,
+ 0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD,
+ 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE,
+ 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43,
+ 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+ 0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF,
+ 0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03,
+ 0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05,
+ 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B,
+ 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+ 0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7,
+ 0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC,
+ 0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08,
+ 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3,
+ 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6,
+ 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF,
+ 0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86,
+ 0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01,
+ 0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A,
+ 0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C,
+ 0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96,
+ 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+ 0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E,
+ 0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF,
+ 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60,
+ 0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29,
+ 0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33,
+ 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF,
+ 0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0,
+ 0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA,
+ 0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01,
+ 0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B,
+ 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC,
+ 0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04,
+ 0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+ 0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D,
+ 0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06,
+ 0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05,
+ 0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04,
+ 0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63,
+ 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+ 0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62,
+ 0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD,
+ 0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+ 0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7,
+ 0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7,
+ 0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+ 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+ 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+ 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0,
+ 0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B,
+ 0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38,
+ 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00,
+ 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC,
+ 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF,
+ 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72,
+ 0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47,
+ 0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF,
+ 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+ 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56,
+ 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00,
+ 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7,
+ 0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1,
+ 0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD,
+ 0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF,
+ 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD,
+ 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4,
+ 0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD,
+ 0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE,
+ 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06,
+ 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF,
+ 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF,
+ 0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A,
+ 0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC,
+ 0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8,
+ 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4,
+ 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0,
+ 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF,
+ 0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D,
+ 0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01,
+ 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+ 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+ 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+ 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+ 0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97,
+ 0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF,
+ 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B,
+ 0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24,
+ 0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF,
+ 0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45,
+ 0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D,
+ 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD,
+ 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15,
+ 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC,
+ 0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08,
+ 0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00,
+ 0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE,
+ 0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07,
+ 0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+ 0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03,
+ 0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D,
+ 0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94,
+ 0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02,
+ 0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+ 0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5,
+ 0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33,
+ 0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+ 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+ 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+ 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB,
+ 0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F,
+ 0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36,
+ 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00,
+ 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81,
+ 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF,
+ 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D,
+ 0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46,
+ 0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5,
+ 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01,
+ 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D,
+ 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30,
+ 0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2,
+ 0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD,
+ 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00,
+ 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC,
+ 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0,
+ 0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF,
+ 0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE,
+ 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06,
+ 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07,
+ 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+ 0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87,
+ 0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC,
+ 0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5,
+ 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6,
+ 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE,
+ 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF,
+ 0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4,
+ 0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01,
+ 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F,
+ 0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16,
+ 0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28,
+ 0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+ 0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60,
+ 0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF,
+ 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9,
+ 0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F,
+ 0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE,
+ 0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE,
+ 0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B,
+ 0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA,
+ 0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10,
+ 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC,
+ 0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB,
+ 0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B,
+ 0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07,
+ 0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D,
+ 0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01,
+ 0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9,
+ 0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1,
+ 0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01,
+ 0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+ 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4,
+ 0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96,
+ 0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+ 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+ 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+ 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B,
+ 0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42,
+ 0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A,
+ 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00,
+ 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50,
+ 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF,
+ 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF,
+ 0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44,
+ 0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA,
+ 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01,
+ 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1,
+ 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00,
+ 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3,
+ 0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3,
+ 0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00,
+ 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35,
+ 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F,
+ 0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00,
+ 0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD,
+ 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06,
+ 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76,
+ 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF,
+ 0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9,
+ 0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC,
+ 0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1,
+ 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8,
+ 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1,
+ 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+ 0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F,
+ 0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE,
+ 0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+ 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+ 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+ 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF,
+ 0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC,
+ 0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF,
+ 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C,
+ 0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A,
+ 0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35,
+ 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE,
+ 0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1,
+ 0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5,
+ 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8,
+ 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B,
+ 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC,
+ 0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B,
+ 0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+ 0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84,
+ 0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06,
+ 0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12,
+ 0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00,
+ 0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45,
+ 0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31,
+ 0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00,
+ 0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36,
+ 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3,
+ 0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF,
+ 0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+ 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+ 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+ 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F,
+ 0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44,
+ 0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43,
+ 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF,
+ 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB,
+ 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00,
+ 0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+ 0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41,
+ 0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE,
+ 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01,
+ 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C,
+ 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00,
+ 0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E,
+ 0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4,
+ 0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+ 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01,
+ 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60,
+ 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D,
+ 0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02,
+ 0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD,
+ 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07,
+ 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE,
+ 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4,
+ 0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC,
+ 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB,
+ 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB,
+ 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B,
+ 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00,
+ 0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73,
+ 0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF,
+ 0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+ 0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20,
+ 0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7,
+ 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF,
+ 0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B,
+ 0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF,
+ 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04,
+ 0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15,
+ 0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01,
+ 0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70,
+ 0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF,
+ 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81,
+ 0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5,
+ 0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07,
+ 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC,
+ 0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7,
+ 0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+ 0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B,
+ 0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06,
+ 0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+ 0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE,
+ 0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC,
+ 0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F,
+ 0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF,
+ 0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35,
+ 0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2,
+ 0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B,
+ 0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00,
+ 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+ 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+ 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76,
+ 0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46,
+ 0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51,
+ 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF,
+ 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C,
+ 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00,
+ 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+ 0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E,
+ 0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF,
+ 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01,
+ 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8,
+ 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00,
+ 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00,
+ 0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5,
+ 0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE,
+ 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0,
+ 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00,
+ 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3,
+ 0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03,
+ 0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD,
+ 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07,
+ 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2,
+ 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00,
+ 0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF,
+ 0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC,
+ 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5,
+ 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE,
+ 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E,
+ 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+ 0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A,
+ 0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF,
+ 0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+ 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+ 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+ 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF,
+ 0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30,
+ 0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF,
+ 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95,
+ 0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10,
+ 0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01,
+ 0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9,
+ 0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF,
+ 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50,
+ 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4,
+ 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03,
+ 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC,
+ 0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20,
+ 0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+ 0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83,
+ 0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06,
+ 0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+ 0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD,
+ 0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C,
+ 0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07,
+ 0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF,
+ 0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+ 0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1,
+ 0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A,
+ 0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00,
+ 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+ 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+ 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF,
+ 0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48,
+ 0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62,
+ 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF,
+ 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1,
+ 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00,
+ 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+ 0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A,
+ 0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01,
+ 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B,
+ 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+ 0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4,
+ 0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7,
+ 0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD,
+ 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89,
+ 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00,
+ 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B,
+ 0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04,
+ 0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD,
+ 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06,
+ 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61,
+ 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+ 0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B,
+ 0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC,
+ 0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F,
+ 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01,
+ 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B,
+ 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+ 0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6,
+ 0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF,
+ 0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45,
+ 0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A,
+ 0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D,
+ 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF,
+ 0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C,
+ 0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+ 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20,
+ 0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C,
+ 0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01,
+ 0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7,
+ 0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF,
+ 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35,
+ 0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2,
+ 0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01,
+ 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC,
+ 0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28,
+ 0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+ 0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F,
+ 0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05,
+ 0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03,
+ 0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC,
+ 0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+ 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6,
+ 0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE,
+ 0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A,
+ 0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1,
+ 0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB,
+ 0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF,
+ 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+ 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+ 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8,
+ 0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48,
+ 0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75,
+ 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF,
+ 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD,
+ 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00,
+ 0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8,
+ 0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36,
+ 0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C,
+ 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00,
+ 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC,
+ 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00,
+ 0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96,
+ 0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8,
+ 0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD,
+ 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F,
+ 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00,
+ 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF,
+ 0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05,
+ 0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE,
+ 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06,
+ 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C,
+ 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+ 0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB,
+ 0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC,
+ 0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+ 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB,
+ 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72,
+ 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+ 0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7,
+ 0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00,
+ 0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+ 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+ 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+ 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF,
+ 0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23,
+ 0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF,
+ 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA,
+ 0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07,
+ 0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01,
+ 0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2,
+ 0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF,
+ 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34,
+ 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2,
+ 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF,
+ 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD,
+ 0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01,
+ 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4,
+ 0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04,
+ 0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04,
+ 0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD,
+ 0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF,
+ 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76,
+ 0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD,
+ 0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21,
+ 0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1,
+ 0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD,
+ 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF,
+ 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+ 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+ 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F,
+ 0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04,
+ 0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03,
+ 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF,
+ 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89,
+ 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00,
+ 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD,
+ 0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32,
+ 0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26,
+ 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00,
+ 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10,
+ 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00,
+ 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73,
+ 0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA,
+ 0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC,
+ 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98,
+ 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00,
+ 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B,
+ 0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06,
+ 0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05,
+ 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31,
+ 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00,
+ 0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C,
+ 0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD,
+ 0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+ 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA,
+ 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9,
+ 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+ 0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD,
+ 0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00,
+ 0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F,
+ 0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33,
+ 0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D,
+ 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00,
+ 0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25,
+ 0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF,
+ 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11,
+ 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48,
+ 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84,
+ 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+ 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A,
+ 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00,
+ 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1,
+ 0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3,
+ 0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE,
+ 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7,
+ 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00,
+ 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26,
+ 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06,
+ 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16,
+ 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF,
+ 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A,
+ 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+ 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84,
+ 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD,
+ 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8,
+ 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9,
+ 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2,
+ 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+ 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+ 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+ 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12,
+ 0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42,
+ 0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B,
+ 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF,
+ 0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7,
+ 0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00,
+ 0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4,
+ 0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20,
+ 0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01,
+ 0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C,
+ 0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF,
+ 0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04,
+ 0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2,
+ 0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD,
+ 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD,
+ 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70,
+ 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+ 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+ 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+ 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B,
+ 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD,
+ 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7,
+ 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+ 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC,
+ 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC,
+ 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2,
+ 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC,
+ 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79,
+ 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+ 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+ 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+ 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47,
+ 0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44,
+ 0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45,
+ 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF,
+ 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32,
+ 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF,
+ 0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77,
+ 0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B,
+ 0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36,
+ 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01,
+ 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E,
+ 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF,
+ 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E,
+ 0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2,
+ 0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD,
+ 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07,
+ 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00,
+ 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96,
+ 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07,
+ 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00,
+ 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02,
+ 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25,
+ 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF,
+ 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85,
+ 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC,
+ 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C,
+ 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF,
+ 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49,
+ 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+ 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+ 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+ 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F,
+ 0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46,
+ 0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53,
+ 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+ 0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE,
+ 0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF,
+ 0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11,
+ 0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16,
+ 0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33,
+ 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01,
+ 0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20,
+ 0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF,
+ 0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10,
+ 0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3,
+ 0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE,
+ 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC,
+ 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83,
+ 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00,
+ 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+ 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+ 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00,
+ 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03,
+ 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45,
+ 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF,
+ 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C,
+ 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC,
+ 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86,
+ 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03,
+ 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14,
+ 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+ 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+ 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+ 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8,
+ 0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48,
+ 0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64,
+ 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF,
+ 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54,
+ 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF,
+ 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2,
+ 0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11,
+ 0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F,
+ 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01,
+ 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55,
+ 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF,
+ 0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9,
+ 0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5,
+ 0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE,
+ 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC,
+ 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC,
+ 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00,
+ 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38,
+ 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06,
+ 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+ 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04,
+ 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A,
+ 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+ 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7,
+ 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC,
+ 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24,
+ 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB,
+ 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3,
+ 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+ 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+ 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+ 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1,
+ 0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48,
+ 0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78,
+ 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF,
+ 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46,
+ 0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF,
+ 0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E,
+ 0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C,
+ 0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A,
+ 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01,
+ 0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD,
+ 0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00,
+ 0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77,
+ 0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6,
+ 0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF,
+ 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC,
+ 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C,
+ 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+ 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+ 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+ 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05,
+ 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A,
+ 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC,
+ 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC,
+ 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29,
+ 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9,
+ 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19,
+ 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+ 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+ 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+ 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C,
+ 0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05,
+ 0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA,
+ 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF,
+ 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3,
+ 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF,
+ 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8,
+ 0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08,
+ 0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25,
+ 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01,
+ 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14,
+ 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00,
+ 0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44,
+ 0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8,
+ 0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF,
+ 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC,
+ 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F,
+ 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00,
+ 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15,
+ 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05,
+ 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+ 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06,
+ 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9,
+ 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00,
+ 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10,
+ 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC,
+ 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E,
+ 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7,
+ 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E,
+ 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00,
+ 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+ 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+ 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68,
+ 0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09,
+ 0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2,
+ 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF,
+ 0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A,
+ 0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF,
+ 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42,
+ 0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04,
+ 0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F,
+ 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01,
+ 0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97,
+ 0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00,
+ 0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D,
+ 0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9,
+ 0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00,
+ 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC,
+ 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1,
+ 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00,
+ 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+ 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+ 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+ 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06,
+ 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C,
+ 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00,
+ 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7,
+ 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC,
+ 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32,
+ 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6,
+ 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE,
+ 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00,
+ 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+ 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+ 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56,
+ 0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D,
+ 0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89,
+ 0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF,
+ 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60,
+ 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF,
+ 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40,
+ 0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48,
+ 0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98,
+ 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01,
+ 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80,
+ 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00,
+ 0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD,
+ 0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB,
+ 0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00,
+ 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC,
+ 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F,
+ 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+ 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D,
+ 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04,
+ 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD,
+ 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06,
+ 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96,
+ 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00,
+ 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0,
+ 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC,
+ 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35,
+ 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4,
+ 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66,
+ 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF,
+ 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+ 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+ 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47,
+ 0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12,
+ 0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51,
+ 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF,
+ 0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64,
+ 0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF,
+ 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71,
+ 0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47,
+ 0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE,
+ 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01,
+ 0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6,
+ 0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00,
+ 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C,
+ 0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02,
+ 0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C,
+ 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC,
+ 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA,
+ 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+ 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+ 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+ 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD,
+ 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07,
+ 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18,
+ 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00,
+ 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B,
+ 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD,
+ 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+ 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3,
+ 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4,
+ 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF,
+ 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+ 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+ 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D,
+ 0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17,
+ 0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B,
+ 0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF,
+ 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B,
+ 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF,
+ 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C,
+ 0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46,
+ 0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4,
+ 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01,
+ 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61,
+ 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+ 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D,
+ 0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE,
+ 0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16,
+ 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC,
+ 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21,
+ 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+ 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13,
+ 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02,
+ 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD,
+ 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07,
+ 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5,
+ 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00,
+ 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94,
+ 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD,
+ 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36,
+ 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2,
+ 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17,
+ 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF,
+ 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+ 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+ 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37,
+ 0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C,
+ 0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA,
+ 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00,
+ 0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56,
+ 0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF,
+ 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE,
+ 0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44,
+ 0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9,
+ 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00,
+ 0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47,
+ 0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00,
+ 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36,
+ 0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB,
+ 0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11,
+ 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC,
+ 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27,
+ 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+ 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+ 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+ 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD,
+ 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07,
+ 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E,
+ 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00,
+ 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05,
+ 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD,
+ 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33,
+ 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1,
+ 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C,
+ 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+ 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+ 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+ 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37,
+ 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21,
+ 0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD,
+ 0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00,
+ 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68,
+ 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF,
+ 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+ 0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41,
+ 0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED,
+ 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00,
+ 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C,
+ 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00,
+ 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD,
+ 0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8,
+ 0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C,
+ 0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD,
+ 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF,
+ 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D,
+ 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF,
+ 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE,
+ 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06,
+ 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42,
+ 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+ 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47,
+ 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02,
+ 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E,
+ 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1,
+ 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94,
+ 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+ 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+ 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+ 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C,
+ 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26,
+ 0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96,
+ 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00,
+ 0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0,
+ 0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF,
+ 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+ 0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E,
+ 0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF,
+ 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00,
+ 0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6,
+ 0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+ 0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93,
+ 0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6,
+ 0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08,
+ 0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD,
+ 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC,
+ 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+ 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+ 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF,
+ 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06,
+ 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31,
+ 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+ 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94,
+ 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01,
+ 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26,
+ 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1,
+ 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE,
+ 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+ 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+ 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+ 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49,
+ 0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B,
+ 0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76,
+ 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00,
+ 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E,
+ 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF,
+ 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+ 0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B,
+ 0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E,
+ 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF,
+ 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48,
+ 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00,
+ 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C,
+ 0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4,
+ 0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04,
+ 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE,
+ 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33,
+ 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A,
+ 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02,
+ 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01,
+ 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05,
+ 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B,
+ 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+ 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6,
+ 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00,
+ 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C,
+ 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1,
+ 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8,
+ 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00,
+ 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+ 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+ 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C,
+ 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30,
+ 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C,
+ 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00,
+ 0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1,
+ 0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF,
+ 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8,
+ 0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37,
+ 0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C,
+ 0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF,
+ 0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6,
+ 0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00,
+ 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B,
+ 0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3,
+ 0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01,
+ 0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF,
+ 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A,
+ 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+ 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+ 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+ 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04,
+ 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04,
+ 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C,
+ 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
+ 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48,
+ 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF,
+ 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E,
+ 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2,
+ 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5,
+ 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00,
+ 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+ 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+ 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76,
+ 0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34,
+ 0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49,
+ 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF,
+ 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2,
+ 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01,
+ 0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE,
+ 0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32,
+ 0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26,
+ 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE,
+ 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94,
+ 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00,
+ 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31,
+ 0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2,
+ 0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF,
+ 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00,
+ 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6,
+ 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+ 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC,
+ 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04,
+ 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08,
+ 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03,
+ 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93,
+ 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF,
+ 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6,
+ 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE,
+ 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF,
+ 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3,
+ 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4,
+ 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00,
+ 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+ 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+ 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96,
+ 0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38,
+ 0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C,
+ 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF,
+ 0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C,
+ 0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00,
+ 0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94,
+ 0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E,
+ 0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E,
+ 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE,
+ 0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47,
+ 0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00,
+ 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42,
+ 0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1,
+ 0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+ 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00,
+ 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D,
+ 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00,
+ 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+ 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+ 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C,
+ 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02,
+ 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD,
+ 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+ 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C,
+ 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE,
+ 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED,
+ 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5,
+ 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7,
+ 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00,
+ 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+ 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+ 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD,
+ 0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C,
+ 0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37,
+ 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF,
+ 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69,
+ 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00,
+ 0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C,
+ 0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29,
+ 0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33,
+ 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01,
+ 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05,
+ 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF,
+ 0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E,
+ 0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1,
+ 0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD,
+ 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01,
+ 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46,
+ 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00,
+ 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27,
+ 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06,
+ 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11,
+ 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00,
+ 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36,
+ 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+ 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47,
+ 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD,
+ 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9,
+ 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7,
+ 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE,
+ 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00,
+ 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+ 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+ 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA,
+ 0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40,
+ 0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37,
+ 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+ 0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8,
+ 0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00,
+ 0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17,
+ 0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24,
+ 0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01,
+ 0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94,
+ 0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF,
+ 0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5,
+ 0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1,
+ 0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD,
+ 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE,
+ 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13,
+ 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00,
+ 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+ 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+ 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16,
+ 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF,
+ 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D,
+ 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+ 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61,
+ 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD,
+ 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4,
+ 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA,
+ 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C,
+ 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+ 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+ 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+ 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B,
+ 0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43,
+ 0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D,
+ 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF,
+ 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77,
+ 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00,
+ 0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4,
+ 0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F,
+ 0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+ 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01,
+ 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B,
+ 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF,
+ 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18,
+ 0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2,
+ 0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+ 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD,
+ 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA,
+ 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00,
+ 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA,
+ 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07,
+ 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C,
+ 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD,
+ 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C,
+ 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+ 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6,
+ 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC,
+ 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE,
+ 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD,
+ 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71,
+ 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+ 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+ 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+ 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51,
+ 0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45,
+ 0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47,
+ 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF,
+ 0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB,
+ 0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF,
+ 0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66,
+ 0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A,
+ 0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35,
+ 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01,
+ 0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0,
+ 0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF,
+ 0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96,
+ 0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3,
+ 0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD,
+ 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D,
+ 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00,
+ 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+ 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+ 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00,
+ 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03,
+ 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD,
+ 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF,
+ 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80,
+ 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC,
+ 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98,
+ 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00,
+ 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40,
+ 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+ 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+ 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+ 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89,
+ 0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47,
+ 0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56,
+ 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+ 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61,
+ 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF,
+ 0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE,
+ 0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15,
+ 0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32,
+ 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01,
+ 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7,
+ 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF,
+ 0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C,
+ 0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4,
+ 0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+ 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC,
+ 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3,
+ 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1,
+ 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07,
+ 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00,
+ 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03,
+ 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D,
+ 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF,
+ 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97,
+ 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC,
+ 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F,
+ 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC,
+ 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42,
+ 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+ 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+ 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+ 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2,
+ 0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48,
+ 0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68,
+ 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF,
+ 0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5,
+ 0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF,
+ 0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E,
+ 0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10,
+ 0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E,
+ 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01,
+ 0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10,
+ 0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF,
+ 0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9,
+ 0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5,
+ 0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+ 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC,
+ 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15,
+ 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+ 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+ 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+ 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04,
+ 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44,
+ 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+ 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14,
+ 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC,
+ 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25,
+ 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA,
+ 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8,
+ 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+ 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+ 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+ 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA,
+ 0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48,
+ 0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C,
+ 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF,
+ 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86,
+ 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF,
+ 0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19,
+ 0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B,
+ 0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29,
+ 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01,
+ 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC,
+ 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00,
+ 0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A,
+ 0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7,
+ 0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF,
+ 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC,
+ 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E,
+ 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00,
+ 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C,
+ 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06,
+ 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05,
+ 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77,
+ 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00,
+ 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD,
+ 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC,
+ 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A,
+ 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9,
+ 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E,
+ 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+ 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+ 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+ 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78,
+ 0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06,
+ 0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1,
+ 0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF,
+ 0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15,
+ 0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF,
+ 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3,
+ 0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07,
+ 0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24,
+ 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01,
+ 0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7,
+ 0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00,
+ 0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A,
+ 0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8,
+ 0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+ 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC,
+ 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38,
+ 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00,
+ 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+ 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+ 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+ 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06,
+ 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9,
+ 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00,
+ 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55,
+ 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC,
+ 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F,
+ 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7,
+ 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2,
+ 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00,
+ 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+ 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+ 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64,
+ 0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A,
+ 0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8,
+ 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF,
+ 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62,
+ 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF,
+ 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14,
+ 0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48,
+ 0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86,
+ 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01,
+ 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C,
+ 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00,
+ 0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45,
+ 0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA,
+ 0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00,
+ 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC,
+ 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00,
+ 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00,
+ 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83,
+ 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05,
+ 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+ 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06,
+ 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10,
+ 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00,
+ 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20,
+ 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC,
+ 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33,
+ 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6,
+ 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11,
+ 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00,
+ 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+ 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+ 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53,
+ 0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E,
+ 0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F,
+ 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF,
+ 0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80,
+ 0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF,
+ 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49,
+ 0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48,
+ 0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C,
+ 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01,
+ 0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85,
+ 0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00,
+ 0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25,
+ 0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB,
+ 0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00,
+ 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC,
+ 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96,
+ 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF,
+ 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+ 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+ 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD,
+ 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06,
+ 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E,
+ 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00,
+ 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E,
+ 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC,
+ 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36,
+ 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4,
+ 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77,
+ 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF,
+ 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+ 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+ 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45,
+ 0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13,
+ 0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47,
+ 0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF,
+ 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81,
+ 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+ 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79,
+ 0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47,
+ 0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2,
+ 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01,
+ 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC,
+ 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00,
+ 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7,
+ 0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01,
+ 0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B,
+ 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC,
+ 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7,
+ 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF,
+ 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70,
+ 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03,
+ 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD,
+ 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07,
+ 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04,
+ 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00,
+ 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C,
+ 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD,
+ 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+ 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3,
+ 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4,
+ 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF,
+ 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+ 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+ 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B,
+ 0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18,
+ 0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12,
+ 0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF,
+ 0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78,
+ 0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF,
+ 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2,
+ 0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46,
+ 0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8,
+ 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01,
+ 0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84,
+ 0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+ 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A,
+ 0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE,
+ 0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16,
+ 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC,
+ 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26,
+ 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+ 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+ 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+ 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD,
+ 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07,
+ 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71,
+ 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+ 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7,
+ 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02,
+ 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05,
+ 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+ 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+ 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+ 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED,
+ 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00,
+ 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84,
+ 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+ 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+ 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00,
+ 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04,
+ 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF,
+ 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B,
+ 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11,
+ 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F,
+ 0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00,
+ 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C,
+ 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00,
+ 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8,
+ 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3,
+ 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD,
+ 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23,
+ 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF,
+ 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+ 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+ 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD,
+ 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07,
+ 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5,
+ 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF,
+ 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC,
+ 0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD,
+ 0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35,
+ 0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4,
+ 0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71,
+ 0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+ 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44,
+ 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF,
+ 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+ 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+ 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+ 0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+ 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5,
+ 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00,
+ 0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1,
+ 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07,
+ 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF,
+ 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A,
+ 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00,
+ 0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B,
+ 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6,
+ 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF,
+ 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE,
+ 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99,
+ 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+ 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+ 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+ 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD,
+ 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06,
+ 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E,
+ 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+ 0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8,
+ 0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC,
+ 0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36,
+ 0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2,
+ 0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20,
+ 0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+ 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C,
+ 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00,
+ 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+ 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+ 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+ 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF,
+ 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F,
+ 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00,
+ 0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43,
+ 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48,
+ 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99,
+ 0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE,
+ 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7,
+ 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00,
+ 0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6,
+ 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9,
+ 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00,
+ 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF,
+ 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C,
+ 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF,
+ 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+ 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+ 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00,
+ 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05,
+ 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35,
+ 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF,
+ 0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1,
+ 0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC,
+ 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D,
+ 0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1,
+ 0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A,
+ 0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00,
+ 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36,
+ 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01,
+ 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+ 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+ 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+ 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+ 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB,
+ 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00,
+ 0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E,
+ 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46,
+ 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6,
+ 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01,
+ 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D,
+ 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF,
+ 0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74,
+ 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC,
+ 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00,
+ 0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01,
+ 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8,
+ 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00,
+ 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+ 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+ 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06,
+ 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03,
+ 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D,
+ 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+ 0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56,
+ 0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC,
+ 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A,
+ 0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1,
+ 0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB,
+ 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00,
+ 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF,
+ 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01,
+ 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+ 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+ 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+ 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+ 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A,
+ 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF,
+ 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8,
+ 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41,
+ 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE,
+ 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01,
+ 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9,
+ 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF,
+ 0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65,
+ 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD,
+ 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14,
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE,
+ 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E,
+ 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00,
+ 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+ 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+ 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F,
+ 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01,
+ 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15,
+ 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00,
+ 0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD,
+ 0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC,
+ 0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD,
+ 0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3,
+ 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3,
+ 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00,
+ 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A,
+ 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+ 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+ 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+ 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+ 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+ 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81,
+ 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF,
+ 0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+ 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A,
+ 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F,
+ 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01,
+ 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85,
+ 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF,
+ 0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4,
+ 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7,
+ 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A,
+ 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD,
+ 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91,
+ 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00,
+ 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+ 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+ 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A,
+ 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD,
+ 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4,
+ 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00,
+ 0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB,
+ 0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD,
+ 0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7,
+ 0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7,
+ 0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA,
+ 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF,
+ 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D,
+ 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01,
+ 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+ 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+ 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+ 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF,
+ 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40,
+ 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF,
+ 0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC,
+ 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32,
+ 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26,
+ 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01,
+ 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A,
+ 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00,
+ 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48,
+ 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3,
+ 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03,
+ 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC,
+ 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D,
+ 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00,
+ 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+ 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+ 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00,
+ 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03,
+ 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C,
+ 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00,
+ 0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6,
+ 0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE,
+ 0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC,
+ 0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD,
+ 0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C,
+ 0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF,
+ 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B,
+ 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01,
+ 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+ 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+ 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+ 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF,
+ 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52,
+ 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+ 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59,
+ 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28,
+ 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33,
+ 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01,
+ 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E,
+ 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00,
+ 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37,
+ 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1,
+ 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE,
+ 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC,
+ 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F,
+ 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00,
+ 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+ 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+ 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF,
+ 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05,
+ 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE,
+ 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8,
+ 0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01,
+ 0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC,
+ 0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50,
+ 0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF,
+ 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70,
+ 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01,
+ 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+ 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+ 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+ 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00,
+ 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B,
+ 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF,
+ 0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF,
+ 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E,
+ 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36,
+ 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01,
+ 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B,
+ 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00,
+ 0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90,
+ 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1,
+ 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD,
+ 0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC,
+ 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB,
+ 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF,
+ 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+ 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+ 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE,
+ 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06,
+ 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49,
+ 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF,
+ 0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E,
+ 0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF,
+ 0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B,
+ 0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8,
+ 0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C,
+ 0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00,
+ 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83,
+ 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE,
+ 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+ 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+ 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+ 0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00,
+ 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7,
+ 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF,
+ 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7,
+ 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14,
+ 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32,
+ 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00,
+ 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47,
+ 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00,
+ 0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57,
+ 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2,
+ 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD,
+ 0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC,
+ 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29,
+ 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+ 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+ 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+ 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD,
+ 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07,
+ 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C,
+ 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+ 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB,
+ 0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD,
+ 0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33,
+ 0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5,
+ 0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D,
+ 0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+ 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59,
+ 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF,
+ 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C,
+ 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+ 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+ 0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00,
+ 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13,
+ 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF,
+ 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12,
+ 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B,
+ 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29,
+ 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF,
+ 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19,
+ 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00,
+ 0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85,
+ 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4,
+ 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE,
+ 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD,
+ 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6,
+ 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+ 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+ 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+ 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD,
+ 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07,
+ 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78,
+ 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+ 0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E,
+ 0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC,
+ 0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+ 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3,
+ 0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE,
+ 0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+ 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C,
+ 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00,
+ 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+ 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+ 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+ 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF,
+ 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D,
+ 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00,
+ 0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18,
+ 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48,
+ 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87,
+ 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE,
+ 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61,
+ 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00,
+ 0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06,
+ 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7,
+ 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF,
+ 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE,
+ 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00,
+ 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF,
+ 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+ 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+ 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+ 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06,
+ 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33,
+ 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+ 0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E,
+ 0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC,
+ 0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32,
+ 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1,
+ 0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E,
+ 0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+ 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18,
+ 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01,
+ 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+ 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+ 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+ 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF,
+ 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC,
+ 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00,
+ 0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C,
+ 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47,
+ 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3,
+ 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01,
+ 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73,
+ 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF,
+ 0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB,
+ 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB,
+ 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00,
+ 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1,
+ 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF,
+ 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+ 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+ 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04,
+ 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04,
+ 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55,
+ 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+ 0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74,
+ 0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC,
+ 0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23,
+ 0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1,
+ 0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7,
+ 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00,
+ 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2,
+ 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01,
+ 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+ 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+ 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+ 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+ 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E,
+ 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF,
+ 0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5,
+ 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43,
+ 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE,
+ 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01,
+ 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1,
+ 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF,
+ 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC,
+ 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF,
+ 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18,
+ 0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02,
+ 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8,
+ 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+ 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+ 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B,
+ 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02,
+ 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF,
+ 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00,
+ 0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F,
+ 0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC,
+ 0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+ 0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2,
+ 0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6,
+ 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00,
+ 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06,
+ 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+ 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+ 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+ 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+ 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF,
+ 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE,
+ 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF,
+ 0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6,
+ 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D,
+ 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03,
+ 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01,
+ 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02,
+ 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF,
+ 0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6,
+ 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9,
+ 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E,
+ 0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD,
+ 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B,
+ 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00,
+ 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+ 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+ 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00,
+ 0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06,
+ 0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16,
+ 0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF,
+ 0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88,
+ 0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02,
+ 0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC,
+ 0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A,
+ 0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18,
+ 0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF,
+ 0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3,
+ 0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00,
+ 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75,
+ 0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10,
+ 0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72,
+ 0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF,
+ 0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6,
+ 0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF,
+ 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C,
+ 0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10,
+ 0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD,
+ 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00,
+ 0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35,
+ 0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1,
+ 0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E,
+ 0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9,
+ 0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02,
+ 0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F,
+ 0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF,
+ 0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71,
+ 0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05,
+ 0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF,
+ 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06,
+ 0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6,
+ 0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF,
+ 0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B,
+ 0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02,
+ 0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89,
+ 0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E,
+ 0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1,
+ 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF,
+ 0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD,
+ 0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00,
+ 0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F,
+ 0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10,
+ 0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E,
+ 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF,
+ 0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA,
+ 0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF,
+ 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6,
+ 0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10,
+ 0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2,
+ 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00,
+ 0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3,
+ 0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF,
+ 0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50,
+ 0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E,
+ 0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2,
+ 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02,
+ 0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03,
+ 0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF,
+ 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB,
+ 0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06,
+ 0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00,
+ 0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05,
+ 0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73,
+ 0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF,
+ 0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88,
+ 0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02,
+ 0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93,
+ 0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E,
+ 0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9,
+ 0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF,
+ 0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E,
+ 0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00,
+ 0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C,
+ 0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10,
+ 0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C,
+ 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF,
+ 0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38,
+ 0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF,
+ 0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6,
+ 0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10,
+ 0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97,
+ 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00,
+ 0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D,
+ 0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF,
+ 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB,
+ 0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A,
+ 0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00,
+ 0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02,
+ 0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99,
+ 0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF,
+ 0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45,
+ 0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06,
+ 0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00,
+ 0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04,
+ 0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4,
+ 0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF,
+ 0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00,
+ 0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03,
+ 0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D,
+ 0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D,
+ 0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7,
+ 0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF,
+ 0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05,
+ 0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01,
+ 0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D,
+ 0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10,
+ 0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D,
+ 0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF,
+ 0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C,
+ 0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF,
+ 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB,
+ 0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10,
+ 0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D,
+ 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00,
+ 0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3,
+ 0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF,
+ 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C,
+ 0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B,
+ 0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01,
+ 0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F,
+ 0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF,
+ 0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF,
+ 0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07,
+ 0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6,
+ 0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09,
+ 0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D,
+ 0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+ 0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73,
+ 0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03,
+ 0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8,
+ 0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D,
+ 0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA,
+ 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF,
+ 0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9,
+ 0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF,
+ 0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70,
+ 0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F,
+ 0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72,
+ 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF,
+ 0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6,
+ 0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF,
+ 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96,
+ 0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10,
+ 0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84,
+ 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF,
+ 0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52,
+ 0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF,
+ 0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3,
+ 0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B,
+ 0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE,
+ 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01,
+ 0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5,
+ 0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF,
+ 0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C,
+ 0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07,
+ 0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC,
+ 0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09,
+ 0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2,
+ 0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+ 0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3,
+ 0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04,
+ 0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3,
+ 0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C,
+ 0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24,
+ 0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF,
+ 0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E,
+ 0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF,
+ 0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75,
+ 0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F,
+ 0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A,
+ 0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF,
+ 0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15,
+ 0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF,
+ 0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0,
+ 0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F,
+ 0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7,
+ 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF,
+ 0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC,
+ 0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF,
+ 0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F,
+ 0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C,
+ 0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC,
+ 0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04,
+ 0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E,
+ 0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00,
+ 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B,
+ 0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08,
+ 0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2,
+ 0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08,
+ 0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E,
+ 0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+ 0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50,
+ 0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04,
+ 0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE,
+ 0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C,
+ 0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53,
+ 0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF,
+ 0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88,
+ 0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF,
+ 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C,
+ 0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F,
+ 0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86,
+ 0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF,
+ 0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A,
+ 0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF,
+ 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF,
+ 0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F,
+ 0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF,
+ 0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E,
+ 0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF,
+ 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2,
+ 0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C,
+ 0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9,
+ 0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04,
+ 0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6,
+ 0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00,
+ 0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD,
+ 0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09,
+ 0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7,
+ 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07,
+ 0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91,
+ 0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF,
+ 0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82,
+ 0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01,
+ 0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8,
+ 0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B,
+ 0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A,
+ 0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF,
+ 0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65,
+ 0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF,
+ 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90,
+ 0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10,
+ 0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81,
+ 0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF,
+ 0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73,
+ 0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF,
+ 0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74,
+ 0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F,
+ 0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4,
+ 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF,
+ 0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8,
+ 0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF,
+ 0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B,
+ 0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D,
+ 0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5,
+ 0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03,
+ 0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32,
+ 0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF,
+ 0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72,
+ 0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09,
+ 0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA,
+ 0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07,
+ 0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA,
+ 0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF,
+ 0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14,
+ 0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01,
+ 0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1,
+ 0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B,
+ 0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6,
+ 0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF,
+ 0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37,
+ 0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00,
+ 0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81,
+ 0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10,
+ 0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79,
+ 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF,
+ 0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91,
+ 0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF,
+ 0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41,
+ 0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10,
+ 0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA,
+ 0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01,
+ 0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88,
+ 0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF,
+ 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B,
+ 0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D,
+ 0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0,
+ 0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03,
+ 0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1,
+ 0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF,
+ 0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22,
+ 0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04,
+ 0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD,
+ 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06,
+ 0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A,
+ 0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF,
+ 0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F,
+ 0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02,
+ 0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA,
+ 0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A,
+ 0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A,
+ 0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF,
+ 0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF,
+ 0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00,
+ 0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77,
+ 0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10,
+ 0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73,
+ 0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF,
+ 0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4,
+ 0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF,
+ 0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14,
+ 0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10,
+ 0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF,
+ 0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00,
+ 0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A,
+ 0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1,
+ 0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E,
+ 0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA,
+ 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02,
+ 0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53,
+ 0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF,
+ 0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D,
+ 0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05,
+ 0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF,
+ 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06,
+ 0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF,
+ 0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF,
+ 0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23,
+ 0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02,
+ 0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87,
+ 0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E,
+ 0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D,
+ 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF,
+ 0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB,
+ 0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00,
+ 0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70,
+ 0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10,
+ 0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E,
+ 0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF,
+ 0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB,
+ 0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF,
+ 0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC,
+ 0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10,
+ 0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4,
+ 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00,
+ 0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9,
+ 0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF,
+ 0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F,
+ 0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E,
+ 0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3,
+ 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02,
+ 0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7,
+ 0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF,
+ 0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7,
+ 0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06,
+ 0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00,
+ 0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05,
+ 0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B,
+ 0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF,
+ 0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1,
+ 0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02,
+ 0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91,
+ 0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E,
+ 0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5,
+ 0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF,
+ 0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D,
+ 0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00,
+ 0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D,
+ 0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10,
+ 0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C,
+ 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF,
+ 0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27,
+ 0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF,
+ 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB,
+ 0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10,
+ 0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99,
+ 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00,
+ 0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34,
+ 0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF,
+ 0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3,
+ 0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A,
+ 0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00,
+ 0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02,
+ 0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D,
+ 0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF,
+ 0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61,
+ 0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06,
+ 0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00,
+ 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05,
+ 0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC,
+ 0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF,
+ 0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A,
+ 0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03,
+ 0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B,
+ 0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D,
+ 0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1,
+ 0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF,
+ 0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16,
+ 0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00,
+ 0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C,
+ 0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10,
+ 0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D,
+ 0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF,
+ 0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D,
+ 0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF,
+ 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0,
+ 0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10,
+ 0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F,
+ 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00,
+ 0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA,
+ 0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF,
+ 0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43,
+ 0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A,
+ 0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01,
+ 0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13,
+ 0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF,
+ 0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB,
+ 0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07,
+ 0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4,
+ 0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09,
+ 0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E,
+ 0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+ 0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E,
+ 0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03,
+ 0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6,
+ 0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D,
+ 0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3,
+ 0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF,
+ 0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA,
+ 0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF,
+ 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F,
+ 0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F,
+ 0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71,
+ 0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF,
+ 0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47,
+ 0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE,
+ 0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30,
+ 0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20,
+ 0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42,
+ 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF,
+ 0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC,
+ 0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00,
+ 0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F,
+ 0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09,
+ 0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD,
+ 0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB,
+ 0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2,
+ 0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73,
+ 0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03,
+ 0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE,
+ 0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC,
+ 0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49,
+ 0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF,
+ 0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65,
+ 0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB,
+ 0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36,
+ 0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E,
+ 0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02,
+ 0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00,
+ 0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0,
+ 0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00,
+ 0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1,
+ 0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21,
+ 0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D,
+ 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00,
+ 0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62,
+ 0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00,
+ 0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52,
+ 0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20,
+ 0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D,
+ 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF,
+ 0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2,
+ 0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00,
+ 0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78,
+ 0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A,
+ 0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD,
+ 0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB,
+ 0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4,
+ 0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58,
+ 0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04,
+ 0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF,
+ 0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB,
+ 0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F,
+ 0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+ 0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56,
+ 0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB,
+ 0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D,
+ 0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D,
+ 0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9,
+ 0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00,
+ 0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A,
+ 0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00,
+ 0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB,
+ 0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21,
+ 0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00,
+ 0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F,
+ 0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00,
+ 0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71,
+ 0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20,
+ 0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58,
+ 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00,
+ 0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9,
+ 0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00,
+ 0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96,
+ 0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B,
+ 0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC,
+ 0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB,
+ 0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10,
+ 0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF,
+ 0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37,
+ 0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB,
+ 0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB,
+ 0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D,
+ 0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+ 0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C,
+ 0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB,
+ 0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24,
+ 0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C,
+ 0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD,
+ 0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00,
+ 0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24,
+ 0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00,
+ 0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3,
+ 0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21,
+ 0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6,
+ 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00,
+ 0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A,
+ 0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00,
+ 0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C,
+ 0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21,
+ 0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64,
+ 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00,
+ 0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02,
+ 0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00,
+ 0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB,
+ 0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C,
+ 0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB,
+ 0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB,
+ 0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56,
+ 0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF,
+ 0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69,
+ 0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB,
+ 0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+ 0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB,
+ 0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44,
+ 0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF,
+ 0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49,
+ 0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB,
+ 0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D,
+ 0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B,
+ 0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E,
+ 0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00,
+ 0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD,
+ 0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00,
+ 0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8,
+ 0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20,
+ 0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2,
+ 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00,
+ 0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF,
+ 0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00,
+ 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4,
+ 0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21,
+ 0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70,
+ 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00,
+ 0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A,
+ 0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00,
+ 0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4,
+ 0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D,
+ 0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA,
+ 0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB,
+ 0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8,
+ 0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF,
+ 0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4,
+ 0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB,
+ 0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB,
+ 0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53,
+ 0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF,
+ 0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B,
+ 0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB,
+ 0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16,
+ 0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A,
+ 0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D,
+ 0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00,
+ 0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26,
+ 0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF,
+ 0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB,
+ 0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20,
+ 0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD,
+ 0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00,
+ 0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5,
+ 0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9,
+ 0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21,
+ 0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D,
+ 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00,
+ 0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32,
+ 0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00,
+ 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12,
+ 0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E,
+ 0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9,
+ 0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB,
+ 0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05,
+ 0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF,
+ 0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA,
+ 0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC,
+ 0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9,
+ 0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03,
+ 0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2,
+ 0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+ 0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54,
+ 0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB,
+ 0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F,
+ 0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09,
+ 0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A,
+ 0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00,
+ 0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90,
+ 0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF,
+ 0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC,
+ 0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20,
+ 0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7,
+ 0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00,
+ 0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4,
+ 0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00,
+ 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB,
+ 0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21,
+ 0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A,
+ 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00,
+ 0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47,
+ 0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00,
+ 0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43,
+ 0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F,
+ 0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8,
+ 0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB,
+ 0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E,
+ 0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF,
+ 0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B,
+ 0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC,
+ 0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8,
+ 0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02,
+ 0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65,
+ 0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66,
+ 0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB,
+ 0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A,
+ 0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08,
+ 0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4,
+ 0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00,
+ 0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB,
+ 0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF,
+ 0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA,
+ 0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F,
+ 0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1,
+ 0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00,
+ 0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8,
+ 0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00,
+ 0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5,
+ 0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17,
+ 0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB,
+ 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00,
+ 0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59,
+ 0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00,
+ 0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77,
+ 0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10,
+ 0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7,
+ 0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB,
+ 0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2,
+ 0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF,
+ 0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98,
+ 0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC,
+ 0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7,
+ 0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01,
+ 0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29,
+ 0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80,
+ 0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB,
+ 0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05,
+ 0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07,
+ 0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC,
+ 0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00,
+ 0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38,
+ 0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF,
+ 0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6,
+ 0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F,
+ 0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9,
+ 0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00,
+ 0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91,
+ 0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00,
+ 0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30,
+ 0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18,
+ 0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE,
+ 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00,
+ 0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66,
+ 0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00,
+ 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE,
+ 0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11,
+ 0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7,
+ 0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC,
+ 0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62,
+ 0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF,
+ 0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF,
+ 0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC,
+ 0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7,
+ 0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01,
+ 0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF,
+ 0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF,
+ 0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F,
+ 0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD,
+ 0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02,
+ 0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06,
+ 0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72,
+ 0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00,
+ 0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78,
+ 0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF,
+ 0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1,
+ 0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E,
+ 0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0,
+ 0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00,
+ 0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F,
+ 0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00,
+ 0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B,
+ 0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19,
+ 0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01,
+ 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00,
+ 0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E,
+ 0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00,
+ 0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7,
+ 0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13,
+ 0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7,
+ 0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC,
+ 0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED,
+ 0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF,
+ 0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73,
+ 0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD,
+ 0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7,
+ 0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00,
+ 0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5,
+ 0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF,
+ 0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87,
+ 0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD,
+ 0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE,
+ 0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05,
+ 0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38,
+ 0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00,
+ 0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC,
+ 0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE,
+ 0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA,
+ 0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E,
+ 0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6,
+ 0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00,
+ 0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21,
+ 0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00,
+ 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5,
+ 0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A,
+ 0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05,
+ 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD,
+ 0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63,
+ 0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF,
+ 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22,
+ 0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14,
+ 0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7,
+ 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC,
+ 0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84,
+ 0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF,
+ 0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1,
+ 0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD,
+ 0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7,
+ 0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF,
+ 0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E,
+ 0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF,
+ 0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B,
+ 0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD,
+ 0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC,
+ 0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04,
+ 0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC,
+ 0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00,
+ 0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3,
+ 0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE,
+ 0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2,
+ 0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D,
+ 0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA,
+ 0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00,
+ 0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6,
+ 0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00,
+ 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD,
+ 0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B,
+ 0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09,
+ 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE,
+ 0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48,
+ 0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E,
+ 0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15,
+ 0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8,
+ 0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC,
+ 0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26,
+ 0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF,
+ 0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C,
+ 0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE,
+ 0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8,
+ 0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF,
+ 0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49,
+ 0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF,
+ 0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69,
+ 0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC,
+ 0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B,
+ 0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16,
+ 0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD,
+ 0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00,
+ 0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0,
+ 0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE,
+ 0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9,
+ 0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C,
+ 0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB,
+ 0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00,
+ 0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C,
+ 0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00,
+ 0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13,
+ 0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C,
+ 0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F,
+ 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE,
+ 0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36,
+ 0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A,
+ 0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16,
+ 0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9,
+ 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC,
+ 0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4,
+ 0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF,
+ 0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12,
+ 0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE,
+ 0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9,
+ 0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE,
+ 0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18,
+ 0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF,
+ 0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54,
+ 0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC,
+ 0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E,
+ 0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15,
+ 0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC,
+ 0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04,
+ 0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE,
+ 0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE,
+ 0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B,
+ 0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB,
+ 0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00,
+ 0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14,
+ 0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00,
+ 0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47,
+ 0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C,
+ 0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15,
+ 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE,
+ 0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C,
+ 0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00,
+ 0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56,
+ 0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04,
+ 0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00,
+ 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD,
+ 0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C,
+ 0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF,
+ 0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6,
+ 0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8,
+ 0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00,
+ 0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE,
+ 0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42,
+ 0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00,
+ 0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5,
+ 0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03,
+ 0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC,
+ 0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3,
+ 0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF,
+ 0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF,
+ 0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3,
+ 0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE,
+ 0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68,
+ 0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A,
+ 0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1,
+ 0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00,
+ 0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8,
+ 0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00,
+ 0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB,
+ 0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27,
+ 0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3,
+ 0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE,
+ 0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E,
+ 0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF,
+ 0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4,
+ 0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4,
+ 0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01,
+ 0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03,
+ 0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F,
+ 0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62,
+ 0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05,
+ 0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05,
+ 0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB,
+ 0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+ 0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54,
+ 0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03,
+ 0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1,
+ 0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3,
+ 0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7,
+ 0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF,
+ 0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB,
+ 0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE,
+ 0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55,
+ 0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28,
+ 0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4,
+ 0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00,
+ 0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8,
+ 0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00,
+ 0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8,
+ 0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29,
+ 0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD,
+ 0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE,
+ 0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02,
+ 0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF,
+ 0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A,
+ 0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3,
+ 0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01,
+ 0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03,
+ 0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28,
+ 0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+ 0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16,
+ 0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05,
+ 0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05,
+ 0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00,
+ 0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE,
+ 0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04,
+ 0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5,
+ 0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4,
+ 0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2,
+ 0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF,
+ 0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F,
+ 0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE,
+ 0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43,
+ 0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26,
+ 0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6,
+ 0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00,
+ 0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98,
+ 0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00,
+ 0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25,
+ 0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B,
+ 0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7,
+ 0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE,
+ 0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8,
+ 0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF,
+ 0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F,
+ 0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3,
+ 0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02,
+ 0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03,
+ 0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39,
+ 0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00,
+ 0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC,
+ 0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05,
+ 0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05,
+ 0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40,
+ 0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71,
+ 0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04,
+ 0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9,
+ 0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4,
+ 0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E,
+ 0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF,
+ 0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70,
+ 0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE,
+ 0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31,
+ 0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24,
+ 0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7,
+ 0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00,
+ 0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68,
+ 0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00,
+ 0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51,
+ 0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D,
+ 0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1,
+ 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE,
+ 0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0,
+ 0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF,
+ 0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4,
+ 0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3,
+ 0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02,
+ 0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02,
+ 0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41,
+ 0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00,
+ 0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84,
+ 0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06,
+ 0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D,
+ 0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9,
+ 0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8,
+ 0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D,
+ 0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04,
+ 0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD,
+ 0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5,
+ 0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D,
+ 0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF,
+ 0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42,
+ 0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD,
+ 0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20,
+ 0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22,
+ 0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07,
+ 0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00,
+ 0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24,
+ 0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00,
+ 0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C,
+ 0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F,
+ 0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB,
+ 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE,
+ 0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC,
+ 0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF,
+ 0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79,
+ 0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3,
+ 0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03,
+ 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02,
+ 0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40,
+ 0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00,
+ 0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C,
+ 0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06,
+ 0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E,
+ 0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9,
+ 0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2,
+ 0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0,
+ 0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04,
+ 0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5,
+ 0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F,
+ 0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF,
+ 0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16,
+ 0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD,
+ 0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10,
+ 0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20,
+ 0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17,
+ 0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+ 0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD,
+ 0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00,
+ 0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7,
+ 0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31,
+ 0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5,
+ 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF,
+ 0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C,
+ 0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF,
+ 0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F,
+ 0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2,
+ 0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03,
+ 0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02,
+ 0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35,
+ 0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00,
+ 0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3,
+ 0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06,
+ 0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F,
+ 0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA,
+ 0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2,
+ 0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A,
+ 0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05,
+ 0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6,
+ 0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34,
+ 0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF,
+ 0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED,
+ 0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD,
+ 0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00,
+ 0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D,
+ 0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26,
+ 0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00,
+ 0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F,
+ 0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00,
+ 0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1,
+ 0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32,
+ 0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0,
+ 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF,
+ 0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41,
+ 0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF,
+ 0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6,
+ 0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2,
+ 0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04,
+ 0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01,
+ 0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20,
+ 0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00,
+ 0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8,
+ 0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06,
+ 0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10,
+ 0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB,
+ 0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5,
+ 0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00,
+ 0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1,
+ 0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC,
+ 0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6,
+ 0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D,
+ 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF,
+ 0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8,
+ 0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD,
+ 0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2,
+ 0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B,
+ 0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33,
+ 0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00,
+ 0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB,
+ 0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01,
+ 0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9,
+ 0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34,
+ 0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA,
+ 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF,
+ 0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B,
+ 0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF,
+ 0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F,
+ 0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2,
+ 0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05,
+ 0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01,
+ 0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02,
+ 0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00,
+ 0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A,
+ 0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06,
+ 0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10,
+ 0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC,
+ 0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE,
+ 0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00,
+ 0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2,
+ 0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC,
+ 0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7,
+ 0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09,
+ 0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB,
+ 0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD,
+ 0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4,
+ 0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19,
+ 0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F,
+ 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00,
+ 0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C,
+ 0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01,
+ 0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20,
+ 0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36,
+ 0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5,
+ 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF,
+ 0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB,
+ 0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF,
+ 0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A,
+ 0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3,
+ 0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06,
+ 0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01,
+ 0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9,
+ 0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00,
+ 0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09,
+ 0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06,
+ 0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11,
+ 0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC,
+ 0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC,
+ 0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00,
+ 0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34,
+ 0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC,
+ 0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B,
+ 0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8,
+ 0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8,
+ 0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95,
+ 0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD,
+ 0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7,
+ 0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17,
+ 0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A,
+ 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00,
+ 0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87,
+ 0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01,
+ 0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45,
+ 0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37,
+ 0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0,
+ 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF,
+ 0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61,
+ 0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF,
+ 0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8,
+ 0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3,
+ 0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06,
+ 0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00,
+ 0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7,
+ 0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00,
+ 0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3,
+ 0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06,
+ 0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11,
+ 0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD,
+ 0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F,
+ 0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00,
+ 0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87,
+ 0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC,
+ 0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96,
+ 0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08,
+ 0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D,
+ 0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89,
+ 0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD,
+ 0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB,
+ 0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15,
+ 0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53,
+ 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF,
+ 0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5,
+ 0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01,
+ 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69,
+ 0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39,
+ 0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B,
+ 0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF,
+ 0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF,
+ 0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF,
+ 0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79,
+ 0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3,
+ 0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07,
+ 0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00,
+ 0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A,
+ 0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00,
+ 0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58,
+ 0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06,
+ 0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10,
+ 0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE,
+ 0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27,
+ 0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00,
+ 0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE,
+ 0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC,
+ 0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92,
+ 0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06,
+ 0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56,
+ 0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88,
+ 0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD,
+ 0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0,
+ 0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13,
+ 0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A,
+ 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF,
+ 0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6,
+ 0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01,
+ 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A,
+ 0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A,
+ 0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97,
+ 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00,
+ 0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93,
+ 0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF,
+ 0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D,
+ 0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3,
+ 0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08,
+ 0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00,
+ 0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23,
+ 0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00,
+ 0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7,
+ 0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05,
+ 0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10,
+ 0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE,
+ 0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45,
+ 0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00,
+ 0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69,
+ 0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC,
+ 0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F,
+ 0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04,
+ 0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D,
+ 0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
+ 0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94,
+ 0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC,
+ 0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6,
+ 0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11,
+ 0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F,
+ 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF,
+ 0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB,
+ 0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01,
+ 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9,
+ 0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C,
+ 0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93,
+ 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00,
+ 0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F,
+ 0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF,
+ 0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4,
+ 0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4,
+ 0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09,
+ 0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF,
+ 0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1,
+ 0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00,
+ 0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90,
+ 0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05,
+ 0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F,
+ 0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF,
+ 0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67,
+ 0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00,
+ 0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8,
+ 0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD,
+ 0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C,
+ 0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02,
+ 0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41,
+ 0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF,
+ 0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A,
+ 0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01,
+ 0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD,
+ 0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F,
+ 0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63,
+ 0x01, 0x8D, 0xFF, 0x0F, 0x00
+};
+
+static u16
+coefficient_sizes[8 * 2] = {
+ /* Playback */
+ 0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000,
+ /* capture */
+ 0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000,
+};
+
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
new file mode 100644
index 00000000000..b96acd5a57d
--- /dev/null
+++ b/sound/pci/rme32.c
@@ -0,0 +1,2043 @@
+/*
+ * ALSA driver for RME Digi32, Digi32/8 and Digi32 PRO audio interfaces
+ *
+ * Copyright (c) 2002-2004 Martin Langer <martin-langer@gmx.de>,
+ * Pilo Chambert <pilo.c@wanadoo.fr>
+ *
+ * Thanks to : Anders Torger <torger@ludd.luth.se>,
+ * Henk Hesselink <henk@anda.nl>
+ * for writing the digi96-driver
+ * and RME for all informations.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * ****************************************************************************
+ *
+ * Note #1 "Sek'd models" ................................... martin 2002-12-07
+ *
+ * Identical soundcards by Sek'd were labeled:
+ * RME Digi 32 = Sek'd Prodif 32
+ * RME Digi 32 Pro = Sek'd Prodif 96
+ * RME Digi 32/8 = Sek'd Prodif Gold
+ *
+ * ****************************************************************************
+ *
+ * Note #2 "full duplex mode" ............................... martin 2002-12-07
+ *
+ * Full duplex doesn't work. All cards (32, 32/8, 32Pro) are working identical
+ * in this mode. Rec data and play data are using the same buffer therefore. At
+ * first you have got the playing bits in the buffer and then (after playing
+ * them) they were overwitten by the captured sound of the CS8412/14. Both
+ * modes (play/record) are running harmonically hand in hand in the same buffer
+ * and you have only one start bit plus one interrupt bit to control this
+ * paired action.
+ * This is opposite to the latter rme96 where playing and capturing is totally
+ * separated and so their full duplex mode is supported by alsa (using two
+ * start bits and two interrupts for two different buffers).
+ * But due to the wrong sequence of playing and capturing ALSA shows no solved
+ * full duplex support for the rme32 at the moment. That's bad, but I'm not
+ * able to solve it. Are you motivated enough to solve this problem now? Your
+ * patch would be welcome!
+ *
+ * ****************************************************************************
+ *
+ * "The story after the long seeking" -- tiwai
+ *
+ * Ok, the situation regarding the full duplex is now improved a bit.
+ * In the fullduplex mode (given by the module parameter), the hardware buffer
+ * is split to halves for read and write directions at the DMA pointer.
+ * That is, the half above the current DMA pointer is used for write, and
+ * the half below is used for read. To mangle this strange behavior, an
+ * software intermediate buffer is introduced. This is, of course, not good
+ * from the viewpoint of the data transfer efficiency. However, this allows
+ * you to use arbitrary buffer sizes, instead of the fixed I/O buffer size.
+ *
+ * ****************************************************************************
+ */
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm-indirect.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int fullduplex[SNDRV_CARDS]; // = {[0 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for RME Digi32 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for RME Digi32 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable RME Digi32 soundcard.");
+module_param_array(fullduplex, bool, NULL, 0444);
+MODULE_PARM_DESC(fullduplex, "Support full-duplex mode.");
+MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>, Pilo Chambert <pilo.c@wanadoo.fr>");
+MODULE_DESCRIPTION("RME Digi32, Digi32/8, Digi32 PRO");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{RME,Digi32}," "{RME,Digi32/8}," "{RME,Digi32 PRO}}");
+
+/* Defines for RME Digi32 series */
+#define RME32_SPDIF_NCHANNELS 2
+
+/* Playback and capture buffer size */
+#define RME32_BUFFER_SIZE 0x20000
+
+/* IO area size */
+#define RME32_IO_SIZE 0x30000
+
+/* IO area offsets */
+#define RME32_IO_DATA_BUFFER 0x0
+#define RME32_IO_CONTROL_REGISTER 0x20000
+#define RME32_IO_GET_POS 0x20000
+#define RME32_IO_CONFIRM_ACTION_IRQ 0x20004
+#define RME32_IO_RESET_POS 0x20100
+
+/* Write control register bits */
+#define RME32_WCR_START (1 << 0) /* startbit */
+#define RME32_WCR_MONO (1 << 1) /* 0=stereo, 1=mono
+ Setting the whole card to mono
+ doesn't seem to be very useful.
+ A software-solution can handle
+ full-duplex with one direction in
+ stereo and the other way in mono.
+ So, the hardware should work all
+ the time in stereo! */
+#define RME32_WCR_MODE24 (1 << 2) /* 0=16bit, 1=32bit */
+#define RME32_WCR_SEL (1 << 3) /* 0=input on output, 1=normal playback/capture */
+#define RME32_WCR_FREQ_0 (1 << 4) /* frequency (play) */
+#define RME32_WCR_FREQ_1 (1 << 5)
+#define RME32_WCR_INP_0 (1 << 6) /* input switch */
+#define RME32_WCR_INP_1 (1 << 7)
+#define RME32_WCR_RESET (1 << 8) /* Reset address */
+#define RME32_WCR_MUTE (1 << 9) /* digital mute for output */
+#define RME32_WCR_PRO (1 << 10) /* 1=professional, 0=consumer */
+#define RME32_WCR_DS_BM (1 << 11) /* 1=DoubleSpeed (only PRO-Version); 1=BlockMode (only Adat-Version) */
+#define RME32_WCR_ADAT (1 << 12) /* Adat Mode (only Adat-Version) */
+#define RME32_WCR_AUTOSYNC (1 << 13) /* AutoSync */
+#define RME32_WCR_PD (1 << 14) /* DAC Reset (only PRO-Version) */
+#define RME32_WCR_EMP (1 << 15) /* 1=Emphasis on (only PRO-Version) */
+
+#define RME32_WCR_BITPOS_FREQ_0 4
+#define RME32_WCR_BITPOS_FREQ_1 5
+#define RME32_WCR_BITPOS_INP_0 6
+#define RME32_WCR_BITPOS_INP_1 7
+
+/* Read control register bits */
+#define RME32_RCR_AUDIO_ADDR_MASK 0x1ffff
+#define RME32_RCR_LOCK (1 << 23) /* 1=locked, 0=not locked */
+#define RME32_RCR_ERF (1 << 26) /* 1=Error, 0=no Error */
+#define RME32_RCR_FREQ_0 (1 << 27) /* CS841x frequency (record) */
+#define RME32_RCR_FREQ_1 (1 << 28)
+#define RME32_RCR_FREQ_2 (1 << 29)
+#define RME32_RCR_KMODE (1 << 30) /* card mode: 1=PLL, 0=quartz */
+#define RME32_RCR_IRQ (1 << 31) /* interrupt */
+
+#define RME32_RCR_BITPOS_F0 27
+#define RME32_RCR_BITPOS_F1 28
+#define RME32_RCR_BITPOS_F2 29
+
+/* Input types */
+#define RME32_INPUT_OPTICAL 0
+#define RME32_INPUT_COAXIAL 1
+#define RME32_INPUT_INTERNAL 2
+#define RME32_INPUT_XLR 3
+
+/* Clock modes */
+#define RME32_CLOCKMODE_SLAVE 0
+#define RME32_CLOCKMODE_MASTER_32 1
+#define RME32_CLOCKMODE_MASTER_44 2
+#define RME32_CLOCKMODE_MASTER_48 3
+
+/* Block sizes in bytes */
+#define RME32_BLOCK_SIZE 8192
+
+/* Software intermediate buffer (max) size */
+#define RME32_MID_BUFFER_SIZE (1024*1024)
+
+/* Hardware revisions */
+#define RME32_32_REVISION 192
+#define RME32_328_REVISION_OLD 100
+#define RME32_328_REVISION_NEW 101
+#define RME32_PRO_REVISION_WITH_8412 192
+#define RME32_PRO_REVISION_WITH_8414 150
+
+
+/* PCI vendor/device ID's */
+#ifndef PCI_VENDOR_ID_XILINX_RME
+# define PCI_VENDOR_ID_XILINX_RME 0xea60
+#endif
+#ifndef PCI_DEVICE_ID_DIGI32
+# define PCI_DEVICE_ID_DIGI32 0x9896
+#endif
+#ifndef PCI_DEVICE_ID_DIGI32_PRO
+# define PCI_DEVICE_ID_DIGI32_PRO 0x9897
+#endif
+#ifndef PCI_DEVICE_ID_DIGI32_8
+# define PCI_DEVICE_ID_DIGI32_8 0x9898
+#endif
+
+typedef struct snd_rme32 {
+ spinlock_t lock;
+ int irq;
+ unsigned long port;
+ void __iomem *iobase;
+
+ u32 wcreg; /* cached write control register value */
+ u32 wcreg_spdif; /* S/PDIF setup */
+ u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */
+ u32 rcreg; /* cached read control register value */
+
+ u8 rev; /* card revision number */
+
+ snd_pcm_substream_t *playback_substream;
+ snd_pcm_substream_t *capture_substream;
+
+ int playback_frlog; /* log2 of framesize */
+ int capture_frlog;
+
+ size_t playback_periodsize; /* in bytes, zero if not used */
+ size_t capture_periodsize; /* in bytes, zero if not used */
+
+ unsigned int fullduplex_mode;
+ int running;
+
+ snd_pcm_indirect_t playback_pcm;
+ snd_pcm_indirect_t capture_pcm;
+
+ snd_card_t *card;
+ snd_pcm_t *spdif_pcm;
+ snd_pcm_t *adat_pcm;
+ struct pci_dev *pci;
+ snd_kcontrol_t *spdif_ctl;
+} rme32_t;
+
+static struct pci_device_id snd_rme32_ids[] = {
+ {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+ {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+ {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32_PRO,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme32_ids);
+
+#define RME32_ISWORKING(rme32) ((rme32)->wcreg & RME32_WCR_START)
+#define RME32_PRO_WITH_8414(rme32) ((rme32)->pci->device == PCI_DEVICE_ID_DIGI32_PRO && (rme32)->rev == RME32_PRO_REVISION_WITH_8414)
+
+static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream);
+
+static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream);
+
+static int snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd);
+
+static void snd_rme32_proc_init(rme32_t * rme32);
+
+static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32);
+
+static inline unsigned int snd_rme32_pcm_byteptr(rme32_t * rme32)
+{
+ return (readl(rme32->iobase + RME32_IO_GET_POS)
+ & RME32_RCR_AUDIO_ADDR_MASK);
+}
+
+static int snd_rme32_ratecode(int rate)
+{
+ switch (rate) {
+ case 32000: return SNDRV_PCM_RATE_32000;
+ case 44100: return SNDRV_PCM_RATE_44100;
+ case 48000: return SNDRV_PCM_RATE_48000;
+ case 64000: return SNDRV_PCM_RATE_64000;
+ case 88200: return SNDRV_PCM_RATE_88200;
+ case 96000: return SNDRV_PCM_RATE_96000;
+ }
+ return 0;
+}
+
+/* silence callback for halfduplex mode */
+static int snd_rme32_playback_silence(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ count <<= rme32->playback_frlog;
+ pos <<= rme32->playback_frlog;
+ memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
+ return 0;
+}
+
+/* copy callback for halfduplex mode */
+static int snd_rme32_playback_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *src, snd_pcm_uframes_t count)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ count <<= rme32->playback_frlog;
+ pos <<= rme32->playback_frlog;
+ if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+ src, count))
+ return -EFAULT;
+ return 0;
+}
+
+/* copy callback for halfduplex mode */
+static int snd_rme32_capture_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *dst, snd_pcm_uframes_t count)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ count <<= rme32->capture_frlog;
+ pos <<= rme32->capture_frlog;
+ if (copy_to_user_fromio(dst,
+ rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+ count))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * SPDIF I/O capabilites (half-duplex mode)
+ */
+static snd_pcm_hardware_t snd_rme32_spdif_info = {
+ .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = RME32_BUFFER_SIZE,
+ .period_bytes_min = RME32_BLOCK_SIZE,
+ .period_bytes_max = RME32_BLOCK_SIZE,
+ .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+ .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+/*
+ * ADAT I/O capabilites (half-duplex mode)
+ */
+static snd_pcm_hardware_t snd_rme32_adat_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats= SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 8,
+ .channels_max = 8,
+ .buffer_bytes_max = RME32_BUFFER_SIZE,
+ .period_bytes_min = RME32_BLOCK_SIZE,
+ .period_bytes_max = RME32_BLOCK_SIZE,
+ .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+ .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+/*
+ * SPDIF I/O capabilites (full-duplex mode)
+ */
+static snd_pcm_hardware_t snd_rme32_spdif_fd_info = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = RME32_MID_BUFFER_SIZE,
+ .period_bytes_min = RME32_BLOCK_SIZE,
+ .period_bytes_max = RME32_BLOCK_SIZE,
+ .periods_min = 2,
+ .periods_max = RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+/*
+ * ADAT I/O capabilites (full-duplex mode)
+ */
+static snd_pcm_hardware_t snd_rme32_adat_fd_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats= SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 8,
+ .channels_max = 8,
+ .buffer_bytes_max = RME32_MID_BUFFER_SIZE,
+ .period_bytes_min = RME32_BLOCK_SIZE,
+ .period_bytes_max = RME32_BLOCK_SIZE,
+ .periods_min = 2,
+ .periods_max = RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+static void snd_rme32_reset_dac(rme32_t *rme32)
+{
+ writel(rme32->wcreg | RME32_WCR_PD,
+ rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+}
+
+static int snd_rme32_playback_getrate(rme32_t * rme32)
+{
+ int rate;
+
+ rate = ((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_0) & 1) +
+ (((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_1) & 1) << 1);
+ switch (rate) {
+ case 1:
+ rate = 32000;
+ break;
+ case 2:
+ rate = 44100;
+ break;
+ case 3:
+ rate = 48000;
+ break;
+ default:
+ return -1;
+ }
+ return (rme32->wcreg & RME32_WCR_DS_BM) ? rate << 1 : rate;
+}
+
+static int snd_rme32_capture_getrate(rme32_t * rme32, int *is_adat)
+{
+ int n;
+
+ *is_adat = 0;
+ if (rme32->rcreg & RME32_RCR_LOCK) {
+ /* ADAT rate */
+ *is_adat = 1;
+ }
+ if (rme32->rcreg & RME32_RCR_ERF) {
+ return -1;
+ }
+
+ /* S/PDIF rate */
+ n = ((rme32->rcreg >> RME32_RCR_BITPOS_F0) & 1) +
+ (((rme32->rcreg >> RME32_RCR_BITPOS_F1) & 1) << 1) +
+ (((rme32->rcreg >> RME32_RCR_BITPOS_F2) & 1) << 2);
+
+ if (RME32_PRO_WITH_8414(rme32))
+ switch (n) { /* supporting the CS8414 */
+ case 0:
+ case 1:
+ case 2:
+ return -1;
+ case 3:
+ return 96000;
+ case 4:
+ return 88200;
+ case 5:
+ return 48000;
+ case 6:
+ return 44100;
+ case 7:
+ return 32000;
+ default:
+ return -1;
+ break;
+ }
+ else
+ switch (n) { /* supporting the CS8412 */
+ case 0:
+ return -1;
+ case 1:
+ return 48000;
+ case 2:
+ return 44100;
+ case 3:
+ return 32000;
+ case 4:
+ return 48000;
+ case 5:
+ return 44100;
+ case 6:
+ return 44056;
+ case 7:
+ return 32000;
+ default:
+ break;
+ }
+ return -1;
+}
+
+static int snd_rme32_playback_setrate(rme32_t * rme32, int rate)
+{
+ int ds;
+
+ ds = rme32->wcreg & RME32_WCR_DS_BM;
+ switch (rate) {
+ case 32000:
+ rme32->wcreg &= ~RME32_WCR_DS_BM;
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) &
+ ~RME32_WCR_FREQ_1;
+ break;
+ case 44100:
+ rme32->wcreg &= ~RME32_WCR_DS_BM;
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_1) &
+ ~RME32_WCR_FREQ_0;
+ break;
+ case 48000:
+ rme32->wcreg &= ~RME32_WCR_DS_BM;
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) |
+ RME32_WCR_FREQ_1;
+ break;
+ case 64000:
+ if (rme32->pci->device != PCI_DEVICE_ID_DIGI32_PRO)
+ return -EINVAL;
+ rme32->wcreg |= RME32_WCR_DS_BM;
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) &
+ ~RME32_WCR_FREQ_1;
+ break;
+ case 88200:
+ if (rme32->pci->device != PCI_DEVICE_ID_DIGI32_PRO)
+ return -EINVAL;
+ rme32->wcreg |= RME32_WCR_DS_BM;
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_1) &
+ ~RME32_WCR_FREQ_0;
+ break;
+ case 96000:
+ if (rme32->pci->device != PCI_DEVICE_ID_DIGI32_PRO)
+ return -EINVAL;
+ rme32->wcreg |= RME32_WCR_DS_BM;
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) |
+ RME32_WCR_FREQ_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if ((!ds && rme32->wcreg & RME32_WCR_DS_BM) ||
+ (ds && !(rme32->wcreg & RME32_WCR_DS_BM)))
+ {
+ /* change to/from double-speed: reset the DAC (if available) */
+ snd_rme32_reset_dac(rme32);
+ } else {
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ }
+ return 0;
+}
+
+static int snd_rme32_setclockmode(rme32_t * rme32, int mode)
+{
+ switch (mode) {
+ case RME32_CLOCKMODE_SLAVE:
+ /* AutoSync */
+ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) &
+ ~RME32_WCR_FREQ_1;
+ break;
+ case RME32_CLOCKMODE_MASTER_32:
+ /* Internal 32.0kHz */
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) &
+ ~RME32_WCR_FREQ_1;
+ break;
+ case RME32_CLOCKMODE_MASTER_44:
+ /* Internal 44.1kHz */
+ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) |
+ RME32_WCR_FREQ_1;
+ break;
+ case RME32_CLOCKMODE_MASTER_48:
+ /* Internal 48.0kHz */
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) |
+ RME32_WCR_FREQ_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static int snd_rme32_getclockmode(rme32_t * rme32)
+{
+ return ((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_0) & 1) +
+ (((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_1) & 1) << 1);
+}
+
+static int snd_rme32_setinputtype(rme32_t * rme32, int type)
+{
+ switch (type) {
+ case RME32_INPUT_OPTICAL:
+ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_INP_0) &
+ ~RME32_WCR_INP_1;
+ break;
+ case RME32_INPUT_COAXIAL:
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_INP_0) &
+ ~RME32_WCR_INP_1;
+ break;
+ case RME32_INPUT_INTERNAL:
+ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_INP_0) |
+ RME32_WCR_INP_1;
+ break;
+ case RME32_INPUT_XLR:
+ rme32->wcreg = (rme32->wcreg | RME32_WCR_INP_0) |
+ RME32_WCR_INP_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static int snd_rme32_getinputtype(rme32_t * rme32)
+{
+ return ((rme32->wcreg >> RME32_WCR_BITPOS_INP_0) & 1) +
+ (((rme32->wcreg >> RME32_WCR_BITPOS_INP_1) & 1) << 1);
+}
+
+static void
+snd_rme32_setframelog(rme32_t * rme32, int n_channels, int is_playback)
+{
+ int frlog;
+
+ if (n_channels == 2) {
+ frlog = 1;
+ } else {
+ /* assume 8 channels */
+ frlog = 3;
+ }
+ if (is_playback) {
+ frlog += (rme32->wcreg & RME32_WCR_MODE24) ? 2 : 1;
+ rme32->playback_frlog = frlog;
+ } else {
+ frlog += (rme32->wcreg & RME32_WCR_MODE24) ? 2 : 1;
+ rme32->capture_frlog = frlog;
+ }
+}
+
+static int snd_rme32_setformat(rme32_t * rme32, int format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rme32->wcreg &= ~RME32_WCR_MODE24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rme32->wcreg |= RME32_WCR_MODE24;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static int
+snd_rme32_playback_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * params)
+{
+ int err, rate, dummy;
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (rme32->fullduplex_mode) {
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (err < 0)
+ return err;
+ } else {
+ runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER);
+ runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
+ runtime->dma_bytes = RME32_BUFFER_SIZE;
+ }
+
+ spin_lock_irq(&rme32->lock);
+ if ((rme32->rcreg & RME32_RCR_KMODE) &&
+ (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
+ /* AutoSync */
+ if ((int)params_rate(params) != rate) {
+ spin_unlock_irq(&rme32->lock);
+ return -EIO;
+ }
+ } else if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) {
+ spin_unlock_irq(&rme32->lock);
+ return err;
+ }
+ if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
+ spin_unlock_irq(&rme32->lock);
+ return err;
+ }
+
+ snd_rme32_setframelog(rme32, params_channels(params), 1);
+ if (rme32->capture_periodsize != 0) {
+ if (params_period_size(params) << rme32->playback_frlog != rme32->capture_periodsize) {
+ spin_unlock_irq(&rme32->lock);
+ return -EBUSY;
+ }
+ }
+ rme32->playback_periodsize = params_period_size(params) << rme32->playback_frlog;
+ /* S/PDIF setup */
+ if ((rme32->wcreg & RME32_WCR_ADAT) == 0) {
+ rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP);
+ rme32->wcreg |= rme32->wcreg_spdif_stream;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ }
+ spin_unlock_irq(&rme32->lock);
+
+ return 0;
+}
+
+static int
+snd_rme32_capture_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * params)
+{
+ int err, isadat, rate;
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (rme32->fullduplex_mode) {
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (err < 0)
+ return err;
+ } else {
+ runtime->dma_area = (void *)rme32->iobase + RME32_IO_DATA_BUFFER;
+ runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
+ runtime->dma_bytes = RME32_BUFFER_SIZE;
+ }
+
+ spin_lock_irq(&rme32->lock);
+ /* enable AutoSync for record-preparing */
+ rme32->wcreg |= RME32_WCR_AUTOSYNC;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+ if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
+ spin_unlock_irq(&rme32->lock);
+ return err;
+ }
+ if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) {
+ spin_unlock_irq(&rme32->lock);
+ return err;
+ }
+ if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
+ if ((int)params_rate(params) != rate) {
+ spin_unlock_irq(&rme32->lock);
+ return -EIO;
+ }
+ if ((isadat && runtime->hw.channels_min == 2) ||
+ (!isadat && runtime->hw.channels_min == 8)) {
+ spin_unlock_irq(&rme32->lock);
+ return -EIO;
+ }
+ }
+ /* AutoSync off for recording */
+ rme32->wcreg &= ~RME32_WCR_AUTOSYNC;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+ snd_rme32_setframelog(rme32, params_channels(params), 0);
+ if (rme32->playback_periodsize != 0) {
+ if (params_period_size(params) << rme32->capture_frlog !=
+ rme32->playback_periodsize) {
+ spin_unlock_irq(&rme32->lock);
+ return -EBUSY;
+ }
+ }
+ rme32->capture_periodsize =
+ params_period_size(params) << rme32->capture_frlog;
+ spin_unlock_irq(&rme32->lock);
+
+ return 0;
+}
+
+static int snd_rme32_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ if (! rme32->fullduplex_mode)
+ return 0;
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static void snd_rme32_pcm_start(rme32_t * rme32, int from_pause)
+{
+ if (!from_pause) {
+ writel(0, rme32->iobase + RME32_IO_RESET_POS);
+ }
+
+ rme32->wcreg |= RME32_WCR_START;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+}
+
+static void snd_rme32_pcm_stop(rme32_t * rme32, int to_pause)
+{
+ /*
+ * Check if there is an unconfirmed IRQ, if so confirm it, or else
+ * the hardware will not stop generating interrupts
+ */
+ rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ if (rme32->rcreg & RME32_RCR_IRQ) {
+ writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ);
+ }
+ rme32->wcreg &= ~RME32_WCR_START;
+ if (rme32->wcreg & RME32_WCR_SEL)
+ rme32->wcreg |= RME32_WCR_MUTE;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ if (! to_pause)
+ writel(0, rme32->iobase + RME32_IO_RESET_POS);
+}
+
+static irqreturn_t
+snd_rme32_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ rme32_t *rme32 = (rme32_t *) dev_id;
+
+ rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ if (!(rme32->rcreg & RME32_RCR_IRQ)) {
+ return IRQ_NONE;
+ } else {
+ if (rme32->capture_substream) {
+ snd_pcm_period_elapsed(rme32->capture_substream);
+ }
+ if (rme32->playback_substream) {
+ snd_pcm_period_elapsed(rme32->playback_substream);
+ }
+ writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ);
+ }
+ return IRQ_HANDLED;
+}
+
+static unsigned int period_bytes[] = { RME32_BLOCK_SIZE };
+
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+ .count = ARRAY_SIZE(period_bytes),
+ .list = period_bytes,
+ .mask = 0
+};
+
+static void snd_rme32_set_buffer_constraint(rme32_t *rme32, snd_pcm_runtime_t *runtime)
+{
+ if (! rme32->fullduplex_mode) {
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ &hw_constraints_period_bytes);
+ }
+}
+
+static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream)
+{
+ int rate, dummy;
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_pcm_set_sync(substream);
+
+ spin_lock_irq(&rme32->lock);
+ if (rme32->playback_substream != NULL) {
+ spin_unlock_irq(&rme32->lock);
+ return -EBUSY;
+ }
+ rme32->wcreg &= ~RME32_WCR_ADAT;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ rme32->playback_substream = substream;
+ spin_unlock_irq(&rme32->lock);
+
+ if (rme32->fullduplex_mode)
+ runtime->hw = snd_rme32_spdif_fd_info;
+ else
+ runtime->hw = snd_rme32_spdif_info;
+ if (rme32->pci->device == PCI_DEVICE_ID_DIGI32_PRO) {
+ runtime->hw.rates |= SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000;
+ runtime->hw.rate_max = 96000;
+ }
+ if ((rme32->rcreg & RME32_RCR_KMODE) &&
+ (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
+ /* AutoSync */
+ runtime->hw.rates = snd_rme32_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+
+ snd_rme32_set_buffer_constraint(rme32, runtime);
+
+ rme32->wcreg_spdif_stream = rme32->wcreg_spdif;
+ rme32->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &rme32->spdif_ctl->id);
+ return 0;
+}
+
+static int snd_rme32_capture_spdif_open(snd_pcm_substream_t * substream)
+{
+ int isadat, rate;
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_pcm_set_sync(substream);
+
+ spin_lock_irq(&rme32->lock);
+ if (rme32->capture_substream != NULL) {
+ spin_unlock_irq(&rme32->lock);
+ return -EBUSY;
+ }
+ rme32->capture_substream = substream;
+ spin_unlock_irq(&rme32->lock);
+
+ if (rme32->fullduplex_mode)
+ runtime->hw = snd_rme32_spdif_fd_info;
+ else
+ runtime->hw = snd_rme32_spdif_info;
+ if (RME32_PRO_WITH_8414(rme32)) {
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000;
+ runtime->hw.rate_max = 96000;
+ }
+ if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
+ if (isadat) {
+ return -EIO;
+ }
+ runtime->hw.rates = snd_rme32_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+
+ snd_rme32_set_buffer_constraint(rme32, runtime);
+
+ return 0;
+}
+
+static int
+snd_rme32_playback_adat_open(snd_pcm_substream_t *substream)
+{
+ int rate, dummy;
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_pcm_set_sync(substream);
+
+ spin_lock_irq(&rme32->lock);
+ if (rme32->playback_substream != NULL) {
+ spin_unlock_irq(&rme32->lock);
+ return -EBUSY;
+ }
+ rme32->wcreg |= RME32_WCR_ADAT;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ rme32->playback_substream = substream;
+ spin_unlock_irq(&rme32->lock);
+
+ if (rme32->fullduplex_mode)
+ runtime->hw = snd_rme32_adat_fd_info;
+ else
+ runtime->hw = snd_rme32_adat_info;
+ if ((rme32->rcreg & RME32_RCR_KMODE) &&
+ (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
+ /* AutoSync */
+ runtime->hw.rates = snd_rme32_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+
+ snd_rme32_set_buffer_constraint(rme32, runtime);
+ return 0;
+}
+
+static int
+snd_rme32_capture_adat_open(snd_pcm_substream_t *substream)
+{
+ int isadat, rate;
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (rme32->fullduplex_mode)
+ runtime->hw = snd_rme32_adat_fd_info;
+ else
+ runtime->hw = snd_rme32_adat_info;
+ if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
+ if (!isadat) {
+ return -EIO;
+ }
+ runtime->hw.rates = snd_rme32_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+
+ spin_lock_irq(&rme32->lock);
+ if (rme32->capture_substream != NULL) {
+ spin_unlock_irq(&rme32->lock);
+ return -EBUSY;
+ }
+ rme32->capture_substream = substream;
+ spin_unlock_irq(&rme32->lock);
+
+ snd_rme32_set_buffer_constraint(rme32, runtime);
+ return 0;
+}
+
+static int snd_rme32_playback_close(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ int spdif = 0;
+
+ spin_lock_irq(&rme32->lock);
+ rme32->playback_substream = NULL;
+ rme32->playback_periodsize = 0;
+ spdif = (rme32->wcreg & RME32_WCR_ADAT) == 0;
+ spin_unlock_irq(&rme32->lock);
+ if (spdif) {
+ rme32->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &rme32->spdif_ctl->id);
+ }
+ return 0;
+}
+
+static int snd_rme32_capture_close(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme32->lock);
+ rme32->capture_substream = NULL;
+ rme32->capture_periodsize = 0;
+ spin_unlock(&rme32->lock);
+ return 0;
+}
+
+static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme32->lock);
+ if (rme32->fullduplex_mode) {
+ memset(&rme32->playback_pcm, 0, sizeof(rme32->playback_pcm));
+ rme32->playback_pcm.hw_buffer_size = RME32_BUFFER_SIZE;
+ rme32->playback_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ } else {
+ writel(0, rme32->iobase + RME32_IO_RESET_POS);
+ }
+ if (rme32->wcreg & RME32_WCR_SEL)
+ rme32->wcreg &= ~RME32_WCR_MUTE;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ spin_unlock_irq(&rme32->lock);
+ return 0;
+}
+
+static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme32->lock);
+ if (rme32->fullduplex_mode) {
+ memset(&rme32->capture_pcm, 0, sizeof(rme32->capture_pcm));
+ rme32->capture_pcm.hw_buffer_size = RME32_BUFFER_SIZE;
+ rme32->capture_pcm.hw_queue_size = RME32_BUFFER_SIZE / 2;
+ rme32->capture_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ } else {
+ writel(0, rme32->iobase + RME32_IO_RESET_POS);
+ }
+ spin_unlock_irq(&rme32->lock);
+ return 0;
+}
+
+static int
+snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+
+ spin_lock(&rme32->lock);
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s != rme32->playback_substream &&
+ s != rme32->capture_substream)
+ continue;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ rme32->running |= (1 << s->stream);
+ if (rme32->fullduplex_mode) {
+ /* remember the current DMA position */
+ if (s == rme32->playback_substream) {
+ rme32->playback_pcm.hw_io =
+ rme32->playback_pcm.hw_data = snd_rme32_pcm_byteptr(rme32);
+ } else {
+ rme32->capture_pcm.hw_io =
+ rme32->capture_pcm.hw_data = snd_rme32_pcm_byteptr(rme32);
+ }
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ rme32->running &= ~(1 << s->stream);
+ break;
+ }
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ /* prefill playback buffer */
+ if (cmd == SNDRV_PCM_TRIGGER_START && rme32->fullduplex_mode) {
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == rme32->playback_substream) {
+ s->ops->ack(s);
+ break;
+ }
+ }
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (rme32->running && ! RME32_ISWORKING(rme32))
+ snd_rme32_pcm_start(rme32, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (! rme32->running && RME32_ISWORKING(rme32))
+ snd_rme32_pcm_stop(rme32, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (rme32->running && RME32_ISWORKING(rme32))
+ snd_rme32_pcm_stop(rme32, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (rme32->running && ! RME32_ISWORKING(rme32))
+ snd_rme32_pcm_start(rme32, 1);
+ break;
+ }
+ spin_unlock(&rme32->lock);
+ return 0;
+}
+
+/* pointer callback for halfduplex mode */
+static snd_pcm_uframes_t
+snd_rme32_playback_pointer(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ return snd_rme32_pcm_byteptr(rme32) >> rme32->playback_frlog;
+}
+
+static snd_pcm_uframes_t
+snd_rme32_capture_pointer(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ return snd_rme32_pcm_byteptr(rme32) >> rme32->capture_frlog;
+}
+
+
+/* ack and pointer callbacks for fullduplex mode */
+static void snd_rme32_pb_trans_copy(snd_pcm_substream_t *substream,
+ snd_pcm_indirect_t *rec, size_t bytes)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data,
+ substream->runtime->dma_area + rec->sw_data, bytes);
+}
+
+static int snd_rme32_playback_fd_ack(snd_pcm_substream_t *substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_indirect_t *rec, *cprec;
+
+ rec = &rme32->playback_pcm;
+ cprec = &rme32->capture_pcm;
+ spin_lock(&rme32->lock);
+ rec->hw_queue_size = RME32_BUFFER_SIZE;
+ if (rme32->running & (1 << SNDRV_PCM_STREAM_CAPTURE))
+ rec->hw_queue_size -= cprec->hw_ready;
+ spin_unlock(&rme32->lock);
+ snd_pcm_indirect_playback_transfer(substream, rec,
+ snd_rme32_pb_trans_copy);
+ return 0;
+}
+
+static void snd_rme32_cp_trans_copy(snd_pcm_substream_t *substream,
+ snd_pcm_indirect_t *rec, size_t bytes)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ memcpy_fromio(substream->runtime->dma_area + rec->sw_data,
+ rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data,
+ bytes);
+}
+
+static int snd_rme32_capture_fd_ack(snd_pcm_substream_t *substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm,
+ snd_rme32_cp_trans_copy);
+ return 0;
+}
+
+static snd_pcm_uframes_t
+snd_rme32_playback_fd_pointer(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ return snd_pcm_indirect_playback_pointer(substream, &rme32->playback_pcm,
+ snd_rme32_pcm_byteptr(rme32));
+}
+
+static snd_pcm_uframes_t
+snd_rme32_capture_fd_pointer(snd_pcm_substream_t * substream)
+{
+ rme32_t *rme32 = snd_pcm_substream_chip(substream);
+ return snd_pcm_indirect_capture_pointer(substream, &rme32->capture_pcm,
+ snd_rme32_pcm_byteptr(rme32));
+}
+
+/* for halfduplex mode */
+static snd_pcm_ops_t snd_rme32_playback_spdif_ops = {
+ .open = snd_rme32_playback_spdif_open,
+ .close = snd_rme32_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_playback_hw_params,
+ .hw_free = snd_rme32_pcm_hw_free,
+ .prepare = snd_rme32_playback_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_playback_pointer,
+ .copy = snd_rme32_playback_copy,
+ .silence = snd_rme32_playback_silence,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static snd_pcm_ops_t snd_rme32_capture_spdif_ops = {
+ .open = snd_rme32_capture_spdif_open,
+ .close = snd_rme32_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_capture_hw_params,
+ .hw_free = snd_rme32_pcm_hw_free,
+ .prepare = snd_rme32_capture_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_capture_pointer,
+ .copy = snd_rme32_capture_copy,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static snd_pcm_ops_t snd_rme32_playback_adat_ops = {
+ .open = snd_rme32_playback_adat_open,
+ .close = snd_rme32_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_playback_hw_params,
+ .prepare = snd_rme32_playback_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_playback_pointer,
+ .copy = snd_rme32_playback_copy,
+ .silence = snd_rme32_playback_silence,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static snd_pcm_ops_t snd_rme32_capture_adat_ops = {
+ .open = snd_rme32_capture_adat_open,
+ .close = snd_rme32_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_capture_hw_params,
+ .prepare = snd_rme32_capture_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_capture_pointer,
+ .copy = snd_rme32_capture_copy,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+/* for fullduplex mode */
+static snd_pcm_ops_t snd_rme32_playback_spdif_fd_ops = {
+ .open = snd_rme32_playback_spdif_open,
+ .close = snd_rme32_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_playback_hw_params,
+ .hw_free = snd_rme32_pcm_hw_free,
+ .prepare = snd_rme32_playback_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_playback_fd_pointer,
+ .ack = snd_rme32_playback_fd_ack,
+};
+
+static snd_pcm_ops_t snd_rme32_capture_spdif_fd_ops = {
+ .open = snd_rme32_capture_spdif_open,
+ .close = snd_rme32_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_capture_hw_params,
+ .hw_free = snd_rme32_pcm_hw_free,
+ .prepare = snd_rme32_capture_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_capture_fd_pointer,
+ .ack = snd_rme32_capture_fd_ack,
+};
+
+static snd_pcm_ops_t snd_rme32_playback_adat_fd_ops = {
+ .open = snd_rme32_playback_adat_open,
+ .close = snd_rme32_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_playback_hw_params,
+ .prepare = snd_rme32_playback_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_playback_fd_pointer,
+ .ack = snd_rme32_playback_fd_ack,
+};
+
+static snd_pcm_ops_t snd_rme32_capture_adat_fd_ops = {
+ .open = snd_rme32_capture_adat_open,
+ .close = snd_rme32_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme32_capture_hw_params,
+ .prepare = snd_rme32_capture_prepare,
+ .trigger = snd_rme32_pcm_trigger,
+ .pointer = snd_rme32_capture_fd_pointer,
+ .ack = snd_rme32_capture_fd_ack,
+};
+
+static void snd_rme32_free(void *private_data)
+{
+ rme32_t *rme32 = (rme32_t *) private_data;
+
+ if (rme32 == NULL) {
+ return;
+ }
+ if (rme32->irq >= 0) {
+ snd_rme32_pcm_stop(rme32, 0);
+ free_irq(rme32->irq, (void *) rme32);
+ rme32->irq = -1;
+ }
+ if (rme32->iobase) {
+ iounmap(rme32->iobase);
+ rme32->iobase = NULL;
+ }
+ if (rme32->port) {
+ pci_release_regions(rme32->pci);
+ rme32->port = 0;
+ }
+ pci_disable_device(rme32->pci);
+}
+
+static void snd_rme32_free_spdif_pcm(snd_pcm_t * pcm)
+{
+ rme32_t *rme32 = (rme32_t *) pcm->private_data;
+ rme32->spdif_pcm = NULL;
+}
+
+static void
+snd_rme32_free_adat_pcm(snd_pcm_t *pcm)
+{
+ rme32_t *rme32 = (rme32_t *) pcm->private_data;
+ rme32->adat_pcm = NULL;
+}
+
+static int __devinit snd_rme32_create(rme32_t * rme32)
+{
+ struct pci_dev *pci = rme32->pci;
+ int err;
+
+ rme32->irq = -1;
+ spin_lock_init(&rme32->lock);
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ if ((err = pci_request_regions(pci, "RME32")) < 0)
+ return err;
+ rme32->port = pci_resource_start(rme32->pci, 0);
+
+ if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ rme32->irq = pci->irq;
+
+ if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) {
+ snd_printk("unable to remap memory region 0x%lx-0x%lx\n",
+ rme32->port, rme32->port + RME32_IO_SIZE - 1);
+ return -ENOMEM;
+ }
+
+ /* read the card's revision number */
+ pci_read_config_byte(pci, 8, &rme32->rev);
+
+ /* set up ALSA pcm device for S/PDIF */
+ if ((err = snd_pcm_new(rme32->card, "Digi32 IEC958", 0, 1, 1, &rme32->spdif_pcm)) < 0) {
+ return err;
+ }
+ rme32->spdif_pcm->private_data = rme32;
+ rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm;
+ strcpy(rme32->spdif_pcm->name, "Digi32 IEC958");
+ if (rme32->fullduplex_mode) {
+ snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_rme32_playback_spdif_fd_ops);
+ snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_rme32_capture_spdif_fd_ops);
+ snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 0, RME32_MID_BUFFER_SIZE);
+ rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+ } else {
+ snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_rme32_playback_spdif_ops);
+ snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_rme32_capture_spdif_ops);
+ rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+ }
+
+ /* set up ALSA pcm device for ADAT */
+ if ((pci->device == PCI_DEVICE_ID_DIGI32) ||
+ (pci->device == PCI_DEVICE_ID_DIGI32_PRO)) {
+ /* ADAT is not available on DIGI32 and DIGI32 Pro */
+ rme32->adat_pcm = NULL;
+ }
+ else {
+ if ((err = snd_pcm_new(rme32->card, "Digi32 ADAT", 1,
+ 1, 1, &rme32->adat_pcm)) < 0)
+ {
+ return err;
+ }
+ rme32->adat_pcm->private_data = rme32;
+ rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm;
+ strcpy(rme32->adat_pcm->name, "Digi32 ADAT");
+ if (rme32->fullduplex_mode) {
+ snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_rme32_playback_adat_fd_ops);
+ snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_rme32_capture_adat_fd_ops);
+ snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 0, RME32_MID_BUFFER_SIZE);
+ rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+ } else {
+ snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_rme32_playback_adat_ops);
+ snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_rme32_capture_adat_ops);
+ rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+ }
+ }
+
+
+ rme32->playback_periodsize = 0;
+ rme32->capture_periodsize = 0;
+
+ /* make sure playback/capture is stopped, if by some reason active */
+ snd_rme32_pcm_stop(rme32, 0);
+
+ /* reset DAC */
+ snd_rme32_reset_dac(rme32);
+
+ /* reset buffer pointer */
+ writel(0, rme32->iobase + RME32_IO_RESET_POS);
+
+ /* set default values in registers */
+ rme32->wcreg = RME32_WCR_SEL | /* normal playback */
+ RME32_WCR_INP_0 | /* input select */
+ RME32_WCR_MUTE; /* muting on */
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+
+ /* init switch interface */
+ if ((err = snd_rme32_create_switches(rme32->card, rme32)) < 0) {
+ return err;
+ }
+
+ /* init proc interface */
+ snd_rme32_proc_init(rme32);
+
+ rme32->capture_substream = NULL;
+ rme32->playback_substream = NULL;
+
+ return 0;
+}
+
+/*
+ * proc interface
+ */
+
+static void
+snd_rme32_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer)
+{
+ int n;
+ rme32_t *rme32 = (rme32_t *) entry->private_data;
+
+ rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+ snd_iprintf(buffer, rme32->card->longname);
+ snd_iprintf(buffer, " (index #%d)\n", rme32->card->number + 1);
+
+ snd_iprintf(buffer, "\nGeneral settings\n");
+ if (rme32->fullduplex_mode)
+ snd_iprintf(buffer, " Full-duplex mode\n");
+ else
+ snd_iprintf(buffer, " Half-duplex mode\n");
+ if (RME32_PRO_WITH_8414(rme32)) {
+ snd_iprintf(buffer, " receiver: CS8414\n");
+ } else {
+ snd_iprintf(buffer, " receiver: CS8412\n");
+ }
+ if (rme32->wcreg & RME32_WCR_MODE24) {
+ snd_iprintf(buffer, " format: 24 bit");
+ } else {
+ snd_iprintf(buffer, " format: 16 bit");
+ }
+ if (rme32->wcreg & RME32_WCR_MONO) {
+ snd_iprintf(buffer, ", Mono\n");
+ } else {
+ snd_iprintf(buffer, ", Stereo\n");
+ }
+
+ snd_iprintf(buffer, "\nInput settings\n");
+ switch (snd_rme32_getinputtype(rme32)) {
+ case RME32_INPUT_OPTICAL:
+ snd_iprintf(buffer, " input: optical");
+ break;
+ case RME32_INPUT_COAXIAL:
+ snd_iprintf(buffer, " input: coaxial");
+ break;
+ case RME32_INPUT_INTERNAL:
+ snd_iprintf(buffer, " input: internal");
+ break;
+ case RME32_INPUT_XLR:
+ snd_iprintf(buffer, " input: XLR");
+ break;
+ }
+ if (snd_rme32_capture_getrate(rme32, &n) < 0) {
+ snd_iprintf(buffer, "\n sample rate: no valid signal\n");
+ } else {
+ if (n) {
+ snd_iprintf(buffer, " (8 channels)\n");
+ } else {
+ snd_iprintf(buffer, " (2 channels)\n");
+ }
+ snd_iprintf(buffer, " sample rate: %d Hz\n",
+ snd_rme32_capture_getrate(rme32, &n));
+ }
+
+ snd_iprintf(buffer, "\nOutput settings\n");
+ if (rme32->wcreg & RME32_WCR_SEL) {
+ snd_iprintf(buffer, " output signal: normal playback");
+ } else {
+ snd_iprintf(buffer, " output signal: same as input");
+ }
+ if (rme32->wcreg & RME32_WCR_MUTE) {
+ snd_iprintf(buffer, " (muted)\n");
+ } else {
+ snd_iprintf(buffer, "\n");
+ }
+
+ /* master output frequency */
+ if (!
+ ((!(rme32->wcreg & RME32_WCR_FREQ_0))
+ && (!(rme32->wcreg & RME32_WCR_FREQ_1)))) {
+ snd_iprintf(buffer, " sample rate: %d Hz\n",
+ snd_rme32_playback_getrate(rme32));
+ }
+ if (rme32->rcreg & RME32_RCR_KMODE) {
+ snd_iprintf(buffer, " sample clock source: AutoSync\n");
+ } else {
+ snd_iprintf(buffer, " sample clock source: Internal\n");
+ }
+ if (rme32->wcreg & RME32_WCR_PRO) {
+ snd_iprintf(buffer, " format: AES/EBU (professional)\n");
+ } else {
+ snd_iprintf(buffer, " format: IEC958 (consumer)\n");
+ }
+ if (rme32->wcreg & RME32_WCR_EMP) {
+ snd_iprintf(buffer, " emphasis: on\n");
+ } else {
+ snd_iprintf(buffer, " emphasis: off\n");
+ }
+}
+
+static void __devinit snd_rme32_proc_init(rme32_t * rme32)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(rme32->card, "rme32", &entry))
+ snd_info_set_text_ops(entry, rme32, 1024, snd_rme32_proc_read);
+}
+
+/*
+ * control interface
+ */
+
+static int
+snd_rme32_info_loopback_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+static int
+snd_rme32_get_loopback_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme32->lock);
+ ucontrol->value.integer.value[0] =
+ rme32->wcreg & RME32_WCR_SEL ? 0 : 1;
+ spin_unlock_irq(&rme32->lock);
+ return 0;
+}
+static int
+snd_rme32_put_loopback_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ucontrol->value.integer.value[0] ? 0 : RME32_WCR_SEL;
+ spin_lock_irq(&rme32->lock);
+ val = (rme32->wcreg & ~RME32_WCR_SEL) | val;
+ change = val != rme32->wcreg;
+ if (ucontrol->value.integer.value[0])
+ val &= ~RME32_WCR_MUTE;
+ else
+ val |= RME32_WCR_MUTE;
+ rme32->wcreg = val;
+ writel(val, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ spin_unlock_irq(&rme32->lock);
+ return change;
+}
+
+static int
+snd_rme32_info_inputtype_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_info_t * uinfo)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+ static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ switch (rme32->pci->device) {
+ case PCI_DEVICE_ID_DIGI32:
+ case PCI_DEVICE_ID_DIGI32_8:
+ uinfo->value.enumerated.items = 3;
+ break;
+ case PCI_DEVICE_ID_DIGI32_PRO:
+ uinfo->value.enumerated.items = 4;
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ if (uinfo->value.enumerated.item >
+ uinfo->value.enumerated.items - 1) {
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ }
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+static int
+snd_rme32_get_inputtype_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+ unsigned int items = 3;
+
+ spin_lock_irq(&rme32->lock);
+ ucontrol->value.enumerated.item[0] = snd_rme32_getinputtype(rme32);
+
+ switch (rme32->pci->device) {
+ case PCI_DEVICE_ID_DIGI32:
+ case PCI_DEVICE_ID_DIGI32_8:
+ items = 3;
+ break;
+ case PCI_DEVICE_ID_DIGI32_PRO:
+ items = 4;
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ if (ucontrol->value.enumerated.item[0] >= items) {
+ ucontrol->value.enumerated.item[0] = items - 1;
+ }
+
+ spin_unlock_irq(&rme32->lock);
+ return 0;
+}
+static int
+snd_rme32_put_inputtype_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change, items = 3;
+
+ switch (rme32->pci->device) {
+ case PCI_DEVICE_ID_DIGI32:
+ case PCI_DEVICE_ID_DIGI32_8:
+ items = 3;
+ break;
+ case PCI_DEVICE_ID_DIGI32_PRO:
+ items = 4;
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ val = ucontrol->value.enumerated.item[0] % items;
+
+ spin_lock_irq(&rme32->lock);
+ change = val != (unsigned int)snd_rme32_getinputtype(rme32);
+ snd_rme32_setinputtype(rme32, val);
+ spin_unlock_irq(&rme32->lock);
+ return change;
+}
+
+static int
+snd_rme32_info_clockmode_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[4] = { "AutoSync",
+ "Internal 32.0kHz",
+ "Internal 44.1kHz",
+ "Internal 48.0kHz" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item > 3) {
+ uinfo->value.enumerated.item = 3;
+ }
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+static int
+snd_rme32_get_clockmode_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme32->lock);
+ ucontrol->value.enumerated.item[0] = snd_rme32_getclockmode(rme32);
+ spin_unlock_irq(&rme32->lock);
+ return 0;
+}
+static int
+snd_rme32_put_clockmode_control(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ucontrol->value.enumerated.item[0] % 3;
+ spin_lock_irq(&rme32->lock);
+ change = val != (unsigned int)snd_rme32_getclockmode(rme32);
+ snd_rme32_setclockmode(rme32, val);
+ spin_unlock_irq(&rme32->lock);
+ return change;
+}
+
+static u32 snd_rme32_convert_from_aes(snd_aes_iec958_t * aes)
+{
+ u32 val = 0;
+ val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME32_WCR_PRO : 0;
+ if (val & RME32_WCR_PRO)
+ val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME32_WCR_EMP : 0;
+ else
+ val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME32_WCR_EMP : 0;
+ return val;
+}
+
+static void snd_rme32_convert_to_aes(snd_aes_iec958_t * aes, u32 val)
+{
+ aes->status[0] = ((val & RME32_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0);
+ if (val & RME32_WCR_PRO)
+ aes->status[0] |= (val & RME32_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+ else
+ aes->status[0] |= (val & RME32_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme32_control_spdif_info(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme32_control_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+
+ snd_rme32_convert_to_aes(&ucontrol->value.iec958,
+ rme32->wcreg_spdif);
+ return 0;
+}
+
+static int snd_rme32_control_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_rme32_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&rme32->lock);
+ change = val != rme32->wcreg_spdif;
+ rme32->wcreg_spdif = val;
+ spin_unlock_irq(&rme32->lock);
+ return change;
+}
+
+static int snd_rme32_control_spdif_stream_info(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme32_control_spdif_stream_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t *
+ ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+
+ snd_rme32_convert_to_aes(&ucontrol->value.iec958,
+ rme32->wcreg_spdif_stream);
+ return 0;
+}
+
+static int snd_rme32_control_spdif_stream_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t *
+ ucontrol)
+{
+ rme32_t *rme32 = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_rme32_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&rme32->lock);
+ change = val != rme32->wcreg_spdif_stream;
+ rme32->wcreg_spdif_stream = val;
+ rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP);
+ rme32->wcreg |= val;
+ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+ spin_unlock_irq(&rme32->lock);
+ return change;
+}
+
+static int snd_rme32_control_spdif_mask_info(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme32_control_spdif_mask_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t *
+ ucontrol)
+{
+ ucontrol->value.iec958.status[0] = kcontrol->private_value;
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_rme32_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = snd_rme32_control_spdif_info,
+ .get = snd_rme32_control_spdif_get,
+ .put = snd_rme32_control_spdif_put
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+ .info = snd_rme32_control_spdif_stream_info,
+ .get = snd_rme32_control_spdif_stream_get,
+ .put = snd_rme32_control_spdif_stream_put
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
+ .info = snd_rme32_control_spdif_mask_info,
+ .get = snd_rme32_control_spdif_mask_get,
+ .private_value = IEC958_AES0_PROFESSIONAL | IEC958_AES0_CON_EMPHASIS
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
+ .info = snd_rme32_control_spdif_mask_info,
+ .get = snd_rme32_control_spdif_mask_get,
+ .private_value = IEC958_AES0_PROFESSIONAL | IEC958_AES0_PRO_EMPHASIS
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Connector",
+ .info = snd_rme32_info_inputtype_control,
+ .get = snd_rme32_get_inputtype_control,
+ .put = snd_rme32_put_inputtype_control
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Loopback Input",
+ .info = snd_rme32_info_loopback_control,
+ .get = snd_rme32_get_loopback_control,
+ .put = snd_rme32_put_loopback_control
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sample Clock Source",
+ .info = snd_rme32_info_clockmode_control,
+ .get = snd_rme32_get_clockmode_control,
+ .put = snd_rme32_put_clockmode_control
+ }
+};
+
+static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32)
+{
+ int idx, err;
+ snd_kcontrol_t *kctl;
+
+ for (idx = 0; idx < (int)ARRAY_SIZE(snd_rme32_controls); idx++) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32))) < 0)
+ return err;
+ if (idx == 1) /* IEC958 (S/PDIF) Stream */
+ rme32->spdif_ctl = kctl;
+ }
+
+ return 0;
+}
+
+/*
+ * Card initialisation
+ */
+
+static void snd_rme32_card_free(snd_card_t * card)
+{
+ snd_rme32_free(card->private_data);
+}
+
+static int __devinit
+snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ static int dev;
+ rme32_t *rme32;
+ snd_card_t *card;
+ int err;
+
+ if (dev >= SNDRV_CARDS) {
+ return -ENODEV;
+ }
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(rme32_t))) == NULL)
+ return -ENOMEM;
+ card->private_free = snd_rme32_card_free;
+ rme32 = (rme32_t *) card->private_data;
+ rme32->card = card;
+ rme32->pci = pci;
+ snd_card_set_dev(card, &pci->dev);
+ if (fullduplex[dev])
+ rme32->fullduplex_mode = 1;
+ if ((err = snd_rme32_create(rme32)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "Digi32");
+ switch (rme32->pci->device) {
+ case PCI_DEVICE_ID_DIGI32:
+ strcpy(card->shortname, "RME Digi32");
+ break;
+ case PCI_DEVICE_ID_DIGI32_8:
+ strcpy(card->shortname, "RME Digi32/8");
+ break;
+ case PCI_DEVICE_ID_DIGI32_PRO:
+ strcpy(card->shortname, "RME Digi32 PRO");
+ break;
+ }
+ sprintf(card->longname, "%s (Rev. %d) at 0x%lx, irq %d",
+ card->shortname, rme32->rev, rme32->port, rme32->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_rme32_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "RME Digi32",
+ .id_table = snd_rme32_ids,
+ .probe = snd_rme32_probe,
+ .remove = __devexit_p(snd_rme32_remove),
+};
+
+static int __init alsa_card_rme32_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_rme32_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_rme32_init)
+module_exit(alsa_card_rme32_exit)
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
new file mode 100644
index 00000000000..8e2666841d2
--- /dev/null
+++ b/sound/pci/rme96.c
@@ -0,0 +1,2449 @@
+/*
+ * ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio
+ * interfaces
+ *
+ * Copyright (c) 2000, 2001 Anders Torger <torger@ludd.luth.se>
+ *
+ * Thanks to Henk Hesselink <henk@anda.nl> for the analog volume control
+ * code.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+/* note, two last pcis should be equal, it is not a bug */
+
+MODULE_AUTHOR("Anders Torger <torger@ludd.luth.se>");
+MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, "
+ "Digi96/8 PAD");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{RME,Digi96},"
+ "{RME,Digi96/8},"
+ "{RME,Digi96/8 PRO},"
+ "{RME,Digi96/8 PST},"
+ "{RME,Digi96/8 PAD}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for RME Digi96 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for RME Digi96 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");
+
+/*
+ * Defines for RME Digi96 series, from internal RME reference documents
+ * dated 12.01.00
+ */
+
+#define RME96_SPDIF_NCHANNELS 2
+
+/* Playback and capture buffer size */
+#define RME96_BUFFER_SIZE 0x10000
+
+/* IO area size */
+#define RME96_IO_SIZE 0x60000
+
+/* IO area offsets */
+#define RME96_IO_PLAY_BUFFER 0x0
+#define RME96_IO_REC_BUFFER 0x10000
+#define RME96_IO_CONTROL_REGISTER 0x20000
+#define RME96_IO_ADDITIONAL_REG 0x20004
+#define RME96_IO_CONFIRM_PLAY_IRQ 0x20008
+#define RME96_IO_CONFIRM_REC_IRQ 0x2000C
+#define RME96_IO_SET_PLAY_POS 0x40000
+#define RME96_IO_RESET_PLAY_POS 0x4FFFC
+#define RME96_IO_SET_REC_POS 0x50000
+#define RME96_IO_RESET_REC_POS 0x5FFFC
+#define RME96_IO_GET_PLAY_POS 0x20000
+#define RME96_IO_GET_REC_POS 0x30000
+
+/* Write control register bits */
+#define RME96_WCR_START (1 << 0)
+#define RME96_WCR_START_2 (1 << 1)
+#define RME96_WCR_GAIN_0 (1 << 2)
+#define RME96_WCR_GAIN_1 (1 << 3)
+#define RME96_WCR_MODE24 (1 << 4)
+#define RME96_WCR_MODE24_2 (1 << 5)
+#define RME96_WCR_BM (1 << 6)
+#define RME96_WCR_BM_2 (1 << 7)
+#define RME96_WCR_ADAT (1 << 8)
+#define RME96_WCR_FREQ_0 (1 << 9)
+#define RME96_WCR_FREQ_1 (1 << 10)
+#define RME96_WCR_DS (1 << 11)
+#define RME96_WCR_PRO (1 << 12)
+#define RME96_WCR_EMP (1 << 13)
+#define RME96_WCR_SEL (1 << 14)
+#define RME96_WCR_MASTER (1 << 15)
+#define RME96_WCR_PD (1 << 16)
+#define RME96_WCR_INP_0 (1 << 17)
+#define RME96_WCR_INP_1 (1 << 18)
+#define RME96_WCR_THRU_0 (1 << 19)
+#define RME96_WCR_THRU_1 (1 << 20)
+#define RME96_WCR_THRU_2 (1 << 21)
+#define RME96_WCR_THRU_3 (1 << 22)
+#define RME96_WCR_THRU_4 (1 << 23)
+#define RME96_WCR_THRU_5 (1 << 24)
+#define RME96_WCR_THRU_6 (1 << 25)
+#define RME96_WCR_THRU_7 (1 << 26)
+#define RME96_WCR_DOLBY (1 << 27)
+#define RME96_WCR_MONITOR_0 (1 << 28)
+#define RME96_WCR_MONITOR_1 (1 << 29)
+#define RME96_WCR_ISEL (1 << 30)
+#define RME96_WCR_IDIS (1 << 31)
+
+#define RME96_WCR_BITPOS_GAIN_0 2
+#define RME96_WCR_BITPOS_GAIN_1 3
+#define RME96_WCR_BITPOS_FREQ_0 9
+#define RME96_WCR_BITPOS_FREQ_1 10
+#define RME96_WCR_BITPOS_INP_0 17
+#define RME96_WCR_BITPOS_INP_1 18
+#define RME96_WCR_BITPOS_MONITOR_0 28
+#define RME96_WCR_BITPOS_MONITOR_1 29
+
+/* Read control register bits */
+#define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF
+#define RME96_RCR_IRQ_2 (1 << 16)
+#define RME96_RCR_T_OUT (1 << 17)
+#define RME96_RCR_DEV_ID_0 (1 << 21)
+#define RME96_RCR_DEV_ID_1 (1 << 22)
+#define RME96_RCR_LOCK (1 << 23)
+#define RME96_RCR_VERF (1 << 26)
+#define RME96_RCR_F0 (1 << 27)
+#define RME96_RCR_F1 (1 << 28)
+#define RME96_RCR_F2 (1 << 29)
+#define RME96_RCR_AUTOSYNC (1 << 30)
+#define RME96_RCR_IRQ (1 << 31)
+
+#define RME96_RCR_BITPOS_F0 27
+#define RME96_RCR_BITPOS_F1 28
+#define RME96_RCR_BITPOS_F2 29
+
+/* Additonal register bits */
+#define RME96_AR_WSEL (1 << 0)
+#define RME96_AR_ANALOG (1 << 1)
+#define RME96_AR_FREQPAD_0 (1 << 2)
+#define RME96_AR_FREQPAD_1 (1 << 3)
+#define RME96_AR_FREQPAD_2 (1 << 4)
+#define RME96_AR_PD2 (1 << 5)
+#define RME96_AR_DAC_EN (1 << 6)
+#define RME96_AR_CLATCH (1 << 7)
+#define RME96_AR_CCLK (1 << 8)
+#define RME96_AR_CDATA (1 << 9)
+
+#define RME96_AR_BITPOS_F0 2
+#define RME96_AR_BITPOS_F1 3
+#define RME96_AR_BITPOS_F2 4
+
+/* Monitor tracks */
+#define RME96_MONITOR_TRACKS_1_2 0
+#define RME96_MONITOR_TRACKS_3_4 1
+#define RME96_MONITOR_TRACKS_5_6 2
+#define RME96_MONITOR_TRACKS_7_8 3
+
+/* Attenuation */
+#define RME96_ATTENUATION_0 0
+#define RME96_ATTENUATION_6 1
+#define RME96_ATTENUATION_12 2
+#define RME96_ATTENUATION_18 3
+
+/* Input types */
+#define RME96_INPUT_OPTICAL 0
+#define RME96_INPUT_COAXIAL 1
+#define RME96_INPUT_INTERNAL 2
+#define RME96_INPUT_XLR 3
+#define RME96_INPUT_ANALOG 4
+
+/* Clock modes */
+#define RME96_CLOCKMODE_SLAVE 0
+#define RME96_CLOCKMODE_MASTER 1
+#define RME96_CLOCKMODE_WORDCLOCK 2
+
+/* Block sizes in bytes */
+#define RME96_SMALL_BLOCK_SIZE 2048
+#define RME96_LARGE_BLOCK_SIZE 8192
+
+/* Volume control */
+#define RME96_AD1852_VOL_BITS 14
+#define RME96_AD1855_VOL_BITS 10
+
+/*
+ * PCI vendor/device ids, could in the future be defined in <linux/pci.h>,
+ * therefore #ifndef is used.
+ */
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96
+#define PCI_DEVICE_ID_DIGI96 0x3fc0
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8
+#define PCI_DEVICE_ID_DIGI96_8 0x3fc1
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8_PRO
+#define PCI_DEVICE_ID_DIGI96_8_PRO 0x3fc2
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST
+#define PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST 0x3fc3
+#endif
+
+typedef struct snd_rme96 {
+ spinlock_t lock;
+ int irq;
+ unsigned long port;
+ void __iomem *iobase;
+
+ u32 wcreg; /* cached write control register value */
+ u32 wcreg_spdif; /* S/PDIF setup */
+ u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */
+ u32 rcreg; /* cached read control register value */
+ u32 areg; /* cached additional register value */
+ u16 vol[2]; /* cached volume of analog output */
+
+ u8 rev; /* card revision number */
+
+ snd_pcm_substream_t *playback_substream;
+ snd_pcm_substream_t *capture_substream;
+
+ int playback_frlog; /* log2 of framesize */
+ int capture_frlog;
+
+ size_t playback_periodsize; /* in bytes, zero if not used */
+ size_t capture_periodsize; /* in bytes, zero if not used */
+
+ snd_card_t *card;
+ snd_pcm_t *spdif_pcm;
+ snd_pcm_t *adat_pcm;
+ struct pci_dev *pci;
+ snd_kcontrol_t *spdif_ctl;
+} rme96_t;
+
+static struct pci_device_id snd_rme96_ids[] = {
+ { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PRO,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme96_ids);
+
+#define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START)
+#define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2)
+#define RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST)
+#define RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO || \
+ (rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST)
+#define RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4)
+#define RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \
+ ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO && (rme96)->rev == 2))
+#define RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1)
+
+static int
+snd_rme96_playback_prepare(snd_pcm_substream_t *substream);
+
+static int
+snd_rme96_capture_prepare(snd_pcm_substream_t *substream);
+
+static int
+snd_rme96_playback_trigger(snd_pcm_substream_t *substream,
+ int cmd);
+
+static int
+snd_rme96_capture_trigger(snd_pcm_substream_t *substream,
+ int cmd);
+
+static snd_pcm_uframes_t
+snd_rme96_playback_pointer(snd_pcm_substream_t *substream);
+
+static snd_pcm_uframes_t
+snd_rme96_capture_pointer(snd_pcm_substream_t *substream);
+
+static void __devinit
+snd_rme96_proc_init(rme96_t *rme96);
+
+static int
+snd_rme96_create_switches(snd_card_t *card,
+ rme96_t *rme96);
+
+static int
+snd_rme96_getinputtype(rme96_t *rme96);
+
+static inline unsigned int
+snd_rme96_playback_ptr(rme96_t *rme96)
+{
+ return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
+ & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog;
+}
+
+static inline unsigned int
+snd_rme96_capture_ptr(rme96_t *rme96)
+{
+ return (readl(rme96->iobase + RME96_IO_GET_REC_POS)
+ & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog;
+}
+
+static int
+snd_rme96_ratecode(int rate)
+{
+ switch (rate) {
+ case 32000: return SNDRV_PCM_RATE_32000;
+ case 44100: return SNDRV_PCM_RATE_44100;
+ case 48000: return SNDRV_PCM_RATE_48000;
+ case 64000: return SNDRV_PCM_RATE_64000;
+ case 88200: return SNDRV_PCM_RATE_88200;
+ case 96000: return SNDRV_PCM_RATE_96000;
+ }
+ return 0;
+}
+
+static int
+snd_rme96_playback_silence(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ count <<= rme96->playback_frlog;
+ pos <<= rme96->playback_frlog;
+ memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
+ 0, count);
+ return 0;
+}
+
+static int
+snd_rme96_playback_copy(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *src,
+ snd_pcm_uframes_t count)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ count <<= rme96->playback_frlog;
+ pos <<= rme96->playback_frlog;
+ copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src,
+ count);
+ return 0;
+}
+
+static int
+snd_rme96_capture_copy(snd_pcm_substream_t *substream,
+ int channel, /* not used (interleaved data) */
+ snd_pcm_uframes_t pos,
+ void __user *dst,
+ snd_pcm_uframes_t count)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ count <<= rme96->capture_frlog;
+ pos <<= rme96->capture_frlog;
+ copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos,
+ count);
+ return 0;
+}
+
+/*
+ * Digital output capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme96_playback_spdif_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = RME96_BUFFER_SIZE,
+ .period_bytes_min = RME96_SMALL_BLOCK_SIZE,
+ .period_bytes_max = RME96_LARGE_BLOCK_SIZE,
+ .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+ .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+/*
+ * Digital input capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_spdif_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = RME96_BUFFER_SIZE,
+ .period_bytes_min = RME96_SMALL_BLOCK_SIZE,
+ .period_bytes_max = RME96_LARGE_BLOCK_SIZE,
+ .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+ .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+/*
+ * Digital output capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_playback_adat_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 8,
+ .channels_max = 8,
+ .buffer_bytes_max = RME96_BUFFER_SIZE,
+ .period_bytes_min = RME96_SMALL_BLOCK_SIZE,
+ .period_bytes_max = RME96_LARGE_BLOCK_SIZE,
+ .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+ .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+/*
+ * Digital input capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_adat_info =
+{
+ .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 8,
+ .channels_max = 8,
+ .buffer_bytes_max = RME96_BUFFER_SIZE,
+ .period_bytes_min = RME96_SMALL_BLOCK_SIZE,
+ .period_bytes_max = RME96_LARGE_BLOCK_SIZE,
+ .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+ .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+ .fifo_size = 0,
+};
+
+/*
+ * The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface
+ * of the AD1852 or AD1852 D/A converter on the board. CDATA must be set up
+ * on the falling edge of CCLK and be stable on the rising edge. The rising
+ * edge of CLATCH after the last data bit clocks in the whole data word.
+ * A fast processor could probably drive the SPI interface faster than the
+ * DAC can handle (3MHz for the 1855, unknown for the 1852). The udelay(1)
+ * limits the data rate to 500KHz and only causes a delay of 33 microsecs.
+ *
+ * NOTE: increased delay from 1 to 10, since there where problems setting
+ * the volume.
+ */
+static void
+snd_rme96_write_SPI(rme96_t *rme96, u16 val)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ if (val & 0x8000) {
+ rme96->areg |= RME96_AR_CDATA;
+ } else {
+ rme96->areg &= ~RME96_AR_CDATA;
+ }
+ rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH);
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ udelay(10);
+ rme96->areg |= RME96_AR_CCLK;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ udelay(10);
+ val <<= 1;
+ }
+ rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA);
+ rme96->areg |= RME96_AR_CLATCH;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ udelay(10);
+ rme96->areg &= ~RME96_AR_CLATCH;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+}
+
+static void
+snd_rme96_apply_dac_volume(rme96_t *rme96)
+{
+ if (RME96_DAC_IS_1852(rme96)) {
+ snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0);
+ snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2);
+ } else if (RME96_DAC_IS_1855(rme96)) {
+ snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000);
+ snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400);
+ }
+}
+
+static void
+snd_rme96_reset_dac(rme96_t *rme96)
+{
+ writel(rme96->wcreg | RME96_WCR_PD,
+ rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static int
+snd_rme96_getmontracks(rme96_t *rme96)
+{
+ return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) +
+ (((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1);
+}
+
+static int
+snd_rme96_setmontracks(rme96_t *rme96,
+ int montracks)
+{
+ if (montracks & 1) {
+ rme96->wcreg |= RME96_WCR_MONITOR_0;
+ } else {
+ rme96->wcreg &= ~RME96_WCR_MONITOR_0;
+ }
+ if (montracks & 2) {
+ rme96->wcreg |= RME96_WCR_MONITOR_1;
+ } else {
+ rme96->wcreg &= ~RME96_WCR_MONITOR_1;
+ }
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static int
+snd_rme96_getattenuation(rme96_t *rme96)
+{
+ return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) +
+ (((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1);
+}
+
+static int
+snd_rme96_setattenuation(rme96_t *rme96,
+ int attenuation)
+{
+ switch (attenuation) {
+ case 0:
+ rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) &
+ ~RME96_WCR_GAIN_1;
+ break;
+ case 1:
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) &
+ ~RME96_WCR_GAIN_1;
+ break;
+ case 2:
+ rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) |
+ RME96_WCR_GAIN_1;
+ break;
+ case 3:
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) |
+ RME96_WCR_GAIN_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static int
+snd_rme96_capture_getrate(rme96_t *rme96,
+ int *is_adat)
+{
+ int n, rate;
+
+ *is_adat = 0;
+ if (rme96->areg & RME96_AR_ANALOG) {
+ /* Analog input, overrides S/PDIF setting */
+ n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) +
+ (((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1);
+ switch (n) {
+ case 1:
+ rate = 32000;
+ break;
+ case 2:
+ rate = 44100;
+ break;
+ case 3:
+ rate = 48000;
+ break;
+ default:
+ return -1;
+ }
+ return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate;
+ }
+
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ if (rme96->rcreg & RME96_RCR_LOCK) {
+ /* ADAT rate */
+ *is_adat = 1;
+ if (rme96->rcreg & RME96_RCR_T_OUT) {
+ return 48000;
+ }
+ return 44100;
+ }
+
+ if (rme96->rcreg & RME96_RCR_VERF) {
+ return -1;
+ }
+
+ /* S/PDIF rate */
+ n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) +
+ (((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) +
+ (((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2);
+
+ switch (n) {
+ case 0:
+ if (rme96->rcreg & RME96_RCR_T_OUT) {
+ return 64000;
+ }
+ return -1;
+ case 3: return 96000;
+ case 4: return 88200;
+ case 5: return 48000;
+ case 6: return 44100;
+ case 7: return 32000;
+ default:
+ break;
+ }
+ return -1;
+}
+
+static int
+snd_rme96_playback_getrate(rme96_t *rme96)
+{
+ int rate, dummy;
+
+ if (!(rme96->wcreg & RME96_WCR_MASTER) &&
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
+ (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
+ {
+ /* slave clock */
+ return rate;
+ }
+ rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) +
+ (((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1);
+ switch (rate) {
+ case 1:
+ rate = 32000;
+ break;
+ case 2:
+ rate = 44100;
+ break;
+ case 3:
+ rate = 48000;
+ break;
+ default:
+ return -1;
+ }
+ return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate;
+}
+
+static int
+snd_rme96_playback_setrate(rme96_t *rme96,
+ int rate)
+{
+ int ds;
+
+ ds = rme96->wcreg & RME96_WCR_DS;
+ switch (rate) {
+ case 32000:
+ rme96->wcreg &= ~RME96_WCR_DS;
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
+ ~RME96_WCR_FREQ_1;
+ break;
+ case 44100:
+ rme96->wcreg &= ~RME96_WCR_DS;
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
+ ~RME96_WCR_FREQ_0;
+ break;
+ case 48000:
+ rme96->wcreg &= ~RME96_WCR_DS;
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
+ RME96_WCR_FREQ_1;
+ break;
+ case 64000:
+ rme96->wcreg |= RME96_WCR_DS;
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
+ ~RME96_WCR_FREQ_1;
+ break;
+ case 88200:
+ rme96->wcreg |= RME96_WCR_DS;
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
+ ~RME96_WCR_FREQ_0;
+ break;
+ case 96000:
+ rme96->wcreg |= RME96_WCR_DS;
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
+ RME96_WCR_FREQ_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if ((!ds && rme96->wcreg & RME96_WCR_DS) ||
+ (ds && !(rme96->wcreg & RME96_WCR_DS)))
+ {
+ /* change to/from double-speed: reset the DAC (if available) */
+ snd_rme96_reset_dac(rme96);
+ } else {
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ }
+ return 0;
+}
+
+static int
+snd_rme96_capture_analog_setrate(rme96_t *rme96,
+ int rate)
+{
+ switch (rate) {
+ case 32000:
+ rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
+ ~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+ break;
+ case 44100:
+ rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
+ RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+ break;
+ case 48000:
+ rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
+ RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+ break;
+ case 64000:
+ if (rme96->rev < 4) {
+ return -EINVAL;
+ }
+ rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
+ ~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+ break;
+ case 88200:
+ if (rme96->rev < 4) {
+ return -EINVAL;
+ }
+ rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
+ RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+ break;
+ case 96000:
+ rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
+ RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ return 0;
+}
+
+static int
+snd_rme96_setclockmode(rme96_t *rme96,
+ int mode)
+{
+ switch (mode) {
+ case RME96_CLOCKMODE_SLAVE:
+ /* AutoSync */
+ rme96->wcreg &= ~RME96_WCR_MASTER;
+ rme96->areg &= ~RME96_AR_WSEL;
+ break;
+ case RME96_CLOCKMODE_MASTER:
+ /* Internal */
+ rme96->wcreg |= RME96_WCR_MASTER;
+ rme96->areg &= ~RME96_AR_WSEL;
+ break;
+ case RME96_CLOCKMODE_WORDCLOCK:
+ /* Word clock is a master mode */
+ rme96->wcreg |= RME96_WCR_MASTER;
+ rme96->areg |= RME96_AR_WSEL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ return 0;
+}
+
+static int
+snd_rme96_getclockmode(rme96_t *rme96)
+{
+ if (rme96->areg & RME96_AR_WSEL) {
+ return RME96_CLOCKMODE_WORDCLOCK;
+ }
+ return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER :
+ RME96_CLOCKMODE_SLAVE;
+}
+
+static int
+snd_rme96_setinputtype(rme96_t *rme96,
+ int type)
+{
+ int n;
+
+ switch (type) {
+ case RME96_INPUT_OPTICAL:
+ rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) &
+ ~RME96_WCR_INP_1;
+ break;
+ case RME96_INPUT_COAXIAL:
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) &
+ ~RME96_WCR_INP_1;
+ break;
+ case RME96_INPUT_INTERNAL:
+ rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) |
+ RME96_WCR_INP_1;
+ break;
+ case RME96_INPUT_XLR:
+ if ((rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST &&
+ rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PRO) ||
+ (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST &&
+ rme96->rev > 4))
+ {
+ /* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */
+ return -EINVAL;
+ }
+ rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) |
+ RME96_WCR_INP_1;
+ break;
+ case RME96_INPUT_ANALOG:
+ if (!RME96_HAS_ANALOG_IN(rme96)) {
+ return -EINVAL;
+ }
+ rme96->areg |= RME96_AR_ANALOG;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ if (rme96->rev < 4) {
+ /*
+ * Revision less than 004 does not support 64 and
+ * 88.2 kHz
+ */
+ if (snd_rme96_capture_getrate(rme96, &n) == 88200) {
+ snd_rme96_capture_analog_setrate(rme96, 44100);
+ }
+ if (snd_rme96_capture_getrate(rme96, &n) == 64000) {
+ snd_rme96_capture_analog_setrate(rme96, 32000);
+ }
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) {
+ rme96->areg &= ~RME96_AR_ANALOG;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ }
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static int
+snd_rme96_getinputtype(rme96_t *rme96)
+{
+ if (rme96->areg & RME96_AR_ANALOG) {
+ return RME96_INPUT_ANALOG;
+ }
+ return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) +
+ (((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1);
+}
+
+static void
+snd_rme96_setframelog(rme96_t *rme96,
+ int n_channels,
+ int is_playback)
+{
+ int frlog;
+
+ if (n_channels == 2) {
+ frlog = 1;
+ } else {
+ /* assume 8 channels */
+ frlog = 3;
+ }
+ if (is_playback) {
+ frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1;
+ rme96->playback_frlog = frlog;
+ } else {
+ frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1;
+ rme96->capture_frlog = frlog;
+ }
+}
+
+static int
+snd_rme96_playback_setformat(rme96_t *rme96,
+ int format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rme96->wcreg &= ~RME96_WCR_MODE24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rme96->wcreg |= RME96_WCR_MODE24;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static int
+snd_rme96_capture_setformat(rme96_t *rme96,
+ int format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rme96->wcreg &= ~RME96_WCR_MODE24_2;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rme96->wcreg |= RME96_WCR_MODE24_2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
+}
+
+static void
+snd_rme96_set_period_properties(rme96_t *rme96,
+ size_t period_bytes)
+{
+ switch (period_bytes) {
+ case RME96_LARGE_BLOCK_SIZE:
+ rme96->wcreg &= ~RME96_WCR_ISEL;
+ break;
+ case RME96_SMALL_BLOCK_SIZE:
+ rme96->wcreg |= RME96_WCR_ISEL;
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ rme96->wcreg &= ~RME96_WCR_IDIS;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static int
+snd_rme96_playback_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *params)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err, rate, dummy;
+
+ runtime->dma_area = (void *)(rme96->iobase + RME96_IO_PLAY_BUFFER);
+ runtime->dma_addr = rme96->port + RME96_IO_PLAY_BUFFER;
+ runtime->dma_bytes = RME96_BUFFER_SIZE;
+
+ spin_lock_irq(&rme96->lock);
+ if (!(rme96->wcreg & RME96_WCR_MASTER) &&
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
+ (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
+ {
+ /* slave clock */
+ if ((int)params_rate(params) != rate) {
+ spin_unlock_irq(&rme96->lock);
+ return -EIO;
+ }
+ } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) {
+ spin_unlock_irq(&rme96->lock);
+ return err;
+ }
+ if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) {
+ spin_unlock_irq(&rme96->lock);
+ return err;
+ }
+ snd_rme96_setframelog(rme96, params_channels(params), 1);
+ if (rme96->capture_periodsize != 0) {
+ if (params_period_size(params) << rme96->playback_frlog !=
+ rme96->capture_periodsize)
+ {
+ spin_unlock_irq(&rme96->lock);
+ return -EBUSY;
+ }
+ }
+ rme96->playback_periodsize =
+ params_period_size(params) << rme96->playback_frlog;
+ snd_rme96_set_period_properties(rme96, rme96->playback_periodsize);
+ /* S/PDIF setup */
+ if ((rme96->wcreg & RME96_WCR_ADAT) == 0) {
+ rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
+ writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ }
+ spin_unlock_irq(&rme96->lock);
+
+ return 0;
+}
+
+static int
+snd_rme96_capture_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *params)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err, isadat, rate;
+
+ runtime->dma_area = (void *)(rme96->iobase + RME96_IO_REC_BUFFER);
+ runtime->dma_addr = rme96->port + RME96_IO_REC_BUFFER;
+ runtime->dma_bytes = RME96_BUFFER_SIZE;
+
+ spin_lock_irq(&rme96->lock);
+ if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) {
+ spin_unlock_irq(&rme96->lock);
+ return err;
+ }
+ if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
+ if ((err = snd_rme96_capture_analog_setrate(rme96,
+ params_rate(params))) < 0)
+ {
+ spin_unlock_irq(&rme96->lock);
+ return err;
+ }
+ } else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) {
+ if ((int)params_rate(params) != rate) {
+ spin_unlock_irq(&rme96->lock);
+ return -EIO;
+ }
+ if ((isadat && runtime->hw.channels_min == 2) ||
+ (!isadat && runtime->hw.channels_min == 8))
+ {
+ spin_unlock_irq(&rme96->lock);
+ return -EIO;
+ }
+ }
+ snd_rme96_setframelog(rme96, params_channels(params), 0);
+ if (rme96->playback_periodsize != 0) {
+ if (params_period_size(params) << rme96->capture_frlog !=
+ rme96->playback_periodsize)
+ {
+ spin_unlock_irq(&rme96->lock);
+ return -EBUSY;
+ }
+ }
+ rme96->capture_periodsize =
+ params_period_size(params) << rme96->capture_frlog;
+ snd_rme96_set_period_properties(rme96, rme96->capture_periodsize);
+ spin_unlock_irq(&rme96->lock);
+
+ return 0;
+}
+
+static void
+snd_rme96_playback_start(rme96_t *rme96,
+ int from_pause)
+{
+ if (!from_pause) {
+ writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+ }
+
+ rme96->wcreg |= RME96_WCR_START;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_capture_start(rme96_t *rme96,
+ int from_pause)
+{
+ if (!from_pause) {
+ writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+ }
+
+ rme96->wcreg |= RME96_WCR_START_2;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_playback_stop(rme96_t *rme96)
+{
+ /*
+ * Check if there is an unconfirmed IRQ, if so confirm it, or else
+ * the hardware will not stop generating interrupts
+ */
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ if (rme96->rcreg & RME96_RCR_IRQ) {
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+ }
+ rme96->wcreg &= ~RME96_WCR_START;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_capture_stop(rme96_t *rme96)
+{
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ if (rme96->rcreg & RME96_RCR_IRQ_2) {
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+ }
+ rme96->wcreg &= ~RME96_WCR_START_2;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static irqreturn_t
+snd_rme96_interrupt(int irq,
+ void *dev_id,
+ struct pt_regs *regs)
+{
+ rme96_t *rme96 = (rme96_t *)dev_id;
+
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ /* fastpath out, to ease interrupt sharing */
+ if (!((rme96->rcreg & RME96_RCR_IRQ) ||
+ (rme96->rcreg & RME96_RCR_IRQ_2)))
+ {
+ return IRQ_NONE;
+ }
+
+ if (rme96->rcreg & RME96_RCR_IRQ) {
+ /* playback */
+ snd_pcm_period_elapsed(rme96->playback_substream);
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+ }
+ if (rme96->rcreg & RME96_RCR_IRQ_2) {
+ /* capture */
+ snd_pcm_period_elapsed(rme96->capture_substream);
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+ }
+ return IRQ_HANDLED;
+}
+
+static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE };
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+ .count = ARRAY_SIZE(period_bytes),
+ .list = period_bytes,
+ .mask = 0
+};
+
+static int
+snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream)
+{
+ int rate, dummy;
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_pcm_set_sync(substream);
+
+ spin_lock_irq(&rme96->lock);
+ if (rme96->playback_substream != NULL) {
+ spin_unlock_irq(&rme96->lock);
+ return -EBUSY;
+ }
+ rme96->wcreg &= ~RME96_WCR_ADAT;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ rme96->playback_substream = substream;
+ spin_unlock_irq(&rme96->lock);
+
+ runtime->hw = snd_rme96_playback_spdif_info;
+ if (!(rme96->wcreg & RME96_WCR_MASTER) &&
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
+ (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
+ {
+ /* slave clock */
+ runtime->hw.rates = snd_rme96_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+
+ rme96->wcreg_spdif_stream = rme96->wcreg_spdif;
+ rme96->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
+ return 0;
+}
+
+static int
+snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream)
+{
+ int isadat, rate;
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_pcm_set_sync(substream);
+
+ runtime->hw = snd_rme96_capture_spdif_info;
+ if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
+ (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0)
+ {
+ if (isadat) {
+ return -EIO;
+ }
+ runtime->hw.rates = snd_rme96_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+
+ spin_lock_irq(&rme96->lock);
+ if (rme96->capture_substream != NULL) {
+ spin_unlock_irq(&rme96->lock);
+ return -EBUSY;
+ }
+ rme96->capture_substream = substream;
+ spin_unlock_irq(&rme96->lock);
+
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+
+ return 0;
+}
+
+static int
+snd_rme96_playback_adat_open(snd_pcm_substream_t *substream)
+{
+ int rate, dummy;
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_pcm_set_sync(substream);
+
+ spin_lock_irq(&rme96->lock);
+ if (rme96->playback_substream != NULL) {
+ spin_unlock_irq(&rme96->lock);
+ return -EBUSY;
+ }
+ rme96->wcreg |= RME96_WCR_ADAT;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ rme96->playback_substream = substream;
+ spin_unlock_irq(&rme96->lock);
+
+ runtime->hw = snd_rme96_playback_adat_info;
+ if (!(rme96->wcreg & RME96_WCR_MASTER) &&
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
+ (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
+ {
+ /* slave clock */
+ runtime->hw.rates = snd_rme96_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+ return 0;
+}
+
+static int
+snd_rme96_capture_adat_open(snd_pcm_substream_t *substream)
+{
+ int isadat, rate;
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_pcm_set_sync(substream);
+
+ runtime->hw = snd_rme96_capture_adat_info;
+ if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
+ /* makes no sense to use analog input. Note that analog
+ expension cards AEB4/8-I are RME96_INPUT_INTERNAL */
+ return -EIO;
+ }
+ if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) {
+ if (!isadat) {
+ return -EIO;
+ }
+ runtime->hw.rates = snd_rme96_ratecode(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+
+ spin_lock_irq(&rme96->lock);
+ if (rme96->capture_substream != NULL) {
+ spin_unlock_irq(&rme96->lock);
+ return -EBUSY;
+ }
+ rme96->capture_substream = substream;
+ spin_unlock_irq(&rme96->lock);
+
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+ return 0;
+}
+
+static int
+snd_rme96_playback_close(snd_pcm_substream_t *substream)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ int spdif = 0;
+
+ spin_lock_irq(&rme96->lock);
+ if (RME96_ISPLAYING(rme96)) {
+ snd_rme96_playback_stop(rme96);
+ }
+ rme96->playback_substream = NULL;
+ rme96->playback_periodsize = 0;
+ spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0;
+ spin_unlock_irq(&rme96->lock);
+ if (spdif) {
+ rme96->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
+ }
+ return 0;
+}
+
+static int
+snd_rme96_capture_close(snd_pcm_substream_t *substream)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme96->lock);
+ if (RME96_ISRECORDING(rme96)) {
+ snd_rme96_capture_stop(rme96);
+ }
+ rme96->capture_substream = NULL;
+ rme96->capture_periodsize = 0;
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+
+static int
+snd_rme96_playback_prepare(snd_pcm_substream_t *substream)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme96->lock);
+ if (RME96_ISPLAYING(rme96)) {
+ snd_rme96_playback_stop(rme96);
+ }
+ writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+
+static int
+snd_rme96_capture_prepare(snd_pcm_substream_t *substream)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme96->lock);
+ if (RME96_ISRECORDING(rme96)) {
+ snd_rme96_capture_stop(rme96);
+ }
+ writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+
+static int
+snd_rme96_playback_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (!RME96_ISPLAYING(rme96)) {
+ if (substream != rme96->playback_substream) {
+ return -EBUSY;
+ }
+ snd_rme96_playback_start(rme96, 0);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (RME96_ISPLAYING(rme96)) {
+ if (substream != rme96->playback_substream) {
+ return -EBUSY;
+ }
+ snd_rme96_playback_stop(rme96);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (RME96_ISPLAYING(rme96)) {
+ snd_rme96_playback_stop(rme96);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!RME96_ISPLAYING(rme96)) {
+ snd_rme96_playback_start(rme96, 1);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int
+snd_rme96_capture_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (!RME96_ISRECORDING(rme96)) {
+ if (substream != rme96->capture_substream) {
+ return -EBUSY;
+ }
+ snd_rme96_capture_start(rme96, 0);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (RME96_ISRECORDING(rme96)) {
+ if (substream != rme96->capture_substream) {
+ return -EBUSY;
+ }
+ snd_rme96_capture_stop(rme96);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (RME96_ISRECORDING(rme96)) {
+ snd_rme96_capture_stop(rme96);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!RME96_ISRECORDING(rme96)) {
+ snd_rme96_capture_start(rme96, 1);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+snd_rme96_playback_pointer(snd_pcm_substream_t *substream)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ return snd_rme96_playback_ptr(rme96);
+}
+
+static snd_pcm_uframes_t
+snd_rme96_capture_pointer(snd_pcm_substream_t *substream)
+{
+ rme96_t *rme96 = snd_pcm_substream_chip(substream);
+ return snd_rme96_capture_ptr(rme96);
+}
+
+static snd_pcm_ops_t snd_rme96_playback_spdif_ops = {
+ .open = snd_rme96_playback_spdif_open,
+ .close = snd_rme96_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme96_playback_hw_params,
+ .prepare = snd_rme96_playback_prepare,
+ .trigger = snd_rme96_playback_trigger,
+ .pointer = snd_rme96_playback_pointer,
+ .copy = snd_rme96_playback_copy,
+ .silence = snd_rme96_playback_silence,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_spdif_ops = {
+ .open = snd_rme96_capture_spdif_open,
+ .close = snd_rme96_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme96_capture_hw_params,
+ .prepare = snd_rme96_capture_prepare,
+ .trigger = snd_rme96_capture_trigger,
+ .pointer = snd_rme96_capture_pointer,
+ .copy = snd_rme96_capture_copy,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static snd_pcm_ops_t snd_rme96_playback_adat_ops = {
+ .open = snd_rme96_playback_adat_open,
+ .close = snd_rme96_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme96_playback_hw_params,
+ .prepare = snd_rme96_playback_prepare,
+ .trigger = snd_rme96_playback_trigger,
+ .pointer = snd_rme96_playback_pointer,
+ .copy = snd_rme96_playback_copy,
+ .silence = snd_rme96_playback_silence,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_adat_ops = {
+ .open = snd_rme96_capture_adat_open,
+ .close = snd_rme96_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_rme96_capture_hw_params,
+ .prepare = snd_rme96_capture_prepare,
+ .trigger = snd_rme96_capture_trigger,
+ .pointer = snd_rme96_capture_pointer,
+ .copy = snd_rme96_capture_copy,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static void
+snd_rme96_free(void *private_data)
+{
+ rme96_t *rme96 = (rme96_t *)private_data;
+
+ if (rme96 == NULL) {
+ return;
+ }
+ if (rme96->irq >= 0) {
+ snd_rme96_playback_stop(rme96);
+ snd_rme96_capture_stop(rme96);
+ rme96->areg &= ~RME96_AR_DAC_EN;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ free_irq(rme96->irq, (void *)rme96);
+ rme96->irq = -1;
+ }
+ if (rme96->iobase) {
+ iounmap(rme96->iobase);
+ rme96->iobase = NULL;
+ }
+ if (rme96->port) {
+ pci_release_regions(rme96->pci);
+ rme96->port = 0;
+ }
+ pci_disable_device(rme96->pci);
+}
+
+static void
+snd_rme96_free_spdif_pcm(snd_pcm_t *pcm)
+{
+ rme96_t *rme96 = (rme96_t *) pcm->private_data;
+ rme96->spdif_pcm = NULL;
+}
+
+static void
+snd_rme96_free_adat_pcm(snd_pcm_t *pcm)
+{
+ rme96_t *rme96 = (rme96_t *) pcm->private_data;
+ rme96->adat_pcm = NULL;
+}
+
+static int __devinit
+snd_rme96_create(rme96_t *rme96)
+{
+ struct pci_dev *pci = rme96->pci;
+ int err;
+
+ rme96->irq = -1;
+ spin_lock_init(&rme96->lock);
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ if ((err = pci_request_regions(pci, "RME96")) < 0)
+ return err;
+ rme96->port = pci_resource_start(rme96->pci, 0);
+
+ if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ rme96->irq = pci->irq;
+
+ if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) {
+ snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
+ return -ENOMEM;
+ }
+
+ /* read the card's revision number */
+ pci_read_config_byte(pci, 8, &rme96->rev);
+
+ /* set up ALSA pcm device for S/PDIF */
+ if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0,
+ 1, 1, &rme96->spdif_pcm)) < 0)
+ {
+ return err;
+ }
+ rme96->spdif_pcm->private_data = rme96;
+ rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm;
+ strcpy(rme96->spdif_pcm->name, "Digi96 IEC958");
+ snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops);
+ snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops);
+
+ rme96->spdif_pcm->info_flags = 0;
+
+ /* set up ALSA pcm device for ADAT */
+ if (pci->device == PCI_DEVICE_ID_DIGI96) {
+ /* ADAT is not available on the base model */
+ rme96->adat_pcm = NULL;
+ } else {
+ if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1,
+ 1, 1, &rme96->adat_pcm)) < 0)
+ {
+ return err;
+ }
+ rme96->adat_pcm->private_data = rme96;
+ rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm;
+ strcpy(rme96->adat_pcm->name, "Digi96 ADAT");
+ snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops);
+ snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops);
+
+ rme96->adat_pcm->info_flags = 0;
+ }
+
+ rme96->playback_periodsize = 0;
+ rme96->capture_periodsize = 0;
+
+ /* make sure playback/capture is stopped, if by some reason active */
+ snd_rme96_playback_stop(rme96);
+ snd_rme96_capture_stop(rme96);
+
+ /* set default values in registers */
+ rme96->wcreg =
+ RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */
+ RME96_WCR_SEL | /* normal playback */
+ RME96_WCR_MASTER | /* set to master clock mode */
+ RME96_WCR_INP_0; /* set coaxial input */
+
+ rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */
+
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+ /* reset the ADC */
+ writel(rme96->areg | RME96_AR_PD2,
+ rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+ /* reset and enable the DAC (order is important). */
+ snd_rme96_reset_dac(rme96);
+ rme96->areg |= RME96_AR_DAC_EN;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+ /* reset playback and record buffer pointers */
+ writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+ writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+
+ /* reset volume */
+ rme96->vol[0] = rme96->vol[1] = 0;
+ if (RME96_HAS_ANALOG_OUT(rme96)) {
+ snd_rme96_apply_dac_volume(rme96);
+ }
+
+ /* init switch interface */
+ if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) {
+ return err;
+ }
+
+ /* init proc interface */
+ snd_rme96_proc_init(rme96);
+
+ return 0;
+}
+
+/*
+ * proc interface
+ */
+
+static void
+snd_rme96_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ int n;
+ rme96_t *rme96 = (rme96_t *)entry->private_data;
+
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+
+ snd_iprintf(buffer, rme96->card->longname);
+ snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1);
+
+ snd_iprintf(buffer, "\nGeneral settings\n");
+ if (rme96->wcreg & RME96_WCR_IDIS) {
+ snd_iprintf(buffer, " period size: N/A (interrupts "
+ "disabled)\n");
+ } else if (rme96->wcreg & RME96_WCR_ISEL) {
+ snd_iprintf(buffer, " period size: 2048 bytes\n");
+ } else {
+ snd_iprintf(buffer, " period size: 8192 bytes\n");
+ }
+ snd_iprintf(buffer, "\nInput settings\n");
+ switch (snd_rme96_getinputtype(rme96)) {
+ case RME96_INPUT_OPTICAL:
+ snd_iprintf(buffer, " input: optical");
+ break;
+ case RME96_INPUT_COAXIAL:
+ snd_iprintf(buffer, " input: coaxial");
+ break;
+ case RME96_INPUT_INTERNAL:
+ snd_iprintf(buffer, " input: internal");
+ break;
+ case RME96_INPUT_XLR:
+ snd_iprintf(buffer, " input: XLR");
+ break;
+ case RME96_INPUT_ANALOG:
+ snd_iprintf(buffer, " input: analog");
+ break;
+ }
+ if (snd_rme96_capture_getrate(rme96, &n) < 0) {
+ snd_iprintf(buffer, "\n sample rate: no valid signal\n");
+ } else {
+ if (n) {
+ snd_iprintf(buffer, " (8 channels)\n");
+ } else {
+ snd_iprintf(buffer, " (2 channels)\n");
+ }
+ snd_iprintf(buffer, " sample rate: %d Hz\n",
+ snd_rme96_capture_getrate(rme96, &n));
+ }
+ if (rme96->wcreg & RME96_WCR_MODE24_2) {
+ snd_iprintf(buffer, " sample format: 24 bit\n");
+ } else {
+ snd_iprintf(buffer, " sample format: 16 bit\n");
+ }
+
+ snd_iprintf(buffer, "\nOutput settings\n");
+ if (rme96->wcreg & RME96_WCR_SEL) {
+ snd_iprintf(buffer, " output signal: normal playback\n");
+ } else {
+ snd_iprintf(buffer, " output signal: same as input\n");
+ }
+ snd_iprintf(buffer, " sample rate: %d Hz\n",
+ snd_rme96_playback_getrate(rme96));
+ if (rme96->wcreg & RME96_WCR_MODE24) {
+ snd_iprintf(buffer, " sample format: 24 bit\n");
+ } else {
+ snd_iprintf(buffer, " sample format: 16 bit\n");
+ }
+ if (rme96->areg & RME96_AR_WSEL) {
+ snd_iprintf(buffer, " sample clock source: word clock\n");
+ } else if (rme96->wcreg & RME96_WCR_MASTER) {
+ snd_iprintf(buffer, " sample clock source: internal\n");
+ } else if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
+ snd_iprintf(buffer, " sample clock source: autosync (internal anyway due to analog input setting)\n");
+ } else if (snd_rme96_capture_getrate(rme96, &n) < 0) {
+ snd_iprintf(buffer, " sample clock source: autosync (internal anyway due to no valid signal)\n");
+ } else {
+ snd_iprintf(buffer, " sample clock source: autosync\n");
+ }
+ if (rme96->wcreg & RME96_WCR_PRO) {
+ snd_iprintf(buffer, " format: AES/EBU (professional)\n");
+ } else {
+ snd_iprintf(buffer, " format: IEC958 (consumer)\n");
+ }
+ if (rme96->wcreg & RME96_WCR_EMP) {
+ snd_iprintf(buffer, " emphasis: on\n");
+ } else {
+ snd_iprintf(buffer, " emphasis: off\n");
+ }
+ if (rme96->wcreg & RME96_WCR_DOLBY) {
+ snd_iprintf(buffer, " non-audio (dolby): on\n");
+ } else {
+ snd_iprintf(buffer, " non-audio (dolby): off\n");
+ }
+ if (RME96_HAS_ANALOG_IN(rme96)) {
+ snd_iprintf(buffer, "\nAnalog output settings\n");
+ switch (snd_rme96_getmontracks(rme96)) {
+ case RME96_MONITOR_TRACKS_1_2:
+ snd_iprintf(buffer, " monitored ADAT tracks: 1+2\n");
+ break;
+ case RME96_MONITOR_TRACKS_3_4:
+ snd_iprintf(buffer, " monitored ADAT tracks: 3+4\n");
+ break;
+ case RME96_MONITOR_TRACKS_5_6:
+ snd_iprintf(buffer, " monitored ADAT tracks: 5+6\n");
+ break;
+ case RME96_MONITOR_TRACKS_7_8:
+ snd_iprintf(buffer, " monitored ADAT tracks: 7+8\n");
+ break;
+ }
+ switch (snd_rme96_getattenuation(rme96)) {
+ case RME96_ATTENUATION_0:
+ snd_iprintf(buffer, " attenuation: 0 dB\n");
+ break;
+ case RME96_ATTENUATION_6:
+ snd_iprintf(buffer, " attenuation: -6 dB\n");
+ break;
+ case RME96_ATTENUATION_12:
+ snd_iprintf(buffer, " attenuation: -12 dB\n");
+ break;
+ case RME96_ATTENUATION_18:
+ snd_iprintf(buffer, " attenuation: -18 dB\n");
+ break;
+ }
+ snd_iprintf(buffer, " volume left: %u\n", rme96->vol[0]);
+ snd_iprintf(buffer, " volume right: %u\n", rme96->vol[1]);
+ }
+}
+
+static void __devinit
+snd_rme96_proc_init(rme96_t *rme96)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(rme96->card, "rme96", &entry))
+ snd_info_set_text_ops(entry, rme96, 1024, snd_rme96_proc_read);
+}
+
+/*
+ * control interface
+ */
+
+static int
+snd_rme96_info_loopback_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+static int
+snd_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme96->lock);
+ ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1;
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+static int
+snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL;
+ spin_lock_irq(&rme96->lock);
+ val = (rme96->wcreg & ~RME96_WCR_SEL) | val;
+ change = val != rme96->wcreg;
+ rme96->wcreg = val;
+ writel(val, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ spin_unlock_irq(&rme96->lock);
+ return change;
+}
+
+static int
+snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" };
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ switch (rme96->pci->device) {
+ case PCI_DEVICE_ID_DIGI96:
+ case PCI_DEVICE_ID_DIGI96_8:
+ uinfo->value.enumerated.items = 3;
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PRO:
+ uinfo->value.enumerated.items = 4;
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+ if (rme96->rev > 4) {
+ /* PST */
+ uinfo->value.enumerated.items = 4;
+ texts[3] = _texts[4]; /* Analog instead of XLR */
+ } else {
+ /* PAD */
+ uinfo->value.enumerated.items = 5;
+ }
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) {
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ }
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+static int
+snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ unsigned int items = 3;
+
+ spin_lock_irq(&rme96->lock);
+ ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96);
+
+ switch (rme96->pci->device) {
+ case PCI_DEVICE_ID_DIGI96:
+ case PCI_DEVICE_ID_DIGI96_8:
+ items = 3;
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PRO:
+ items = 4;
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+ if (rme96->rev > 4) {
+ /* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */
+ if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) {
+ ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR;
+ }
+ items = 4;
+ } else {
+ items = 5;
+ }
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ if (ucontrol->value.enumerated.item[0] >= items) {
+ ucontrol->value.enumerated.item[0] = items - 1;
+ }
+
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+static int
+snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change, items = 3;
+
+ switch (rme96->pci->device) {
+ case PCI_DEVICE_ID_DIGI96:
+ case PCI_DEVICE_ID_DIGI96_8:
+ items = 3;
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PRO:
+ items = 4;
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+ if (rme96->rev > 4) {
+ items = 4;
+ } else {
+ items = 5;
+ }
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ val = ucontrol->value.enumerated.item[0] % items;
+
+ /* special case for PST */
+ if (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4) {
+ if (val == RME96_INPUT_XLR) {
+ val = RME96_INPUT_ANALOG;
+ }
+ }
+
+ spin_lock_irq(&rme96->lock);
+ change = (int)val != snd_rme96_getinputtype(rme96);
+ snd_rme96_setinputtype(rme96, val);
+ spin_unlock_irq(&rme96->lock);
+ return change;
+}
+
+static int
+snd_rme96_info_clockmode_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[3] = { "AutoSync", "Internal", "Word" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2) {
+ uinfo->value.enumerated.item = 2;
+ }
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+static int
+snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme96->lock);
+ ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96);
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+static int
+snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ucontrol->value.enumerated.item[0] % 3;
+ spin_lock_irq(&rme96->lock);
+ change = (int)val != snd_rme96_getclockmode(rme96);
+ snd_rme96_setclockmode(rme96, val);
+ spin_unlock_irq(&rme96->lock);
+ return change;
+}
+
+static int
+snd_rme96_info_attenuation_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item > 3) {
+ uinfo->value.enumerated.item = 3;
+ }
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+static int
+snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme96->lock);
+ ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96);
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+static int
+snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ucontrol->value.enumerated.item[0] % 4;
+ spin_lock_irq(&rme96->lock);
+
+ change = (int)val != snd_rme96_getattenuation(rme96);
+ snd_rme96_setattenuation(rme96, val);
+ spin_unlock_irq(&rme96->lock);
+ return change;
+}
+
+static int
+snd_rme96_info_montracks_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item > 3) {
+ uinfo->value.enumerated.item = 3;
+ }
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+static int
+snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme96->lock);
+ ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96);
+ spin_unlock_irq(&rme96->lock);
+ return 0;
+}
+static int
+snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ucontrol->value.enumerated.item[0] % 4;
+ spin_lock_irq(&rme96->lock);
+ change = (int)val != snd_rme96_getmontracks(rme96);
+ snd_rme96_setmontracks(rme96, val);
+ spin_unlock_irq(&rme96->lock);
+ return change;
+}
+
+static u32 snd_rme96_convert_from_aes(snd_aes_iec958_t *aes)
+{
+ u32 val = 0;
+ val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0;
+ val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0;
+ if (val & RME96_WCR_PRO)
+ val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
+ else
+ val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
+ return val;
+}
+
+static void snd_rme96_convert_to_aes(snd_aes_iec958_t *aes, u32 val)
+{
+ aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) |
+ ((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0);
+ if (val & RME96_WCR_PRO)
+ aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+ else
+ aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme96_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif);
+ return 0;
+}
+
+static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&rme96->lock);
+ change = val != rme96->wcreg_spdif;
+ rme96->wcreg_spdif = val;
+ spin_unlock_irq(&rme96->lock);
+ return change;
+}
+
+static int snd_rme96_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream);
+ return 0;
+}
+
+static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&rme96->lock);
+ change = val != rme96->wcreg_spdif_stream;
+ rme96->wcreg_spdif_stream = val;
+ rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
+ rme96->wcreg |= val;
+ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ spin_unlock_irq(&rme96->lock);
+ return change;
+}
+
+static int snd_rme96_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme96_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = kcontrol->private_value;
+ return 0;
+}
+
+static int
+snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96);
+ return 0;
+}
+
+static int
+snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme96->lock);
+ u->value.integer.value[0] = rme96->vol[0];
+ u->value.integer.value[1] = rme96->vol[1];
+ spin_unlock_irq(&rme96->lock);
+
+ return 0;
+}
+
+static int
+snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+ rme96_t *rme96 = snd_kcontrol_chip(kcontrol);
+ int change = 0;
+
+ if (!RME96_HAS_ANALOG_OUT(rme96)) {
+ return -EINVAL;
+ }
+ spin_lock_irq(&rme96->lock);
+ if (u->value.integer.value[0] != rme96->vol[0]) {
+ rme96->vol[0] = u->value.integer.value[0];
+ change = 1;
+ }
+ if (u->value.integer.value[1] != rme96->vol[1]) {
+ rme96->vol[1] = u->value.integer.value[1];
+ change = 1;
+ }
+ if (change) {
+ snd_rme96_apply_dac_volume(rme96);
+ }
+ spin_unlock_irq(&rme96->lock);
+
+ return change;
+}
+
+static snd_kcontrol_new_t snd_rme96_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_rme96_control_spdif_info,
+ .get = snd_rme96_control_spdif_get,
+ .put = snd_rme96_control_spdif_put
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_rme96_control_spdif_stream_info,
+ .get = snd_rme96_control_spdif_stream_get,
+ .put = snd_rme96_control_spdif_stream_put
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_rme96_control_spdif_mask_info,
+ .get = snd_rme96_control_spdif_mask_get,
+ .private_value = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_CON_EMPHASIS
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .info = snd_rme96_control_spdif_mask_info,
+ .get = snd_rme96_control_spdif_mask_get,
+ .private_value = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_PRO_EMPHASIS
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Connector",
+ .info = snd_rme96_info_inputtype_control,
+ .get = snd_rme96_get_inputtype_control,
+ .put = snd_rme96_put_inputtype_control
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Loopback Input",
+ .info = snd_rme96_info_loopback_control,
+ .get = snd_rme96_get_loopback_control,
+ .put = snd_rme96_put_loopback_control
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sample Clock Source",
+ .info = snd_rme96_info_clockmode_control,
+ .get = snd_rme96_get_clockmode_control,
+ .put = snd_rme96_put_clockmode_control
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitor Tracks",
+ .info = snd_rme96_info_montracks_control,
+ .get = snd_rme96_get_montracks_control,
+ .put = snd_rme96_put_montracks_control
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Attenuation",
+ .info = snd_rme96_info_attenuation_control,
+ .get = snd_rme96_get_attenuation_control,
+ .put = snd_rme96_put_attenuation_control
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Playback Volume",
+ .info = snd_rme96_dac_volume_info,
+ .get = snd_rme96_dac_volume_get,
+ .put = snd_rme96_dac_volume_put
+}
+};
+
+static int
+snd_rme96_create_switches(snd_card_t *card,
+ rme96_t *rme96)
+{
+ int idx, err;
+ snd_kcontrol_t *kctl;
+
+ for (idx = 0; idx < 7; idx++) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+ return err;
+ if (idx == 1) /* IEC958 (S/PDIF) Stream */
+ rme96->spdif_ctl = kctl;
+ }
+
+ if (RME96_HAS_ANALOG_OUT(rme96)) {
+ for (idx = 7; idx < 10; idx++)
+ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Card initialisation
+ */
+
+static void snd_rme96_card_free(snd_card_t *card)
+{
+ snd_rme96_free(card->private_data);
+}
+
+static int __devinit
+snd_rme96_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ rme96_t *rme96;
+ snd_card_t *card;
+ int err;
+ u8 val;
+
+ if (dev >= SNDRV_CARDS) {
+ return -ENODEV;
+ }
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+ if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(rme96_t))) == NULL)
+ return -ENOMEM;
+ card->private_free = snd_rme96_card_free;
+ rme96 = (rme96_t *)card->private_data;
+ rme96->card = card;
+ rme96->pci = pci;
+ snd_card_set_dev(card, &pci->dev);
+ if ((err = snd_rme96_create(rme96)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "Digi96");
+ switch (rme96->pci->device) {
+ case PCI_DEVICE_ID_DIGI96:
+ strcpy(card->shortname, "RME Digi96");
+ break;
+ case PCI_DEVICE_ID_DIGI96_8:
+ strcpy(card->shortname, "RME Digi96/8");
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PRO:
+ strcpy(card->shortname, "RME Digi96/8 PRO");
+ break;
+ case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+ pci_read_config_byte(rme96->pci, 8, &val);
+ if (val < 5) {
+ strcpy(card->shortname, "RME Digi96/8 PAD");
+ } else {
+ strcpy(card->shortname, "RME Digi96/8 PST");
+ }
+ break;
+ }
+ sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
+ rme96->port, rme96->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_rme96_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "RME Digi96",
+ .id_table = snd_rme96_ids,
+ .probe = snd_rme96_probe,
+ .remove = __devexit_p(snd_rme96_remove),
+};
+
+static int __init alsa_card_rme96_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_rme96_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_rme96_init)
+module_exit(alsa_card_rme96_exit)
diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile
new file mode 100644
index 00000000000..917374c9cd4
--- /dev/null
+++ b/sound/pci/rme9652/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-rme9652-objs := rme9652.o
+snd-hdsp-objs := hdsp.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_RME9652) += snd-rme9652.o
+obj-$(CONFIG_SND_HDSP) += snd-hdsp.o
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
new file mode 100644
index 00000000000..12efbf0fab5
--- /dev/null
+++ b/sound/pci/rme9652/hdsp.c
@@ -0,0 +1,5206 @@
+/*
+ * ALSA driver for RME Hammerfall DSP audio interface(s)
+ *
+ * Copyright (c) 2002 Paul Davis
+ * Marcus Andersson
+ * Thomas Charbonnel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/moduleparam.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#include <sound/rawmidi.h>
+#include <sound/hwdep.h>
+#include <sound/initval.h>
+#include <sound/hdsp.h>
+
+#include <asm/byteorder.h>
+#include <asm/current.h>
+#include <asm/io.h>
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for RME Hammerfall DSP interface.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for RME Hammerfall DSP interface.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable/disable specific Hammerfall DSP soundcards.");
+MODULE_AUTHOR("Paul Davis <paul@linuxaudiosystems.com>, Marcus Andersson, Thomas Charbonnel <thomas@undata.org>");
+MODULE_DESCRIPTION("RME Hammerfall DSP");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP},"
+ "{RME HDSP-9652},"
+ "{RME HDSP-9632}}");
+
+#define HDSP_MAX_CHANNELS 26
+#define HDSP_MAX_DS_CHANNELS 14
+#define HDSP_MAX_QS_CHANNELS 8
+#define DIGIFACE_SS_CHANNELS 26
+#define DIGIFACE_DS_CHANNELS 14
+#define MULTIFACE_SS_CHANNELS 18
+#define MULTIFACE_DS_CHANNELS 14
+#define H9652_SS_CHANNELS 26
+#define H9652_DS_CHANNELS 14
+/* This does not include possible Analog Extension Boards
+ AEBs are detected at card initialization
+*/
+#define H9632_SS_CHANNELS 12
+#define H9632_DS_CHANNELS 8
+#define H9632_QS_CHANNELS 4
+
+/* Write registers. These are defined as byte-offsets from the iobase value.
+ */
+#define HDSP_resetPointer 0
+#define HDSP_outputBufferAddress 32
+#define HDSP_inputBufferAddress 36
+#define HDSP_controlRegister 64
+#define HDSP_interruptConfirmation 96
+#define HDSP_outputEnable 128
+#define HDSP_control2Reg 256
+#define HDSP_midiDataOut0 352
+#define HDSP_midiDataOut1 356
+#define HDSP_fifoData 368
+#define HDSP_inputEnable 384
+
+/* Read registers. These are defined as byte-offsets from the iobase value
+ */
+
+#define HDSP_statusRegister 0
+#define HDSP_timecode 128
+#define HDSP_status2Register 192
+#define HDSP_midiDataOut0 352
+#define HDSP_midiDataOut1 356
+#define HDSP_midiDataIn0 360
+#define HDSP_midiDataIn1 364
+#define HDSP_midiStatusOut0 384
+#define HDSP_midiStatusOut1 388
+#define HDSP_midiStatusIn0 392
+#define HDSP_midiStatusIn1 396
+#define HDSP_fifoStatus 400
+
+/* the meters are regular i/o-mapped registers, but offset
+ considerably from the rest. the peak registers are reset
+ when read; the least-significant 4 bits are full-scale counters;
+ the actual peak value is in the most-significant 24 bits.
+*/
+
+#define HDSP_playbackPeakLevel 4096 /* 26 * 32 bit values */
+#define HDSP_inputPeakLevel 4224 /* 26 * 32 bit values */
+#define HDSP_outputPeakLevel 4352 /* (26+2) * 32 bit values */
+#define HDSP_playbackRmsLevel 4612 /* 26 * 64 bit values */
+#define HDSP_inputRmsLevel 4868 /* 26 * 64 bit values */
+
+
+/* This is for H9652 cards
+ Peak values are read downward from the base
+ Rms values are read upward
+ There are rms values for the outputs too
+ 26*3 values are read in ss mode
+ 14*3 in ds mode, with no gap between values
+*/
+#define HDSP_9652_peakBase 7164
+#define HDSP_9652_rmsBase 4096
+
+/* c.f. the hdsp_9632_meters_t struct */
+#define HDSP_9632_metersBase 4096
+
+#define HDSP_IO_EXTENT 7168
+
+/* control2 register bits */
+
+#define HDSP_TMS 0x01
+#define HDSP_TCK 0x02
+#define HDSP_TDI 0x04
+#define HDSP_JTAG 0x08
+#define HDSP_PWDN 0x10
+#define HDSP_PROGRAM 0x020
+#define HDSP_CONFIG_MODE_0 0x040
+#define HDSP_CONFIG_MODE_1 0x080
+#define HDSP_VERSION_BIT 0x100
+#define HDSP_BIGENDIAN_MODE 0x200
+#define HDSP_RD_MULTIPLE 0x400
+#define HDSP_9652_ENABLE_MIXER 0x800
+#define HDSP_TDO 0x10000000
+
+#define HDSP_S_PROGRAM (HDSP_PROGRAM|HDSP_CONFIG_MODE_0)
+#define HDSP_S_LOAD (HDSP_PROGRAM|HDSP_CONFIG_MODE_1)
+
+/* Control Register bits */
+
+#define HDSP_Start (1<<0) /* start engine */
+#define HDSP_Latency0 (1<<1) /* buffer size = 2^n where n is defined by Latency{2,1,0} */
+#define HDSP_Latency1 (1<<2) /* [ see above ] */
+#define HDSP_Latency2 (1<<3) /* [ see above ] */
+#define HDSP_ClockModeMaster (1<<4) /* 1=Master, 0=Slave/Autosync */
+#define HDSP_AudioInterruptEnable (1<<5) /* what do you think ? */
+#define HDSP_Frequency0 (1<<6) /* 0=44.1kHz/88.2kHz/176.4kHz 1=48kHz/96kHz/192kHz */
+#define HDSP_Frequency1 (1<<7) /* 0=32kHz/64kHz/128kHz */
+#define HDSP_DoubleSpeed (1<<8) /* 0=normal speed, 1=double speed */
+#define HDSP_SPDIFProfessional (1<<9) /* 0=consumer, 1=professional */
+#define HDSP_SPDIFEmphasis (1<<10) /* 0=none, 1=on */
+#define HDSP_SPDIFNonAudio (1<<11) /* 0=off, 1=on */
+#define HDSP_SPDIFOpticalOut (1<<12) /* 1=use 1st ADAT connector for SPDIF, 0=do not */
+#define HDSP_SyncRef2 (1<<13)
+#define HDSP_SPDIFInputSelect0 (1<<14)
+#define HDSP_SPDIFInputSelect1 (1<<15)
+#define HDSP_SyncRef0 (1<<16)
+#define HDSP_SyncRef1 (1<<17)
+#define HDSP_AnalogExtensionBoard (1<<18) /* For H9632 cards */
+#define HDSP_XLRBreakoutCable (1<<20) /* For H9632 cards */
+#define HDSP_Midi0InterruptEnable (1<<22)
+#define HDSP_Midi1InterruptEnable (1<<23)
+#define HDSP_LineOut (1<<24)
+#define HDSP_ADGain0 (1<<25) /* From here : H9632 specific */
+#define HDSP_ADGain1 (1<<26)
+#define HDSP_DAGain0 (1<<27)
+#define HDSP_DAGain1 (1<<28)
+#define HDSP_PhoneGain0 (1<<29)
+#define HDSP_PhoneGain1 (1<<30)
+#define HDSP_QuadSpeed (1<<31)
+
+#define HDSP_ADGainMask (HDSP_ADGain0|HDSP_ADGain1)
+#define HDSP_ADGainMinus10dBV HDSP_ADGainMask
+#define HDSP_ADGainPlus4dBu (HDSP_ADGain0)
+#define HDSP_ADGainLowGain 0
+
+#define HDSP_DAGainMask (HDSP_DAGain0|HDSP_DAGain1)
+#define HDSP_DAGainHighGain HDSP_DAGainMask
+#define HDSP_DAGainPlus4dBu (HDSP_DAGain0)
+#define HDSP_DAGainMinus10dBV 0
+
+#define HDSP_PhoneGainMask (HDSP_PhoneGain0|HDSP_PhoneGain1)
+#define HDSP_PhoneGain0dB HDSP_PhoneGainMask
+#define HDSP_PhoneGainMinus6dB (HDSP_PhoneGain0)
+#define HDSP_PhoneGainMinus12dB 0
+
+#define HDSP_LatencyMask (HDSP_Latency0|HDSP_Latency1|HDSP_Latency2)
+#define HDSP_FrequencyMask (HDSP_Frequency0|HDSP_Frequency1|HDSP_DoubleSpeed|HDSP_QuadSpeed)
+
+#define HDSP_SPDIFInputMask (HDSP_SPDIFInputSelect0|HDSP_SPDIFInputSelect1)
+#define HDSP_SPDIFInputADAT1 0
+#define HDSP_SPDIFInputCoaxial (HDSP_SPDIFInputSelect0)
+#define HDSP_SPDIFInputCdrom (HDSP_SPDIFInputSelect1)
+#define HDSP_SPDIFInputAES (HDSP_SPDIFInputSelect0|HDSP_SPDIFInputSelect1)
+
+#define HDSP_SyncRefMask (HDSP_SyncRef0|HDSP_SyncRef1|HDSP_SyncRef2)
+#define HDSP_SyncRef_ADAT1 0
+#define HDSP_SyncRef_ADAT2 (HDSP_SyncRef0)
+#define HDSP_SyncRef_ADAT3 (HDSP_SyncRef1)
+#define HDSP_SyncRef_SPDIF (HDSP_SyncRef0|HDSP_SyncRef1)
+#define HDSP_SyncRef_WORD (HDSP_SyncRef2)
+#define HDSP_SyncRef_ADAT_SYNC (HDSP_SyncRef0|HDSP_SyncRef2)
+
+/* Sample Clock Sources */
+
+#define HDSP_CLOCK_SOURCE_AUTOSYNC 0
+#define HDSP_CLOCK_SOURCE_INTERNAL_32KHZ 1
+#define HDSP_CLOCK_SOURCE_INTERNAL_44_1KHZ 2
+#define HDSP_CLOCK_SOURCE_INTERNAL_48KHZ 3
+#define HDSP_CLOCK_SOURCE_INTERNAL_64KHZ 4
+#define HDSP_CLOCK_SOURCE_INTERNAL_88_2KHZ 5
+#define HDSP_CLOCK_SOURCE_INTERNAL_96KHZ 6
+#define HDSP_CLOCK_SOURCE_INTERNAL_128KHZ 7
+#define HDSP_CLOCK_SOURCE_INTERNAL_176_4KHZ 8
+#define HDSP_CLOCK_SOURCE_INTERNAL_192KHZ 9
+
+/* Preferred sync reference choices - used by "pref_sync_ref" control switch */
+
+#define HDSP_SYNC_FROM_WORD 0
+#define HDSP_SYNC_FROM_SPDIF 1
+#define HDSP_SYNC_FROM_ADAT1 2
+#define HDSP_SYNC_FROM_ADAT_SYNC 3
+#define HDSP_SYNC_FROM_ADAT2 4
+#define HDSP_SYNC_FROM_ADAT3 5
+
+/* SyncCheck status */
+
+#define HDSP_SYNC_CHECK_NO_LOCK 0
+#define HDSP_SYNC_CHECK_LOCK 1
+#define HDSP_SYNC_CHECK_SYNC 2
+
+/* AutoSync references - used by "autosync_ref" control switch */
+
+#define HDSP_AUTOSYNC_FROM_WORD 0
+#define HDSP_AUTOSYNC_FROM_ADAT_SYNC 1
+#define HDSP_AUTOSYNC_FROM_SPDIF 2
+#define HDSP_AUTOSYNC_FROM_NONE 3
+#define HDSP_AUTOSYNC_FROM_ADAT1 4
+#define HDSP_AUTOSYNC_FROM_ADAT2 5
+#define HDSP_AUTOSYNC_FROM_ADAT3 6
+
+/* Possible sources of S/PDIF input */
+
+#define HDSP_SPDIFIN_OPTICAL 0 /* optical (ADAT1) */
+#define HDSP_SPDIFIN_COAXIAL 1 /* coaxial (RCA) */
+#define HDSP_SPDIFIN_INTERNAL 2 /* internal (CDROM) */
+#define HDSP_SPDIFIN_AES 3 /* xlr for H9632 (AES)*/
+
+#define HDSP_Frequency32KHz HDSP_Frequency0
+#define HDSP_Frequency44_1KHz HDSP_Frequency1
+#define HDSP_Frequency48KHz (HDSP_Frequency1|HDSP_Frequency0)
+#define HDSP_Frequency64KHz (HDSP_DoubleSpeed|HDSP_Frequency0)
+#define HDSP_Frequency88_2KHz (HDSP_DoubleSpeed|HDSP_Frequency1)
+#define HDSP_Frequency96KHz (HDSP_DoubleSpeed|HDSP_Frequency1|HDSP_Frequency0)
+/* For H9632 cards */
+#define HDSP_Frequency128KHz (HDSP_QuadSpeed|HDSP_DoubleSpeed|HDSP_Frequency0)
+#define HDSP_Frequency176_4KHz (HDSP_QuadSpeed|HDSP_DoubleSpeed|HDSP_Frequency1)
+#define HDSP_Frequency192KHz (HDSP_QuadSpeed|HDSP_DoubleSpeed|HDSP_Frequency1|HDSP_Frequency0)
+
+#define hdsp_encode_latency(x) (((x)<<1) & HDSP_LatencyMask)
+#define hdsp_decode_latency(x) (((x) & HDSP_LatencyMask)>>1)
+
+#define hdsp_encode_spdif_in(x) (((x)&0x3)<<14)
+#define hdsp_decode_spdif_in(x) (((x)>>14)&0x3)
+
+/* Status Register bits */
+
+#define HDSP_audioIRQPending (1<<0)
+#define HDSP_Lock2 (1<<1) /* this is for Digiface and H9652 */
+#define HDSP_spdifFrequency3 HDSP_Lock2 /* this is for H9632 only */
+#define HDSP_Lock1 (1<<2)
+#define HDSP_Lock0 (1<<3)
+#define HDSP_SPDIFSync (1<<4)
+#define HDSP_TimecodeLock (1<<5)
+#define HDSP_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
+#define HDSP_Sync2 (1<<16)
+#define HDSP_Sync1 (1<<17)
+#define HDSP_Sync0 (1<<18)
+#define HDSP_DoubleSpeedStatus (1<<19)
+#define HDSP_ConfigError (1<<20)
+#define HDSP_DllError (1<<21)
+#define HDSP_spdifFrequency0 (1<<22)
+#define HDSP_spdifFrequency1 (1<<23)
+#define HDSP_spdifFrequency2 (1<<24)
+#define HDSP_SPDIFErrorFlag (1<<25)
+#define HDSP_BufferID (1<<26)
+#define HDSP_TimecodeSync (1<<27)
+#define HDSP_AEBO (1<<28) /* H9632 specific Analog Extension Boards */
+#define HDSP_AEBI (1<<29) /* 0 = present, 1 = absent */
+#define HDSP_midi0IRQPending (1<<30)
+#define HDSP_midi1IRQPending (1<<31)
+
+#define HDSP_spdifFrequencyMask (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2)
+
+#define HDSP_spdifFrequency32KHz (HDSP_spdifFrequency0)
+#define HDSP_spdifFrequency44_1KHz (HDSP_spdifFrequency1)
+#define HDSP_spdifFrequency48KHz (HDSP_spdifFrequency0|HDSP_spdifFrequency1)
+
+#define HDSP_spdifFrequency64KHz (HDSP_spdifFrequency2)
+#define HDSP_spdifFrequency88_2KHz (HDSP_spdifFrequency0|HDSP_spdifFrequency2)
+#define HDSP_spdifFrequency96KHz (HDSP_spdifFrequency2|HDSP_spdifFrequency1)
+
+/* This is for H9632 cards */
+#define HDSP_spdifFrequency128KHz HDSP_spdifFrequencyMask
+#define HDSP_spdifFrequency176_4KHz HDSP_spdifFrequency3
+#define HDSP_spdifFrequency192KHz (HDSP_spdifFrequency3|HDSP_spdifFrequency0)
+
+/* Status2 Register bits */
+
+#define HDSP_version0 (1<<0)
+#define HDSP_version1 (1<<1)
+#define HDSP_version2 (1<<2)
+#define HDSP_wc_lock (1<<3)
+#define HDSP_wc_sync (1<<4)
+#define HDSP_inp_freq0 (1<<5)
+#define HDSP_inp_freq1 (1<<6)
+#define HDSP_inp_freq2 (1<<7)
+#define HDSP_SelSyncRef0 (1<<8)
+#define HDSP_SelSyncRef1 (1<<9)
+#define HDSP_SelSyncRef2 (1<<10)
+
+#define HDSP_wc_valid (HDSP_wc_lock|HDSP_wc_sync)
+
+#define HDSP_systemFrequencyMask (HDSP_inp_freq0|HDSP_inp_freq1|HDSP_inp_freq2)
+#define HDSP_systemFrequency32 (HDSP_inp_freq0)
+#define HDSP_systemFrequency44_1 (HDSP_inp_freq1)
+#define HDSP_systemFrequency48 (HDSP_inp_freq0|HDSP_inp_freq1)
+#define HDSP_systemFrequency64 (HDSP_inp_freq2)
+#define HDSP_systemFrequency88_2 (HDSP_inp_freq0|HDSP_inp_freq2)
+#define HDSP_systemFrequency96 (HDSP_inp_freq1|HDSP_inp_freq2)
+/* FIXME : more values for 9632 cards ? */
+
+#define HDSP_SelSyncRefMask (HDSP_SelSyncRef0|HDSP_SelSyncRef1|HDSP_SelSyncRef2)
+#define HDSP_SelSyncRef_ADAT1 0
+#define HDSP_SelSyncRef_ADAT2 (HDSP_SelSyncRef0)
+#define HDSP_SelSyncRef_ADAT3 (HDSP_SelSyncRef1)
+#define HDSP_SelSyncRef_SPDIF (HDSP_SelSyncRef0|HDSP_SelSyncRef1)
+#define HDSP_SelSyncRef_WORD (HDSP_SelSyncRef2)
+#define HDSP_SelSyncRef_ADAT_SYNC (HDSP_SelSyncRef0|HDSP_SelSyncRef2)
+
+/* Card state flags */
+
+#define HDSP_InitializationComplete (1<<0)
+#define HDSP_FirmwareLoaded (1<<1)
+#define HDSP_FirmwareCached (1<<2)
+
+/* FIFO wait times, defined in terms of 1/10ths of msecs */
+
+#define HDSP_LONG_WAIT 5000
+#define HDSP_SHORT_WAIT 30
+
+#define UNITY_GAIN 32768
+#define MINUS_INFINITY_GAIN 0
+
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5
+#endif
+
+/* the size of a substream (1 mono data stream) */
+
+#define HDSP_CHANNEL_BUFFER_SAMPLES (16*1024)
+#define HDSP_CHANNEL_BUFFER_BYTES (4*HDSP_CHANNEL_BUFFER_SAMPLES)
+
+/* the size of the area we need to allocate for DMA transfers. the
+ size is the same regardless of the number of channels - the
+ Multiface still uses the same memory area.
+
+ Note that we allocate 1 more channel than is apparently needed
+ because the h/w seems to write 1 byte beyond the end of the last
+ page. Sigh.
+*/
+
+#define HDSP_DMA_AREA_BYTES ((HDSP_MAX_CHANNELS+1) * HDSP_CHANNEL_BUFFER_BYTES)
+#define HDSP_DMA_AREA_KILOBYTES (HDSP_DMA_AREA_BYTES/1024)
+
+/* use hotplug firmeare loader? */
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+#ifndef HDSP_USE_HWDEP_LOADER
+#define HDSP_FW_LOADER
+#endif
+#endif
+
+typedef struct _hdsp hdsp_t;
+typedef struct _hdsp_midi hdsp_midi_t;
+typedef struct _hdsp_9632_meters hdsp_9632_meters_t;
+
+struct _hdsp_9632_meters {
+ u32 input_peak[16];
+ u32 playback_peak[16];
+ u32 output_peak[16];
+ u32 xxx_peak[16];
+ u32 padding[64];
+ u32 input_rms_low[16];
+ u32 playback_rms_low[16];
+ u32 output_rms_low[16];
+ u32 xxx_rms_low[16];
+ u32 input_rms_high[16];
+ u32 playback_rms_high[16];
+ u32 output_rms_high[16];
+ u32 xxx_rms_high[16];
+};
+
+struct _hdsp_midi {
+ hdsp_t *hdsp;
+ int id;
+ snd_rawmidi_t *rmidi;
+ snd_rawmidi_substream_t *input;
+ snd_rawmidi_substream_t *output;
+ char istimer; /* timer in use */
+ struct timer_list timer;
+ spinlock_t lock;
+ int pending;
+};
+
+struct _hdsp {
+ spinlock_t lock;
+ snd_pcm_substream_t *capture_substream;
+ snd_pcm_substream_t *playback_substream;
+ hdsp_midi_t midi[2];
+ struct tasklet_struct midi_tasklet;
+ int use_midi_tasklet;
+ int precise_ptr;
+ u32 control_register; /* cached value */
+ u32 control2_register; /* cached value */
+ u32 creg_spdif;
+ u32 creg_spdif_stream;
+ char *card_name; /* digiface/multiface */
+ HDSP_IO_Type io_type; /* ditto, but for code use */
+ unsigned short firmware_rev;
+ unsigned short state; /* stores state bits */
+ u32 firmware_cache[24413]; /* this helps recover from accidental iobox power failure */
+ size_t period_bytes; /* guess what this is */
+ unsigned char max_channels;
+ unsigned char qs_in_channels; /* quad speed mode for H9632 */
+ unsigned char ds_in_channels;
+ unsigned char ss_in_channels; /* different for multiface/digiface */
+ unsigned char qs_out_channels;
+ unsigned char ds_out_channels;
+ unsigned char ss_out_channels;
+
+ struct snd_dma_buffer capture_dma_buf;
+ struct snd_dma_buffer playback_dma_buf;
+ unsigned char *capture_buffer; /* suitably aligned address */
+ unsigned char *playback_buffer; /* suitably aligned address */
+
+ pid_t capture_pid;
+ pid_t playback_pid;
+ int running;
+ int system_sample_rate;
+ char *channel_map;
+ int dev;
+ int irq;
+ unsigned long port;
+ void __iomem *iobase;
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ snd_hwdep_t *hwdep;
+ struct pci_dev *pci;
+ snd_kcontrol_t *spdif_ctl;
+ unsigned short mixer_matrix[HDSP_MATRIX_MIXER_SIZE];
+};
+
+/* These tables map the ALSA channels 1..N to the channels that we
+ need to use in order to find the relevant channel buffer. RME
+ refer to this kind of mapping as between "the ADAT channel and
+ the DMA channel." We index it using the logical audio channel,
+ and the value is the DMA channel (i.e. channel buffer number)
+ where the data for that channel can be read/written from/to.
+*/
+
+static char channel_map_df_ss[HDSP_MAX_CHANNELS] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25
+};
+
+static char channel_map_mf_ss[HDSP_MAX_CHANNELS] = { /* Multiface */
+ /* Analog */
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ /* ADAT 2 */
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ /* SPDIF */
+ 24, 25,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_ds[HDSP_MAX_CHANNELS] = {
+ /* ADAT channels are remapped */
+ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23,
+ /* channels 12 and 13 are S/PDIF */
+ 24, 25,
+ /* others don't exist */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_H9632_ss[HDSP_MAX_CHANNELS] = {
+ /* ADAT channels */
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ /* SPDIF */
+ 8, 9,
+ /* Analog */
+ 10, 11,
+ /* AO4S-192 and AI4S-192 extension boards */
+ 12, 13, 14, 15,
+ /* others don't exist */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1
+};
+
+static char channel_map_H9632_ds[HDSP_MAX_CHANNELS] = {
+ /* ADAT */
+ 1, 3, 5, 7,
+ /* SPDIF */
+ 8, 9,
+ /* Analog */
+ 10, 11,
+ /* AO4S-192 and AI4S-192 extension boards */
+ 12, 13, 14, 15,
+ /* others don't exist */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_H9632_qs[HDSP_MAX_CHANNELS] = {
+ /* ADAT is disabled in this mode */
+ /* SPDIF */
+ 8, 9,
+ /* Analog */
+ 10, 11,
+ /* AO4S-192 and AI4S-192 extension boards */
+ 12, 13, 14, 15,
+ /* others don't exist */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1
+};
+
+static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer *dmab, size_t size)
+{
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+ dmab->dev.dev = snd_dma_pci_data(pci);
+ if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ size, dmab) < 0)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
+{
+ if (dmab->area)
+ snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
+}
+
+
+static struct pci_device_id snd_hdsp_ids[] = {
+ {
+ .vendor = PCI_VENDOR_ID_XILINX,
+ .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ }, /* RME Hammerfall-DSP */
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, snd_hdsp_ids);
+
+/* prototypes */
+static int snd_hdsp_create_alsa_devices(snd_card_t *card, hdsp_t *hdsp);
+static int snd_hdsp_create_pcm(snd_card_t *card, hdsp_t *hdsp);
+static int snd_hdsp_enable_io (hdsp_t *hdsp);
+static void snd_hdsp_initialize_midi_flush (hdsp_t *hdsp);
+static void snd_hdsp_initialize_channels (hdsp_t *hdsp);
+static int hdsp_fifo_wait(hdsp_t *hdsp, int count, int timeout);
+static int hdsp_autosync_ref(hdsp_t *hdsp);
+static int snd_hdsp_set_defaults(hdsp_t *hdsp);
+static void snd_hdsp_9652_enable_mixer (hdsp_t *hdsp);
+
+static int hdsp_playback_to_output_key (hdsp_t *hdsp, int in, int out)
+{
+ switch (hdsp->firmware_rev) {
+ case 0xa:
+ return (64 * out) + (32 + (in));
+ case 0x96:
+ case 0x97:
+ return (32 * out) + (16 + (in));
+ default:
+ return (52 * out) + (26 + (in));
+ }
+}
+
+static int hdsp_input_to_output_key (hdsp_t *hdsp, int in, int out)
+{
+ switch (hdsp->firmware_rev) {
+ case 0xa:
+ return (64 * out) + in;
+ case 0x96:
+ case 0x97:
+ return (32 * out) + in;
+ default:
+ return (52 * out) + in;
+ }
+}
+
+static void hdsp_write(hdsp_t *hdsp, int reg, int val)
+{
+ writel(val, hdsp->iobase + reg);
+}
+
+static unsigned int hdsp_read(hdsp_t *hdsp, int reg)
+{
+ return readl (hdsp->iobase + reg);
+}
+
+static int hdsp_check_for_iobox (hdsp_t *hdsp)
+{
+
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
+ if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
+ snd_printk ("Hammerfall-DSP: no Digiface or Multiface connected!\n");
+ hdsp->state &= ~HDSP_FirmwareLoaded;
+ return -EIO;
+ }
+ return 0;
+
+}
+
+static int snd_hdsp_load_firmware_from_cache(hdsp_t *hdsp) {
+
+ int i;
+ unsigned long flags;
+
+ if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
+
+ snd_printk ("Hammerfall-DSP: loading firmware\n");
+
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_PROGRAM);
+ hdsp_write (hdsp, HDSP_fifoData, 0);
+
+ if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
+ snd_printk ("Hammerfall-DSP: timeout waiting for download preparation\n");
+ return -EIO;
+ }
+
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+
+ for (i = 0; i < 24413; ++i) {
+ hdsp_write(hdsp, HDSP_fifoData, hdsp->firmware_cache[i]);
+ if (hdsp_fifo_wait (hdsp, 127, HDSP_LONG_WAIT)) {
+ snd_printk ("Hammerfall-DSP: timeout during firmware loading\n");
+ return -EIO;
+ }
+ }
+
+ if ((1000 / HZ) < 3000) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((3000 * HZ + 999) / 1000);
+ } else {
+ mdelay(3000);
+ }
+
+ if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
+ snd_printk ("Hammerfall-DSP: timeout at end of firmware loading\n");
+ return -EIO;
+ }
+
+#ifdef SNDRV_BIG_ENDIAN
+ hdsp->control2_register = HDSP_BIGENDIAN_MODE;
+#else
+ hdsp->control2_register = 0;
+#endif
+ hdsp_write (hdsp, HDSP_control2Reg, hdsp->control2_register);
+ snd_printk ("Hammerfall-DSP: finished firmware loading\n");
+
+ }
+ if (hdsp->state & HDSP_InitializationComplete) {
+ snd_printk("Hammerfall-DSP: firmware loaded from cache, restoring defaults\n");
+ spin_lock_irqsave(&hdsp->lock, flags);
+ snd_hdsp_set_defaults(hdsp);
+ spin_unlock_irqrestore(&hdsp->lock, flags);
+ }
+
+ hdsp->state |= HDSP_FirmwareLoaded;
+
+ return 0;
+}
+
+static int hdsp_get_iobox_version (hdsp_t *hdsp)
+{
+ if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
+
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_PROGRAM);
+ hdsp_write (hdsp, HDSP_fifoData, 0);
+ if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT) < 0) {
+ return -EIO;
+ }
+
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ hdsp_write (hdsp, HDSP_fifoData, 0);
+
+ if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT)) {
+ hdsp->io_type = Multiface;
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT);
+ } else {
+ hdsp->io_type = Digiface;
+ }
+ } else {
+ /* firmware was already loaded, get iobox type */
+ if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1) {
+ hdsp->io_type = Multiface;
+ } else {
+ hdsp->io_type = Digiface;
+ }
+ }
+ return 0;
+}
+
+
+static int hdsp_check_for_firmware (hdsp_t *hdsp)
+{
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
+ if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
+ snd_printk("Hammerfall-DSP: firmware not present.\n");
+ hdsp->state &= ~HDSP_FirmwareLoaded;
+ return -EIO;
+ }
+ return 0;
+}
+
+
+static int hdsp_fifo_wait(hdsp_t *hdsp, int count, int timeout)
+{
+ int i;
+
+ /* the fifoStatus registers reports on how many words
+ are available in the command FIFO.
+ */
+
+ for (i = 0; i < timeout; i++) {
+
+ if ((int)(hdsp_read (hdsp, HDSP_fifoStatus) & 0xff) <= count)
+ return 0;
+
+ /* not very friendly, but we only do this during a firmware
+ load and changing the mixer, so we just put up with it.
+ */
+
+ udelay (100);
+ }
+
+ snd_printk ("Hammerfall-DSP: wait for FIFO status <= %d failed after %d iterations\n",
+ count, timeout);
+ return -1;
+}
+
+static int hdsp_read_gain (hdsp_t *hdsp, unsigned int addr)
+{
+ if (addr >= HDSP_MATRIX_MIXER_SIZE) {
+ return 0;
+ }
+ return hdsp->mixer_matrix[addr];
+}
+
+static int hdsp_write_gain(hdsp_t *hdsp, unsigned int addr, unsigned short data)
+{
+ unsigned int ad;
+
+ if (addr >= HDSP_MATRIX_MIXER_SIZE)
+ return -1;
+
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632) {
+
+ /* from martin bjornsen:
+
+ "You can only write dwords to the
+ mixer memory which contain two
+ mixer values in the low and high
+ word. So if you want to change
+ value 0 you have to read value 1
+ from the cache and write both to
+ the first dword in the mixer
+ memory."
+ */
+
+ if (hdsp->io_type == H9632 && addr >= 512) {
+ return 0;
+ }
+
+ if (hdsp->io_type == H9652 && addr >= 1352) {
+ return 0;
+ }
+
+ hdsp->mixer_matrix[addr] = data;
+
+
+ /* `addr' addresses a 16-bit wide address, but
+ the address space accessed via hdsp_write
+ uses byte offsets. put another way, addr
+ varies from 0 to 1351, but to access the
+ corresponding memory location, we need
+ to access 0 to 2703 ...
+ */
+ ad = addr/2;
+
+ hdsp_write (hdsp, 4096 + (ad*4),
+ (hdsp->mixer_matrix[(addr&0x7fe)+1] << 16) +
+ hdsp->mixer_matrix[addr&0x7fe]);
+
+ return 0;
+
+ } else {
+
+ ad = (addr << 16) + data;
+
+ if (hdsp_fifo_wait(hdsp, 127, HDSP_LONG_WAIT)) {
+ return -1;
+ }
+
+ hdsp_write (hdsp, HDSP_fifoData, ad);
+ hdsp->mixer_matrix[addr] = data;
+
+ }
+
+ return 0;
+}
+
+static int snd_hdsp_use_is_exclusive(hdsp_t *hdsp)
+{
+ unsigned long flags;
+ int ret = 1;
+
+ spin_lock_irqsave(&hdsp->lock, flags);
+ if ((hdsp->playback_pid != hdsp->capture_pid) &&
+ (hdsp->playback_pid >= 0) && (hdsp->capture_pid >= 0)) {
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&hdsp->lock, flags);
+ return ret;
+}
+
+static int hdsp_external_sample_rate (hdsp_t *hdsp)
+{
+ unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register);
+ unsigned int rate_bits = status2 & HDSP_systemFrequencyMask;
+
+ switch (rate_bits) {
+ case HDSP_systemFrequency32: return 32000;
+ case HDSP_systemFrequency44_1: return 44100;
+ case HDSP_systemFrequency48: return 48000;
+ case HDSP_systemFrequency64: return 64000;
+ case HDSP_systemFrequency88_2: return 88200;
+ case HDSP_systemFrequency96: return 96000;
+ default:
+ return 0;
+ }
+}
+
+static int hdsp_spdif_sample_rate(hdsp_t *hdsp)
+{
+ unsigned int status = hdsp_read(hdsp, HDSP_statusRegister);
+ unsigned int rate_bits = (status & HDSP_spdifFrequencyMask);
+
+ if (status & HDSP_SPDIFErrorFlag) {
+ return 0;
+ }
+
+ switch (rate_bits) {
+ case HDSP_spdifFrequency32KHz: return 32000;
+ case HDSP_spdifFrequency44_1KHz: return 44100;
+ case HDSP_spdifFrequency48KHz: return 48000;
+ case HDSP_spdifFrequency64KHz: return 64000;
+ case HDSP_spdifFrequency88_2KHz: return 88200;
+ case HDSP_spdifFrequency96KHz: return 96000;
+ case HDSP_spdifFrequency128KHz:
+ if (hdsp->io_type == H9632) return 128000;
+ break;
+ case HDSP_spdifFrequency176_4KHz:
+ if (hdsp->io_type == H9632) return 176400;
+ break;
+ case HDSP_spdifFrequency192KHz:
+ if (hdsp->io_type == H9632) return 192000;
+ break;
+ default:
+ break;
+ }
+ snd_printk ("Hammerfall-DSP: unknown spdif frequency status; bits = 0x%x, status = 0x%x\n", rate_bits, status);
+ return 0;
+}
+
+static void hdsp_compute_period_size(hdsp_t *hdsp)
+{
+ hdsp->period_bytes = 1 << ((hdsp_decode_latency(hdsp->control_register) + 8));
+}
+
+static snd_pcm_uframes_t hdsp_hw_pointer(hdsp_t *hdsp)
+{
+ int position;
+
+ position = hdsp_read(hdsp, HDSP_statusRegister);
+
+ if (!hdsp->precise_ptr) {
+ return (position & HDSP_BufferID) ? (hdsp->period_bytes / 4) : 0;
+ }
+
+ position &= HDSP_BufferPositionMask;
+ position /= 4;
+ position &= (hdsp->period_bytes/2) - 1;
+ return position;
+}
+
+static void hdsp_reset_hw_pointer(hdsp_t *hdsp)
+{
+ hdsp_write (hdsp, HDSP_resetPointer, 0);
+}
+
+static void hdsp_start_audio(hdsp_t *s)
+{
+ s->control_register |= (HDSP_AudioInterruptEnable | HDSP_Start);
+ hdsp_write(s, HDSP_controlRegister, s->control_register);
+}
+
+static void hdsp_stop_audio(hdsp_t *s)
+{
+ s->control_register &= ~(HDSP_Start | HDSP_AudioInterruptEnable);
+ hdsp_write(s, HDSP_controlRegister, s->control_register);
+}
+
+static void hdsp_silence_playback(hdsp_t *hdsp)
+{
+ memset(hdsp->playback_buffer, 0, HDSP_DMA_AREA_BYTES);
+}
+
+static int hdsp_set_interrupt_interval(hdsp_t *s, unsigned int frames)
+{
+ int n;
+
+ spin_lock_irq(&s->lock);
+
+ frames >>= 7;
+ n = 0;
+ while (frames) {
+ n++;
+ frames >>= 1;
+ }
+
+ s->control_register &= ~HDSP_LatencyMask;
+ s->control_register |= hdsp_encode_latency(n);
+
+ hdsp_write(s, HDSP_controlRegister, s->control_register);
+
+ hdsp_compute_period_size(s);
+
+ spin_unlock_irq(&s->lock);
+
+ return 0;
+}
+
+static int hdsp_set_rate(hdsp_t *hdsp, int rate, int called_internally)
+{
+ int reject_if_open = 0;
+ int current_rate;
+ int rate_bits;
+
+ /* ASSUMPTION: hdsp->lock is either held, or
+ there is no need for it (e.g. during module
+ initialization).
+ */
+
+ if (!(hdsp->control_register & HDSP_ClockModeMaster)) {
+ if (called_internally) {
+ /* request from ctl or card initialization */
+ snd_printk("Hammerfall-DSP: device is not running as a clock master: cannot set sample rate.\n");
+ return -1;
+ } else {
+ /* hw_param request while in AutoSync mode */
+ int external_freq = hdsp_external_sample_rate(hdsp);
+ int spdif_freq = hdsp_spdif_sample_rate(hdsp);
+
+ if ((spdif_freq == external_freq*2) && (hdsp_autosync_ref(hdsp) >= HDSP_AUTOSYNC_FROM_ADAT1)) {
+ snd_printk("Hammerfall-DSP: Detected ADAT in double speed mode\n");
+ } else if (hdsp->io_type == H9632 && (spdif_freq == external_freq*4) && (hdsp_autosync_ref(hdsp) >= HDSP_AUTOSYNC_FROM_ADAT1)) {
+ snd_printk("Hammerfall-DSP: Detected ADAT in quad speed mode\n");
+ } else if (rate != external_freq) {
+ snd_printk("Hammerfall-DSP: No AutoSync source for requested rate\n");
+ return -1;
+ }
+ }
+ }
+
+ current_rate = hdsp->system_sample_rate;
+
+ /* Changing from a "single speed" to a "double speed" rate is
+ not allowed if any substreams are open. This is because
+ such a change causes a shift in the location of
+ the DMA buffers and a reduction in the number of available
+ buffers.
+
+ Note that a similar but essentially insoluble problem
+ exists for externally-driven rate changes. All we can do
+ is to flag rate changes in the read/write routines. */
+
+ if (rate > 96000 && hdsp->io_type != H9632) {
+ return -EINVAL;
+ }
+
+ switch (rate) {
+ case 32000:
+ if (current_rate > 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency32KHz;
+ break;
+ case 44100:
+ if (current_rate > 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency44_1KHz;
+ break;
+ case 48000:
+ if (current_rate > 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency48KHz;
+ break;
+ case 64000:
+ if (current_rate <= 48000 || current_rate > 96000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency64KHz;
+ break;
+ case 88200:
+ if (current_rate <= 48000 || current_rate > 96000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency88_2KHz;
+ break;
+ case 96000:
+ if (current_rate <= 48000 || current_rate > 96000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency96KHz;
+ break;
+ case 128000:
+ if (current_rate < 128000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency128KHz;
+ break;
+ case 176400:
+ if (current_rate < 128000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency176_4KHz;
+ break;
+ case 192000:
+ if (current_rate < 128000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSP_Frequency192KHz;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (reject_if_open && (hdsp->capture_pid >= 0 || hdsp->playback_pid >= 0)) {
+ snd_printk ("Hammerfall-DSP: cannot change speed mode (capture PID = %d, playback PID = %d)\n",
+ hdsp->capture_pid,
+ hdsp->playback_pid);
+ return -EBUSY;
+ }
+
+ hdsp->control_register &= ~HDSP_FrequencyMask;
+ hdsp->control_register |= rate_bits;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+
+ if (rate >= 128000) {
+ hdsp->channel_map = channel_map_H9632_qs;
+ } else if (rate > 48000) {
+ if (hdsp->io_type == H9632) {
+ hdsp->channel_map = channel_map_H9632_ds;
+ } else {
+ hdsp->channel_map = channel_map_ds;
+ }
+ } else {
+ switch (hdsp->io_type) {
+ case Multiface:
+ hdsp->channel_map = channel_map_mf_ss;
+ break;
+ case Digiface:
+ case H9652:
+ hdsp->channel_map = channel_map_df_ss;
+ break;
+ case H9632:
+ hdsp->channel_map = channel_map_H9632_ss;
+ break;
+ default:
+ /* should never happen */
+ break;
+ }
+ }
+
+ hdsp->system_sample_rate = rate;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+ MIDI
+ ----------------------------------------------------------------------------*/
+
+static unsigned char snd_hdsp_midi_read_byte (hdsp_t *hdsp, int id)
+{
+ /* the hardware already does the relevant bit-mask with 0xff */
+ if (id) {
+ return hdsp_read(hdsp, HDSP_midiDataIn1);
+ } else {
+ return hdsp_read(hdsp, HDSP_midiDataIn0);
+ }
+}
+
+static void snd_hdsp_midi_write_byte (hdsp_t *hdsp, int id, int val)
+{
+ /* the hardware already does the relevant bit-mask with 0xff */
+ if (id) {
+ hdsp_write(hdsp, HDSP_midiDataOut1, val);
+ } else {
+ hdsp_write(hdsp, HDSP_midiDataOut0, val);
+ }
+}
+
+static int snd_hdsp_midi_input_available (hdsp_t *hdsp, int id)
+{
+ if (id) {
+ return (hdsp_read(hdsp, HDSP_midiStatusIn1) & 0xff);
+ } else {
+ return (hdsp_read(hdsp, HDSP_midiStatusIn0) & 0xff);
+ }
+}
+
+static int snd_hdsp_midi_output_possible (hdsp_t *hdsp, int id)
+{
+ int fifo_bytes_used;
+
+ if (id) {
+ fifo_bytes_used = hdsp_read(hdsp, HDSP_midiStatusOut1) & 0xff;
+ } else {
+ fifo_bytes_used = hdsp_read(hdsp, HDSP_midiStatusOut0) & 0xff;
+ }
+
+ if (fifo_bytes_used < 128) {
+ return 128 - fifo_bytes_used;
+ } else {
+ return 0;
+ }
+}
+
+static void snd_hdsp_flush_midi_input (hdsp_t *hdsp, int id)
+{
+ while (snd_hdsp_midi_input_available (hdsp, id)) {
+ snd_hdsp_midi_read_byte (hdsp, id);
+ }
+}
+
+static int snd_hdsp_midi_output_write (hdsp_midi_t *hmidi)
+{
+ unsigned long flags;
+ int n_pending;
+ int to_write;
+ int i;
+ unsigned char buf[128];
+
+ /* Output is not interrupt driven */
+
+ spin_lock_irqsave (&hmidi->lock, flags);
+ if (hmidi->output) {
+ if (!snd_rawmidi_transmit_empty (hmidi->output)) {
+ if ((n_pending = snd_hdsp_midi_output_possible (hmidi->hdsp, hmidi->id)) > 0) {
+ if (n_pending > (int)sizeof (buf))
+ n_pending = sizeof (buf);
+
+ if ((to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending)) > 0) {
+ for (i = 0; i < to_write; ++i)
+ snd_hdsp_midi_write_byte (hmidi->hdsp, hmidi->id, buf[i]);
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore (&hmidi->lock, flags);
+ return 0;
+}
+
+static int snd_hdsp_midi_input_read (hdsp_midi_t *hmidi)
+{
+ unsigned char buf[128]; /* this buffer is designed to match the MIDI input FIFO size */
+ unsigned long flags;
+ int n_pending;
+ int i;
+
+ spin_lock_irqsave (&hmidi->lock, flags);
+ if ((n_pending = snd_hdsp_midi_input_available (hmidi->hdsp, hmidi->id)) > 0) {
+ if (hmidi->input) {
+ if (n_pending > (int)sizeof (buf)) {
+ n_pending = sizeof (buf);
+ }
+ for (i = 0; i < n_pending; ++i) {
+ buf[i] = snd_hdsp_midi_read_byte (hmidi->hdsp, hmidi->id);
+ }
+ if (n_pending) {
+ snd_rawmidi_receive (hmidi->input, buf, n_pending);
+ }
+ } else {
+ /* flush the MIDI input FIFO */
+ while (--n_pending) {
+ snd_hdsp_midi_read_byte (hmidi->hdsp, hmidi->id);
+ }
+ }
+ }
+ hmidi->pending = 0;
+ if (hmidi->id) {
+ hmidi->hdsp->control_register |= HDSP_Midi1InterruptEnable;
+ } else {
+ hmidi->hdsp->control_register |= HDSP_Midi0InterruptEnable;
+ }
+ hdsp_write(hmidi->hdsp, HDSP_controlRegister, hmidi->hdsp->control_register);
+ spin_unlock_irqrestore (&hmidi->lock, flags);
+ return snd_hdsp_midi_output_write (hmidi);
+}
+
+static void snd_hdsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ hdsp_t *hdsp;
+ hdsp_midi_t *hmidi;
+ unsigned long flags;
+ u32 ie;
+
+ hmidi = (hdsp_midi_t *) substream->rmidi->private_data;
+ hdsp = hmidi->hdsp;
+ ie = hmidi->id ? HDSP_Midi1InterruptEnable : HDSP_Midi0InterruptEnable;
+ spin_lock_irqsave (&hdsp->lock, flags);
+ if (up) {
+ if (!(hdsp->control_register & ie)) {
+ snd_hdsp_flush_midi_input (hdsp, hmidi->id);
+ hdsp->control_register |= ie;
+ }
+ } else {
+ hdsp->control_register &= ~ie;
+ tasklet_kill(&hdsp->midi_tasklet);
+ }
+
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ spin_unlock_irqrestore (&hdsp->lock, flags);
+}
+
+static void snd_hdsp_midi_output_timer(unsigned long data)
+{
+ hdsp_midi_t *hmidi = (hdsp_midi_t *) data;
+ unsigned long flags;
+
+ snd_hdsp_midi_output_write(hmidi);
+ spin_lock_irqsave (&hmidi->lock, flags);
+
+ /* this does not bump hmidi->istimer, because the
+ kernel automatically removed the timer when it
+ expired, and we are now adding it back, thus
+ leaving istimer wherever it was set before.
+ */
+
+ if (hmidi->istimer) {
+ hmidi->timer.expires = 1 + jiffies;
+ add_timer(&hmidi->timer);
+ }
+
+ spin_unlock_irqrestore (&hmidi->lock, flags);
+}
+
+static void snd_hdsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ hdsp_midi_t *hmidi;
+ unsigned long flags;
+
+ hmidi = (hdsp_midi_t *) substream->rmidi->private_data;
+ spin_lock_irqsave (&hmidi->lock, flags);
+ if (up) {
+ if (!hmidi->istimer) {
+ init_timer(&hmidi->timer);
+ hmidi->timer.function = snd_hdsp_midi_output_timer;
+ hmidi->timer.data = (unsigned long) hmidi;
+ hmidi->timer.expires = 1 + jiffies;
+ add_timer(&hmidi->timer);
+ hmidi->istimer++;
+ }
+ } else {
+ if (hmidi->istimer && --hmidi->istimer <= 0) {
+ del_timer (&hmidi->timer);
+ }
+ }
+ spin_unlock_irqrestore (&hmidi->lock, flags);
+ if (up)
+ snd_hdsp_midi_output_write(hmidi);
+}
+
+static int snd_hdsp_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+ hdsp_midi_t *hmidi;
+
+ hmidi = (hdsp_midi_t *) substream->rmidi->private_data;
+ spin_lock_irq (&hmidi->lock);
+ snd_hdsp_flush_midi_input (hmidi->hdsp, hmidi->id);
+ hmidi->input = substream;
+ spin_unlock_irq (&hmidi->lock);
+
+ return 0;
+}
+
+static int snd_hdsp_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+ hdsp_midi_t *hmidi;
+
+ hmidi = (hdsp_midi_t *) substream->rmidi->private_data;
+ spin_lock_irq (&hmidi->lock);
+ hmidi->output = substream;
+ spin_unlock_irq (&hmidi->lock);
+
+ return 0;
+}
+
+static int snd_hdsp_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+ hdsp_midi_t *hmidi;
+
+ snd_hdsp_midi_input_trigger (substream, 0);
+
+ hmidi = (hdsp_midi_t *) substream->rmidi->private_data;
+ spin_lock_irq (&hmidi->lock);
+ hmidi->input = NULL;
+ spin_unlock_irq (&hmidi->lock);
+
+ return 0;
+}
+
+static int snd_hdsp_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+ hdsp_midi_t *hmidi;
+
+ snd_hdsp_midi_output_trigger (substream, 0);
+
+ hmidi = (hdsp_midi_t *) substream->rmidi->private_data;
+ spin_lock_irq (&hmidi->lock);
+ hmidi->output = NULL;
+ spin_unlock_irq (&hmidi->lock);
+
+ return 0;
+}
+
+static snd_rawmidi_ops_t snd_hdsp_midi_output =
+{
+ .open = snd_hdsp_midi_output_open,
+ .close = snd_hdsp_midi_output_close,
+ .trigger = snd_hdsp_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_hdsp_midi_input =
+{
+ .open = snd_hdsp_midi_input_open,
+ .close = snd_hdsp_midi_input_close,
+ .trigger = snd_hdsp_midi_input_trigger,
+};
+
+static int __devinit snd_hdsp_create_midi (snd_card_t *card, hdsp_t *hdsp, int id)
+{
+ char buf[32];
+
+ hdsp->midi[id].id = id;
+ hdsp->midi[id].rmidi = NULL;
+ hdsp->midi[id].input = NULL;
+ hdsp->midi[id].output = NULL;
+ hdsp->midi[id].hdsp = hdsp;
+ hdsp->midi[id].istimer = 0;
+ hdsp->midi[id].pending = 0;
+ spin_lock_init (&hdsp->midi[id].lock);
+
+ sprintf (buf, "%s MIDI %d", card->shortname, id+1);
+ if (snd_rawmidi_new (card, buf, id, 1, 1, &hdsp->midi[id].rmidi) < 0) {
+ return -1;
+ }
+
+ sprintf (hdsp->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
+ hdsp->midi[id].rmidi->private_data = &hdsp->midi[id];
+
+ snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdsp_midi_output);
+ snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_hdsp_midi_input);
+
+ hdsp->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Control Interface
+ ----------------------------------------------------------------------------*/
+
+static u32 snd_hdsp_convert_from_aes(snd_aes_iec958_t *aes)
+{
+ u32 val = 0;
+ val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? HDSP_SPDIFProfessional : 0;
+ val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? HDSP_SPDIFNonAudio : 0;
+ if (val & HDSP_SPDIFProfessional)
+ val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? HDSP_SPDIFEmphasis : 0;
+ else
+ val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? HDSP_SPDIFEmphasis : 0;
+ return val;
+}
+
+static void snd_hdsp_convert_to_aes(snd_aes_iec958_t *aes, u32 val)
+{
+ aes->status[0] = ((val & HDSP_SPDIFProfessional) ? IEC958_AES0_PROFESSIONAL : 0) |
+ ((val & HDSP_SPDIFNonAudio) ? IEC958_AES0_NONAUDIO : 0);
+ if (val & HDSP_SPDIFProfessional)
+ aes->status[0] |= (val & HDSP_SPDIFEmphasis) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+ else
+ aes->status[0] |= (val & HDSP_SPDIFEmphasis) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_hdsp_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_hdsp_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif);
+ return 0;
+}
+
+static int snd_hdsp_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&hdsp->lock);
+ change = val != hdsp->creg_spdif;
+ hdsp->creg_spdif = val;
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+static int snd_hdsp_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_hdsp_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif_stream);
+ return 0;
+}
+
+static int snd_hdsp_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&hdsp->lock);
+ change = val != hdsp->creg_spdif_stream;
+ hdsp->creg_spdif_stream = val;
+ hdsp->control_register &= ~(HDSP_SPDIFProfessional | HDSP_SPDIFNonAudio | HDSP_SPDIFEmphasis);
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register |= val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+static int snd_hdsp_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_hdsp_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = kcontrol->private_value;
+ return 0;
+}
+
+#define HDSP_SPDIF_IN(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_spdif_in, \
+ .get = snd_hdsp_get_spdif_in, \
+ .put = snd_hdsp_put_spdif_in }
+
+static unsigned int hdsp_spdif_in(hdsp_t *hdsp)
+{
+ return hdsp_decode_spdif_in(hdsp->control_register & HDSP_SPDIFInputMask);
+}
+
+static int hdsp_set_spdif_input(hdsp_t *hdsp, int in)
+{
+ hdsp->control_register &= ~HDSP_SPDIFInputMask;
+ hdsp->control_register |= hdsp_encode_spdif_in(in);
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[4] = {"Optical", "Coaxial", "Internal", "AES"};
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = ((hdsp->io_type == H9632) ? 4 : 3);
+ if (uinfo->value.enumerated.item > ((hdsp->io_type == H9632) ? 3 : 2))
+ uinfo->value.enumerated.item = ((hdsp->io_type == H9632) ? 3 : 2);
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_spdif_in(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0] % ((hdsp->io_type == H9632) ? 4 : 3);
+ spin_lock_irq(&hdsp->lock);
+ change = val != hdsp_spdif_in(hdsp);
+ if (change)
+ hdsp_set_spdif_input(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_OUT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out }
+
+static int hdsp_spdif_out(hdsp_t *hdsp)
+{
+ return (hdsp->control_register & HDSP_SPDIFOpticalOut) ? 1 : 0;
+}
+
+static int hdsp_set_spdif_output(hdsp_t *hdsp, int out)
+{
+ if (out) {
+ hdsp->control_register |= HDSP_SPDIFOpticalOut;
+ } else {
+ hdsp->control_register &= ~HDSP_SPDIFOpticalOut;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_spdif_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_spdif_out(hdsp);
+ hdsp_set_spdif_output(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_PROFESSIONAL(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_professional, .put = snd_hdsp_put_spdif_professional }
+
+static int hdsp_spdif_professional(hdsp_t *hdsp)
+{
+ return (hdsp->control_register & HDSP_SPDIFProfessional) ? 1 : 0;
+}
+
+static int hdsp_set_spdif_professional(hdsp_t *hdsp, int val)
+{
+ if (val) {
+ hdsp->control_register |= HDSP_SPDIFProfessional;
+ } else {
+ hdsp->control_register &= ~HDSP_SPDIFProfessional;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_professional(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_professional(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_spdif_professional(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_spdif_professional(hdsp);
+ hdsp_set_spdif_professional(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_EMPHASIS(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_emphasis, .put = snd_hdsp_put_spdif_emphasis }
+
+static int hdsp_spdif_emphasis(hdsp_t *hdsp)
+{
+ return (hdsp->control_register & HDSP_SPDIFEmphasis) ? 1 : 0;
+}
+
+static int hdsp_set_spdif_emphasis(hdsp_t *hdsp, int val)
+{
+ if (val) {
+ hdsp->control_register |= HDSP_SPDIFEmphasis;
+ } else {
+ hdsp->control_register &= ~HDSP_SPDIFEmphasis;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_emphasis(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_emphasis(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_spdif_emphasis(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_spdif_emphasis(hdsp);
+ hdsp_set_spdif_emphasis(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_NON_AUDIO(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_nonaudio, .put = snd_hdsp_put_spdif_nonaudio }
+
+static int hdsp_spdif_nonaudio(hdsp_t *hdsp)
+{
+ return (hdsp->control_register & HDSP_SPDIFNonAudio) ? 1 : 0;
+}
+
+static int hdsp_set_spdif_nonaudio(hdsp_t *hdsp, int val)
+{
+ if (val) {
+ hdsp->control_register |= HDSP_SPDIFNonAudio;
+ } else {
+ hdsp->control_register &= ~HDSP_SPDIFNonAudio;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_nonaudio(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_nonaudio(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_spdif_nonaudio(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_spdif_nonaudio(hdsp);
+ hdsp_set_spdif_nonaudio(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_hdsp_info_spdif_sample_rate, \
+ .get = snd_hdsp_get_spdif_sample_rate \
+}
+
+static int snd_hdsp_info_spdif_sample_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"};
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ switch (hdsp_spdif_sample_rate(hdsp)) {
+ case 32000:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ case 44100:
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ case 48000:
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case 64000:
+ ucontrol->value.enumerated.item[0] = 3;
+ break;
+ case 88200:
+ ucontrol->value.enumerated.item[0] = 4;
+ break;
+ case 96000:
+ ucontrol->value.enumerated.item[0] = 5;
+ break;
+ case 128000:
+ ucontrol->value.enumerated.item[0] = 7;
+ break;
+ case 176400:
+ ucontrol->value.enumerated.item[0] = 8;
+ break;
+ case 192000:
+ ucontrol->value.enumerated.item[0] = 9;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 6;
+ }
+ return 0;
+}
+
+#define HDSP_SYSTEM_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_hdsp_info_system_sample_rate, \
+ .get = snd_hdsp_get_system_sample_rate \
+}
+
+static int snd_hdsp_info_system_sample_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_system_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp->system_sample_rate;
+ return 0;
+}
+
+#define HDSP_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_hdsp_info_autosync_sample_rate, \
+ .get = snd_hdsp_get_autosync_sample_rate \
+}
+
+static int snd_hdsp_info_autosync_sample_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"};
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7 ;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_autosync_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ switch (hdsp_external_sample_rate(hdsp)) {
+ case 32000:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ case 44100:
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ case 48000:
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case 64000:
+ ucontrol->value.enumerated.item[0] = 3;
+ break;
+ case 88200:
+ ucontrol->value.enumerated.item[0] = 4;
+ break;
+ case 96000:
+ ucontrol->value.enumerated.item[0] = 5;
+ break;
+ case 128000:
+ ucontrol->value.enumerated.item[0] = 7;
+ break;
+ case 176400:
+ ucontrol->value.enumerated.item[0] = 8;
+ break;
+ case 192000:
+ ucontrol->value.enumerated.item[0] = 9;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 6;
+ }
+ return 0;
+}
+
+#define HDSP_SYSTEM_CLOCK_MODE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_hdsp_info_system_clock_mode, \
+ .get = snd_hdsp_get_system_clock_mode \
+}
+
+static int hdsp_system_clock_mode(hdsp_t *hdsp)
+{
+ if (hdsp->control_register & HDSP_ClockModeMaster) {
+ return 0;
+ } else if (hdsp_external_sample_rate(hdsp) != hdsp->system_sample_rate) {
+ return 0;
+ }
+ return 1;
+}
+
+static int snd_hdsp_info_system_clock_mode(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"Master", "Slave" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_system_clock_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_system_clock_mode(hdsp);
+ return 0;
+}
+
+#define HDSP_CLOCK_SOURCE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_clock_source, \
+ .get = snd_hdsp_get_clock_source, \
+ .put = snd_hdsp_put_clock_source \
+}
+
+static int hdsp_clock_source(hdsp_t *hdsp)
+{
+ if (hdsp->control_register & HDSP_ClockModeMaster) {
+ switch (hdsp->system_sample_rate) {
+ case 32000:
+ return 1;
+ case 44100:
+ return 2;
+ case 48000:
+ return 3;
+ case 64000:
+ return 4;
+ case 88200:
+ return 5;
+ case 96000:
+ return 6;
+ case 128000:
+ return 7;
+ case 176400:
+ return 8;
+ case 192000:
+ return 9;
+ default:
+ return 3;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int hdsp_set_clock_source(hdsp_t *hdsp, int mode)
+{
+ int rate;
+ switch (mode) {
+ case HDSP_CLOCK_SOURCE_AUTOSYNC:
+ if (hdsp_external_sample_rate(hdsp) != 0) {
+ if (!hdsp_set_rate(hdsp, hdsp_external_sample_rate(hdsp), 1)) {
+ hdsp->control_register &= ~HDSP_ClockModeMaster;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+ }
+ }
+ return -1;
+ case HDSP_CLOCK_SOURCE_INTERNAL_32KHZ:
+ rate = 32000;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_44_1KHZ:
+ rate = 44100;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_48KHZ:
+ rate = 48000;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_64KHZ:
+ rate = 64000;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_88_2KHZ:
+ rate = 88200;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_96KHZ:
+ rate = 96000;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_128KHZ:
+ rate = 128000;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_176_4KHZ:
+ rate = 176400;
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_192KHZ:
+ rate = 192000;
+ break;
+ default:
+ rate = 48000;
+ }
+ hdsp->control_register |= HDSP_ClockModeMaster;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ hdsp_set_rate(hdsp, rate, 1);
+ return 0;
+}
+
+static int snd_hdsp_info_clock_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", "Internal 192.0 KHz" };
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ if (hdsp->io_type == H9632)
+ uinfo->value.enumerated.items = 10;
+ else
+ uinfo->value.enumerated.items = 7;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_clock_source(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_clock_source(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_clock_source(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0) val = 0;
+ if (hdsp->io_type == H9632) {
+ if (val > 9) val = 9;
+ } else {
+ if (val > 6) val = 6;
+ }
+ spin_lock_irq(&hdsp->lock);
+ if (val != hdsp_clock_source(hdsp)) {
+ change = (hdsp_set_clock_source(hdsp, val) == 0) ? 1 : 0;
+ } else {
+ change = 0;
+ }
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_DA_GAIN(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_da_gain, \
+ .get = snd_hdsp_get_da_gain, \
+ .put = snd_hdsp_put_da_gain \
+}
+
+static int hdsp_da_gain(hdsp_t *hdsp)
+{
+ switch (hdsp->control_register & HDSP_DAGainMask) {
+ case HDSP_DAGainHighGain:
+ return 0;
+ case HDSP_DAGainPlus4dBu:
+ return 1;
+ case HDSP_DAGainMinus10dBV:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int hdsp_set_da_gain(hdsp_t *hdsp, int mode)
+{
+ hdsp->control_register &= ~HDSP_DAGainMask;
+ switch (mode) {
+ case 0:
+ hdsp->control_register |= HDSP_DAGainHighGain;
+ break;
+ case 1:
+ hdsp->control_register |= HDSP_DAGainPlus4dBu;
+ break;
+ case 2:
+ hdsp->control_register |= HDSP_DAGainMinus10dBV;
+ break;
+ default:
+ return -1;
+
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_da_gain(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_da_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_da_gain(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_da_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0) val = 0;
+ if (val > 2) val = 2;
+ spin_lock_irq(&hdsp->lock);
+ if (val != hdsp_da_gain(hdsp)) {
+ change = (hdsp_set_da_gain(hdsp, val) == 0) ? 1 : 0;
+ } else {
+ change = 0;
+ }
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_AD_GAIN(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_ad_gain, \
+ .get = snd_hdsp_get_ad_gain, \
+ .put = snd_hdsp_put_ad_gain \
+}
+
+static int hdsp_ad_gain(hdsp_t *hdsp)
+{
+ switch (hdsp->control_register & HDSP_ADGainMask) {
+ case HDSP_ADGainMinus10dBV:
+ return 0;
+ case HDSP_ADGainPlus4dBu:
+ return 1;
+ case HDSP_ADGainLowGain:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int hdsp_set_ad_gain(hdsp_t *hdsp, int mode)
+{
+ hdsp->control_register &= ~HDSP_ADGainMask;
+ switch (mode) {
+ case 0:
+ hdsp->control_register |= HDSP_ADGainMinus10dBV;
+ break;
+ case 1:
+ hdsp->control_register |= HDSP_ADGainPlus4dBu;
+ break;
+ case 2:
+ hdsp->control_register |= HDSP_ADGainLowGain;
+ break;
+ default:
+ return -1;
+
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_ad_gain(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_ad_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_ad_gain(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_ad_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0) val = 0;
+ if (val > 2) val = 2;
+ spin_lock_irq(&hdsp->lock);
+ if (val != hdsp_ad_gain(hdsp)) {
+ change = (hdsp_set_ad_gain(hdsp, val) == 0) ? 1 : 0;
+ } else {
+ change = 0;
+ }
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_PHONE_GAIN(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_phone_gain, \
+ .get = snd_hdsp_get_phone_gain, \
+ .put = snd_hdsp_put_phone_gain \
+}
+
+static int hdsp_phone_gain(hdsp_t *hdsp)
+{
+ switch (hdsp->control_register & HDSP_PhoneGainMask) {
+ case HDSP_PhoneGain0dB:
+ return 0;
+ case HDSP_PhoneGainMinus6dB:
+ return 1;
+ case HDSP_PhoneGainMinus12dB:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+static int hdsp_set_phone_gain(hdsp_t *hdsp, int mode)
+{
+ hdsp->control_register &= ~HDSP_PhoneGainMask;
+ switch (mode) {
+ case 0:
+ hdsp->control_register |= HDSP_PhoneGain0dB;
+ break;
+ case 1:
+ hdsp->control_register |= HDSP_PhoneGainMinus6dB;
+ break;
+ case 2:
+ hdsp->control_register |= HDSP_PhoneGainMinus12dB;
+ break;
+ default:
+ return -1;
+
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_phone_gain(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"0 dB", "-6 dB", "-12 dB"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_phone_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_phone_gain(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_phone_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0) val = 0;
+ if (val > 2) val = 2;
+ spin_lock_irq(&hdsp->lock);
+ if (val != hdsp_phone_gain(hdsp)) {
+ change = (hdsp_set_phone_gain(hdsp, val) == 0) ? 1 : 0;
+ } else {
+ change = 0;
+ }
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_XLR_BREAKOUT_CABLE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_xlr_breakout_cable, \
+ .get = snd_hdsp_get_xlr_breakout_cable, \
+ .put = snd_hdsp_put_xlr_breakout_cable \
+}
+
+static int hdsp_xlr_breakout_cable(hdsp_t *hdsp)
+{
+ if (hdsp->control_register & HDSP_XLRBreakoutCable) {
+ return 1;
+ }
+ return 0;
+}
+
+static int hdsp_set_xlr_breakout_cable(hdsp_t *hdsp, int mode)
+{
+ if (mode) {
+ hdsp->control_register |= HDSP_XLRBreakoutCable;
+ } else {
+ hdsp->control_register &= ~HDSP_XLRBreakoutCable;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_xlr_breakout_cable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_xlr_breakout_cable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_xlr_breakout_cable(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_xlr_breakout_cable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_xlr_breakout_cable(hdsp);
+ hdsp_set_xlr_breakout_cable(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+/* (De)activates old RME Analog Extension Board
+ These are connected to the internal ADAT connector
+ Switching this on desactivates external ADAT
+*/
+#define HDSP_AEB(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_aeb, \
+ .get = snd_hdsp_get_aeb, \
+ .put = snd_hdsp_put_aeb \
+}
+
+static int hdsp_aeb(hdsp_t *hdsp)
+{
+ if (hdsp->control_register & HDSP_AnalogExtensionBoard) {
+ return 1;
+ }
+ return 0;
+}
+
+static int hdsp_set_aeb(hdsp_t *hdsp, int mode)
+{
+ if (mode) {
+ hdsp->control_register |= HDSP_AnalogExtensionBoard;
+ } else {
+ hdsp->control_register &= ~HDSP_AnalogExtensionBoard;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_aeb(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_aeb(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_aeb(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_aeb(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_aeb(hdsp);
+ hdsp_set_aeb(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_PREF_SYNC_REF(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_pref_sync_ref, \
+ .get = snd_hdsp_get_pref_sync_ref, \
+ .put = snd_hdsp_put_pref_sync_ref \
+}
+
+static int hdsp_pref_sync_ref(hdsp_t *hdsp)
+{
+ /* Notice that this looks at the requested sync source,
+ not the one actually in use.
+ */
+
+ switch (hdsp->control_register & HDSP_SyncRefMask) {
+ case HDSP_SyncRef_ADAT1:
+ return HDSP_SYNC_FROM_ADAT1;
+ case HDSP_SyncRef_ADAT2:
+ return HDSP_SYNC_FROM_ADAT2;
+ case HDSP_SyncRef_ADAT3:
+ return HDSP_SYNC_FROM_ADAT3;
+ case HDSP_SyncRef_SPDIF:
+ return HDSP_SYNC_FROM_SPDIF;
+ case HDSP_SyncRef_WORD:
+ return HDSP_SYNC_FROM_WORD;
+ case HDSP_SyncRef_ADAT_SYNC:
+ return HDSP_SYNC_FROM_ADAT_SYNC;
+ default:
+ return HDSP_SYNC_FROM_WORD;
+ }
+ return 0;
+}
+
+static int hdsp_set_pref_sync_ref(hdsp_t *hdsp, int pref)
+{
+ hdsp->control_register &= ~HDSP_SyncRefMask;
+ switch (pref) {
+ case HDSP_SYNC_FROM_ADAT1:
+ hdsp->control_register &= ~HDSP_SyncRefMask; /* clear SyncRef bits */
+ break;
+ case HDSP_SYNC_FROM_ADAT2:
+ hdsp->control_register |= HDSP_SyncRef_ADAT2;
+ break;
+ case HDSP_SYNC_FROM_ADAT3:
+ hdsp->control_register |= HDSP_SyncRef_ADAT3;
+ break;
+ case HDSP_SYNC_FROM_SPDIF:
+ hdsp->control_register |= HDSP_SyncRef_SPDIF;
+ break;
+ case HDSP_SYNC_FROM_WORD:
+ hdsp->control_register |= HDSP_SyncRef_WORD;
+ break;
+ case HDSP_SYNC_FROM_ADAT_SYNC:
+ hdsp->control_register |= HDSP_SyncRef_ADAT_SYNC;
+ break;
+ default:
+ return -1;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_pref_sync_ref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" };
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ switch (hdsp->io_type) {
+ case Digiface:
+ case H9652:
+ uinfo->value.enumerated.items = 6;
+ break;
+ case Multiface:
+ uinfo->value.enumerated.items = 4;
+ break;
+ case H9632:
+ uinfo->value.enumerated.items = 3;
+ break;
+ default:
+ uinfo->value.enumerated.items = 0;
+ break;
+ }
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_pref_sync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_pref_sync_ref(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_pref_sync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change, max;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+
+ switch (hdsp->io_type) {
+ case Digiface:
+ case H9652:
+ max = 6;
+ break;
+ case Multiface:
+ max = 4;
+ break;
+ case H9632:
+ max = 3;
+ break;
+ default:
+ return -EIO;
+ }
+
+ val = ucontrol->value.enumerated.item[0] % max;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_pref_sync_ref(hdsp);
+ hdsp_set_pref_sync_ref(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_AUTOSYNC_REF(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_hdsp_info_autosync_ref, \
+ .get = snd_hdsp_get_autosync_ref, \
+}
+
+static int hdsp_autosync_ref(hdsp_t *hdsp)
+{
+ /* This looks at the autosync selected sync reference */
+ unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register);
+
+ switch (status2 & HDSP_SelSyncRefMask) {
+ case HDSP_SelSyncRef_WORD:
+ return HDSP_AUTOSYNC_FROM_WORD;
+ case HDSP_SelSyncRef_ADAT_SYNC:
+ return HDSP_AUTOSYNC_FROM_ADAT_SYNC;
+ case HDSP_SelSyncRef_SPDIF:
+ return HDSP_AUTOSYNC_FROM_SPDIF;
+ case HDSP_SelSyncRefMask:
+ return HDSP_AUTOSYNC_FROM_NONE;
+ case HDSP_SelSyncRef_ADAT1:
+ return HDSP_AUTOSYNC_FROM_ADAT1;
+ case HDSP_SelSyncRef_ADAT2:
+ return HDSP_AUTOSYNC_FROM_ADAT2;
+ case HDSP_SelSyncRef_ADAT3:
+ return HDSP_AUTOSYNC_FROM_ADAT3;
+ default:
+ return HDSP_AUTOSYNC_FROM_WORD;
+ }
+ return 0;
+}
+
+static int snd_hdsp_info_autosync_ref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 7;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_hdsp_get_autosync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_autosync_ref(hdsp);
+ return 0;
+}
+
+#define HDSP_LINE_OUT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_line_out, \
+ .get = snd_hdsp_get_line_out, \
+ .put = snd_hdsp_put_line_out \
+}
+
+static int hdsp_line_out(hdsp_t *hdsp)
+{
+ return (hdsp->control_register & HDSP_LineOut) ? 1 : 0;
+}
+
+static int hdsp_set_line_output(hdsp_t *hdsp, int out)
+{
+ if (out) {
+ hdsp->control_register |= HDSP_LineOut;
+ } else {
+ hdsp->control_register &= ~HDSP_LineOut;
+ }
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_info_line_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_line_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdsp->lock);
+ ucontrol->value.integer.value[0] = hdsp_line_out(hdsp);
+ spin_unlock_irq(&hdsp->lock);
+ return 0;
+}
+
+static int snd_hdsp_put_line_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_line_out(hdsp);
+ hdsp_set_line_output(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_PRECISE_POINTER(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_precise_pointer, \
+ .get = snd_hdsp_get_precise_pointer, \
+ .put = snd_hdsp_put_precise_pointer \
+}
+
+static int hdsp_set_precise_pointer(hdsp_t *hdsp, int precise)
+{
+ if (precise) {
+ hdsp->precise_ptr = 1;
+ } else {
+ hdsp->precise_ptr = 0;
+ }
+ return 0;
+}
+
+static int snd_hdsp_info_precise_pointer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_precise_pointer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdsp->lock);
+ ucontrol->value.integer.value[0] = hdsp->precise_ptr;
+ spin_unlock_irq(&hdsp->lock);
+ return 0;
+}
+
+static int snd_hdsp_put_precise_pointer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp->precise_ptr;
+ hdsp_set_precise_pointer(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_USE_MIDI_TASKLET(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_use_midi_tasklet, \
+ .get = snd_hdsp_get_use_midi_tasklet, \
+ .put = snd_hdsp_put_use_midi_tasklet \
+}
+
+static int hdsp_set_use_midi_tasklet(hdsp_t *hdsp, int use_tasklet)
+{
+ if (use_tasklet) {
+ hdsp->use_midi_tasklet = 1;
+ } else {
+ hdsp->use_midi_tasklet = 0;
+ }
+ return 0;
+}
+
+static int snd_hdsp_info_use_midi_tasklet(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_use_midi_tasklet(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdsp->lock);
+ ucontrol->value.integer.value[0] = hdsp->use_midi_tasklet;
+ spin_unlock_irq(&hdsp->lock);
+ return 0;
+}
+
+static int snd_hdsp_put_use_midi_tasklet(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp->use_midi_tasklet;
+ hdsp_set_use_midi_tasklet(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_MIXER(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdsp_info_mixer, \
+ .get = snd_hdsp_get_mixer, \
+ .put = snd_hdsp_put_mixer \
+}
+
+static int snd_hdsp_info_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 65536;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int snd_hdsp_get_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int source;
+ int destination;
+ int addr;
+
+ source = ucontrol->value.integer.value[0];
+ destination = ucontrol->value.integer.value[1];
+
+ if (source >= hdsp->max_channels) {
+ addr = hdsp_playback_to_output_key(hdsp,source-hdsp->max_channels,destination);
+ } else {
+ addr = hdsp_input_to_output_key(hdsp,source, destination);
+ }
+
+ spin_lock_irq(&hdsp->lock);
+ ucontrol->value.integer.value[2] = hdsp_read_gain (hdsp, addr);
+ spin_unlock_irq(&hdsp->lock);
+ return 0;
+}
+
+static int snd_hdsp_put_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int source;
+ int destination;
+ int gain;
+ int addr;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+
+ source = ucontrol->value.integer.value[0];
+ destination = ucontrol->value.integer.value[1];
+
+ if (source >= hdsp->max_channels) {
+ addr = hdsp_playback_to_output_key(hdsp,source-hdsp->max_channels, destination);
+ } else {
+ addr = hdsp_input_to_output_key(hdsp,source, destination);
+ }
+
+ gain = ucontrol->value.integer.value[2];
+
+ spin_lock_irq(&hdsp->lock);
+ change = gain != hdsp_read_gain(hdsp, addr);
+ if (change)
+ hdsp_write_gain(hdsp, addr, gain);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_WC_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdsp_info_sync_check, \
+ .get = snd_hdsp_get_wc_sync_check \
+}
+
+static int snd_hdsp_info_sync_check(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[] = {"No Lock", "Lock", "Sync" };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int hdsp_wc_sync_check(hdsp_t *hdsp)
+{
+ int status2 = hdsp_read(hdsp, HDSP_status2Register);
+ if (status2 & HDSP_wc_lock) {
+ if (status2 & HDSP_wc_sync) {
+ return 2;
+ } else {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+ return 0;
+}
+
+static int snd_hdsp_get_wc_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_wc_sync_check(hdsp);
+ return 0;
+}
+
+#define HDSP_SPDIF_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdsp_info_sync_check, \
+ .get = snd_hdsp_get_spdif_sync_check \
+}
+
+static int hdsp_spdif_sync_check(hdsp_t *hdsp)
+{
+ int status = hdsp_read(hdsp, HDSP_statusRegister);
+ if (status & HDSP_SPDIFErrorFlag) {
+ return 0;
+ } else {
+ if (status & HDSP_SPDIFSync) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_spdif_sync_check(hdsp);
+ return 0;
+}
+
+#define HDSP_ADATSYNC_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdsp_info_sync_check, \
+ .get = snd_hdsp_get_adatsync_sync_check \
+}
+
+static int hdsp_adatsync_sync_check(hdsp_t *hdsp)
+{
+ int status = hdsp_read(hdsp, HDSP_statusRegister);
+ if (status & HDSP_TimecodeLock) {
+ if (status & HDSP_TimecodeSync) {
+ return 2;
+ } else {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int snd_hdsp_get_adatsync_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_adatsync_sync_check(hdsp);
+ return 0;
+}
+
+#define HDSP_ADAT_SYNC_CHECK \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdsp_info_sync_check, \
+ .get = snd_hdsp_get_adat_sync_check \
+}
+
+static int hdsp_adat_sync_check(hdsp_t *hdsp, int idx)
+{
+ int status = hdsp_read(hdsp, HDSP_statusRegister);
+
+ if (status & (HDSP_Lock0>>idx)) {
+ if (status & (HDSP_Sync0>>idx)) {
+ return 2;
+ } else {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int snd_hdsp_get_adat_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ int offset;
+ hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+
+ offset = ucontrol->id.index - 1;
+ snd_assert(offset >= 0);
+
+ switch (hdsp->io_type) {
+ case Digiface:
+ case H9652:
+ if (offset >= 3)
+ return -EINVAL;
+ break;
+ case Multiface:
+ case H9632:
+ if (offset >= 1)
+ return -EINVAL;
+ break;
+ default:
+ return -EIO;
+ }
+
+ ucontrol->value.enumerated.item[0] = hdsp_adat_sync_check(hdsp, offset);
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_hdsp_9632_controls[] = {
+HDSP_DA_GAIN("DA Gain", 0),
+HDSP_AD_GAIN("AD Gain", 0),
+HDSP_PHONE_GAIN("Phones Gain", 0),
+HDSP_XLR_BREAKOUT_CABLE("XLR Breakout Cable", 0)
+};
+
+static snd_kcontrol_new_t snd_hdsp_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_hdsp_control_spdif_info,
+ .get = snd_hdsp_control_spdif_get,
+ .put = snd_hdsp_control_spdif_put,
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_hdsp_control_spdif_stream_info,
+ .get = snd_hdsp_control_spdif_stream_get,
+ .put = snd_hdsp_control_spdif_stream_put,
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_hdsp_control_spdif_mask_info,
+ .get = snd_hdsp_control_spdif_mask_get,
+ .private_value = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_CON_EMPHASIS,
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .info = snd_hdsp_control_spdif_mask_info,
+ .get = snd_hdsp_control_spdif_mask_get,
+ .private_value = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_PRO_EMPHASIS,
+},
+HDSP_MIXER("Mixer", 0),
+HDSP_SPDIF_IN("IEC958 Input Connector", 0),
+HDSP_SPDIF_OUT("IEC958 Output also on ADAT1", 0),
+HDSP_SPDIF_PROFESSIONAL("IEC958 Professional Bit", 0),
+HDSP_SPDIF_EMPHASIS("IEC958 Emphasis Bit", 0),
+HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0),
+/* 'Sample Clock Source' complies with the alsa control naming scheme */
+HDSP_CLOCK_SOURCE("Sample Clock Source", 0),
+HDSP_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
+HDSP_PREF_SYNC_REF("Preferred Sync Reference", 0),
+HDSP_AUTOSYNC_REF("AutoSync Reference", 0),
+HDSP_SPDIF_SAMPLE_RATE("SPDIF Sample Rate", 0),
+HDSP_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+/* 'External Rate' complies with the alsa control naming scheme */
+HDSP_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+HDSP_WC_SYNC_CHECK("Word Clock Lock Status", 0),
+HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0),
+HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 0),
+HDSP_LINE_OUT("Line Out", 0),
+HDSP_PRECISE_POINTER("Precise Pointer", 0),
+HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0),
+};
+
+static snd_kcontrol_new_t snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
+static snd_kcontrol_new_t snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
+
+static int snd_hdsp_create_controls(snd_card_t *card, hdsp_t *hdsp)
+{
+ unsigned int idx;
+ int err;
+ snd_kcontrol_t *kctl;
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_controls); idx++) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0) {
+ return err;
+ }
+ if (idx == 1) /* IEC958 (S/PDIF) Stream */
+ hdsp->spdif_ctl = kctl;
+ }
+
+ /* ADAT SyncCheck status */
+ snd_hdsp_adat_sync_check.name = "ADAT Lock Status";
+ snd_hdsp_adat_sync_check.index = 1;
+ if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_adat_sync_check, hdsp)))) {
+ return err;
+ }
+ if (hdsp->io_type == Digiface || hdsp->io_type == H9652) {
+ for (idx = 1; idx < 3; ++idx) {
+ snd_hdsp_adat_sync_check.index = idx+1;
+ if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_adat_sync_check, hdsp)))) {
+ return err;
+ }
+ }
+ }
+
+ /* DA, AD and Phone gain and XLR breakout cable controls for H9632 cards */
+ if (hdsp->io_type == H9632) {
+ for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_9632_controls); idx++) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_9632_controls[idx], hdsp))) < 0) {
+ return err;
+ }
+ }
+ }
+
+ /* AEB control for H96xx card */
+ if (hdsp->io_type == H9632 || hdsp->io_type == H9652) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_96xx_aeb, hdsp))) < 0) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*------------------------------------------------------------
+ /proc interface
+ ------------------------------------------------------------*/
+
+static void
+snd_hdsp_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ hdsp_t *hdsp = (hdsp_t *) entry->private_data;
+ unsigned int status;
+ unsigned int status2;
+ char *pref_sync_ref;
+ char *autosync_ref;
+ char *system_clock_mode;
+ char *clock_source;
+ int x;
+
+ if (hdsp_check_for_iobox (hdsp)) {
+ snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n");
+ return;
+ }
+
+ if (hdsp_check_for_firmware(hdsp)) {
+ if (hdsp->state & HDSP_FirmwareCached) {
+ if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
+ snd_iprintf(buffer, "Firmware loading from cache failed, please upload manually.\n");
+ return;
+ }
+ } else {
+ snd_iprintf(buffer, "No firmware loaded nor cached, please upload firmware.\n");
+ return;
+ }
+ }
+
+ status = hdsp_read(hdsp, HDSP_statusRegister);
+ status2 = hdsp_read(hdsp, HDSP_status2Register);
+
+ snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, hdsp->card->number + 1);
+ snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
+ hdsp->capture_buffer, hdsp->playback_buffer);
+ snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+ hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase);
+ snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register);
+ snd_iprintf(buffer, "Control2 register: 0x%x\n", hdsp->control2_register);
+ snd_iprintf(buffer, "Status register: 0x%x\n", status);
+ snd_iprintf(buffer, "Status2 register: 0x%x\n", status2);
+ snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff);
+ snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0));
+ snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0));
+ snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1));
+ snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1));
+ snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_tasklet ? "on" : "off");
+
+ snd_iprintf(buffer, "\n");
+
+ x = 1 << (6 + hdsp_decode_latency(hdsp->control_register & HDSP_LatencyMask));
+
+ snd_iprintf(buffer, "Buffer Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdsp->period_bytes);
+ snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", hdsp_hw_pointer(hdsp));
+ snd_iprintf(buffer, "Precise pointer: %s\n", hdsp->precise_ptr ? "on" : "off");
+ snd_iprintf(buffer, "Line out: %s\n", (hdsp->control_register & HDSP_LineOut) ? "on" : "off");
+
+ snd_iprintf(buffer, "Firmware version: %d\n", (status2&HDSP_version0)|(status2&HDSP_version1)<<1|(status2&HDSP_version2)<<2);
+
+ snd_iprintf(buffer, "\n");
+
+
+ switch (hdsp_clock_source(hdsp)) {
+ case HDSP_CLOCK_SOURCE_AUTOSYNC:
+ clock_source = "AutoSync";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_32KHZ:
+ clock_source = "Internal 32 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_44_1KHZ:
+ clock_source = "Internal 44.1 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_48KHZ:
+ clock_source = "Internal 48 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_64KHZ:
+ clock_source = "Internal 64 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_88_2KHZ:
+ clock_source = "Internal 88.2 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_96KHZ:
+ clock_source = "Internal 96 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_128KHZ:
+ clock_source = "Internal 128 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_176_4KHZ:
+ clock_source = "Internal 176.4 kHz";
+ break;
+ case HDSP_CLOCK_SOURCE_INTERNAL_192KHZ:
+ clock_source = "Internal 192 kHz";
+ break;
+ default:
+ clock_source = "Error";
+ }
+ snd_iprintf (buffer, "Sample Clock Source: %s\n", clock_source);
+
+ if (hdsp_system_clock_mode(hdsp)) {
+ system_clock_mode = "Slave";
+ } else {
+ system_clock_mode = "Master";
+ }
+
+ switch (hdsp_pref_sync_ref (hdsp)) {
+ case HDSP_SYNC_FROM_WORD:
+ pref_sync_ref = "Word Clock";
+ break;
+ case HDSP_SYNC_FROM_ADAT_SYNC:
+ pref_sync_ref = "ADAT Sync";
+ break;
+ case HDSP_SYNC_FROM_SPDIF:
+ pref_sync_ref = "SPDIF";
+ break;
+ case HDSP_SYNC_FROM_ADAT1:
+ pref_sync_ref = "ADAT1";
+ break;
+ case HDSP_SYNC_FROM_ADAT2:
+ pref_sync_ref = "ADAT2";
+ break;
+ case HDSP_SYNC_FROM_ADAT3:
+ pref_sync_ref = "ADAT3";
+ break;
+ default:
+ pref_sync_ref = "Word Clock";
+ break;
+ }
+ snd_iprintf (buffer, "Preferred Sync Reference: %s\n", pref_sync_ref);
+
+ switch (hdsp_autosync_ref (hdsp)) {
+ case HDSP_AUTOSYNC_FROM_WORD:
+ autosync_ref = "Word Clock";
+ break;
+ case HDSP_AUTOSYNC_FROM_ADAT_SYNC:
+ autosync_ref = "ADAT Sync";
+ break;
+ case HDSP_AUTOSYNC_FROM_SPDIF:
+ autosync_ref = "SPDIF";
+ break;
+ case HDSP_AUTOSYNC_FROM_NONE:
+ autosync_ref = "None";
+ break;
+ case HDSP_AUTOSYNC_FROM_ADAT1:
+ autosync_ref = "ADAT1";
+ break;
+ case HDSP_AUTOSYNC_FROM_ADAT2:
+ autosync_ref = "ADAT2";
+ break;
+ case HDSP_AUTOSYNC_FROM_ADAT3:
+ autosync_ref = "ADAT3";
+ break;
+ default:
+ autosync_ref = "---";
+ break;
+ }
+ snd_iprintf (buffer, "AutoSync Reference: %s\n", autosync_ref);
+
+ snd_iprintf (buffer, "AutoSync Frequency: %d\n", hdsp_external_sample_rate(hdsp));
+
+ snd_iprintf (buffer, "System Clock Mode: %s\n", system_clock_mode);
+
+ snd_iprintf (buffer, "System Clock Frequency: %d\n", hdsp->system_sample_rate);
+
+ snd_iprintf(buffer, "\n");
+
+ switch (hdsp_spdif_in(hdsp)) {
+ case HDSP_SPDIFIN_OPTICAL:
+ snd_iprintf(buffer, "IEC958 input: Optical\n");
+ break;
+ case HDSP_SPDIFIN_COAXIAL:
+ snd_iprintf(buffer, "IEC958 input: Coaxial\n");
+ break;
+ case HDSP_SPDIFIN_INTERNAL:
+ snd_iprintf(buffer, "IEC958 input: Internal\n");
+ break;
+ case HDSP_SPDIFIN_AES:
+ snd_iprintf(buffer, "IEC958 input: AES\n");
+ break;
+ default:
+ snd_iprintf(buffer, "IEC958 input: ???\n");
+ break;
+ }
+
+ if (hdsp->control_register & HDSP_SPDIFOpticalOut) {
+ snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+ }
+
+ if (hdsp->control_register & HDSP_SPDIFProfessional) {
+ snd_iprintf(buffer, "IEC958 quality: Professional\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+ }
+
+ if (hdsp->control_register & HDSP_SPDIFEmphasis) {
+ snd_iprintf(buffer, "IEC958 emphasis: on\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 emphasis: off\n");
+ }
+
+ if (hdsp->control_register & HDSP_SPDIFNonAudio) {
+ snd_iprintf(buffer, "IEC958 NonAudio: on\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 NonAudio: off\n");
+ }
+ if ((x = hdsp_spdif_sample_rate (hdsp)) != 0) {
+ snd_iprintf (buffer, "IEC958 sample rate: %d\n", x);
+ } else {
+ snd_iprintf (buffer, "IEC958 sample rate: Error flag set\n");
+ }
+
+ snd_iprintf(buffer, "\n");
+
+ /* Sync Check */
+ x = status & HDSP_Sync0;
+ if (status & HDSP_Lock0) {
+ snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf(buffer, "ADAT1: No Lock\n");
+ }
+
+ switch (hdsp->io_type) {
+ case Digiface:
+ case H9652:
+ x = status & HDSP_Sync1;
+ if (status & HDSP_Lock1) {
+ snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf(buffer, "ADAT2: No Lock\n");
+ }
+ x = status & HDSP_Sync2;
+ if (status & HDSP_Lock2) {
+ snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf(buffer, "ADAT3: No Lock\n");
+ }
+ default:
+ /* relax */
+ break;
+ }
+
+ x = status & HDSP_SPDIFSync;
+ if (status & HDSP_SPDIFErrorFlag) {
+ snd_iprintf (buffer, "SPDIF: No Lock\n");
+ } else {
+ snd_iprintf (buffer, "SPDIF: %s\n", x ? "Sync" : "Lock");
+ }
+
+ x = status2 & HDSP_wc_sync;
+ if (status2 & HDSP_wc_lock) {
+ snd_iprintf (buffer, "Word Clock: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf (buffer, "Word Clock: No Lock\n");
+ }
+
+ x = status & HDSP_TimecodeSync;
+ if (status & HDSP_TimecodeLock) {
+ snd_iprintf(buffer, "ADAT Sync: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf(buffer, "ADAT Sync: No Lock\n");
+ }
+
+ snd_iprintf(buffer, "\n");
+
+ /* Informations about H9632 specific controls */
+ if (hdsp->io_type == H9632) {
+ char *tmp;
+
+ switch (hdsp_ad_gain(hdsp)) {
+ case 0:
+ tmp = "-10 dBV";
+ break;
+ case 1:
+ tmp = "+4 dBu";
+ break;
+ default:
+ tmp = "Lo Gain";
+ break;
+ }
+ snd_iprintf(buffer, "AD Gain : %s\n", tmp);
+
+ switch (hdsp_da_gain(hdsp)) {
+ case 0:
+ tmp = "Hi Gain";
+ break;
+ case 1:
+ tmp = "+4 dBu";
+ break;
+ default:
+ tmp = "-10 dBV";
+ break;
+ }
+ snd_iprintf(buffer, "DA Gain : %s\n", tmp);
+
+ switch (hdsp_phone_gain(hdsp)) {
+ case 0:
+ tmp = "0 dB";
+ break;
+ case 1:
+ tmp = "-6 dB";
+ break;
+ default:
+ tmp = "-12 dB";
+ break;
+ }
+ snd_iprintf(buffer, "Phones Gain : %s\n", tmp);
+
+ snd_iprintf(buffer, "XLR Breakout Cable : %s\n", hdsp_xlr_breakout_cable(hdsp) ? "yes" : "no");
+
+ if (hdsp->control_register & HDSP_AnalogExtensionBoard) {
+ snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n");
+ } else {
+ snd_iprintf(buffer, "AEB : off (ADAT1 external)\n");
+ }
+ snd_iprintf(buffer, "\n");
+ }
+
+}
+
+static void __devinit snd_hdsp_proc_init(hdsp_t *hdsp)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(hdsp->card, "hdsp", &entry))
+ snd_info_set_text_ops(entry, hdsp, 1024, snd_hdsp_proc_read);
+}
+
+static void snd_hdsp_free_buffers(hdsp_t *hdsp)
+{
+ snd_hammerfall_free_buffer(&hdsp->capture_dma_buf, hdsp->pci);
+ snd_hammerfall_free_buffer(&hdsp->playback_dma_buf, hdsp->pci);
+}
+
+static int __devinit snd_hdsp_initialize_memory(hdsp_t *hdsp)
+{
+ unsigned long pb_bus, cb_bus;
+
+ if (snd_hammerfall_get_buffer(hdsp->pci, &hdsp->capture_dma_buf, HDSP_DMA_AREA_BYTES) < 0 ||
+ snd_hammerfall_get_buffer(hdsp->pci, &hdsp->playback_dma_buf, HDSP_DMA_AREA_BYTES) < 0) {
+ if (hdsp->capture_dma_buf.area)
+ snd_dma_free_pages(&hdsp->capture_dma_buf);
+ printk(KERN_ERR "%s: no buffers available\n", hdsp->card_name);
+ return -ENOMEM;
+ }
+
+ /* Align to bus-space 64K boundary */
+
+ cb_bus = (hdsp->capture_dma_buf.addr + 0xFFFF) & ~0xFFFFl;
+ pb_bus = (hdsp->playback_dma_buf.addr + 0xFFFF) & ~0xFFFFl;
+
+ /* Tell the card where it is */
+
+ hdsp_write(hdsp, HDSP_inputBufferAddress, cb_bus);
+ hdsp_write(hdsp, HDSP_outputBufferAddress, pb_bus);
+
+ hdsp->capture_buffer = hdsp->capture_dma_buf.area + (cb_bus - hdsp->capture_dma_buf.addr);
+ hdsp->playback_buffer = hdsp->playback_dma_buf.area + (pb_bus - hdsp->playback_dma_buf.addr);
+
+ return 0;
+}
+
+static int snd_hdsp_set_defaults(hdsp_t *hdsp)
+{
+ unsigned int i;
+
+ /* ASSUMPTION: hdsp->lock is either held, or
+ there is no need to hold it (e.g. during module
+ initalization).
+ */
+
+ /* set defaults:
+
+ SPDIF Input via Coax
+ Master clock mode
+ maximum latency (7 => 2^7 = 8192 samples, 64Kbyte buffer,
+ which implies 2 4096 sample, 32Kbyte periods).
+ Enable line out.
+ */
+
+ hdsp->control_register = HDSP_ClockModeMaster |
+ HDSP_SPDIFInputCoaxial |
+ hdsp_encode_latency(7) |
+ HDSP_LineOut;
+
+
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+
+#ifdef SNDRV_BIG_ENDIAN
+ hdsp->control2_register = HDSP_BIGENDIAN_MODE;
+#else
+ hdsp->control2_register = 0;
+#endif
+ if (hdsp->io_type == H9652) {
+ snd_hdsp_9652_enable_mixer (hdsp);
+ } else {
+ hdsp_write (hdsp, HDSP_control2Reg, hdsp->control2_register);
+ }
+
+ hdsp_reset_hw_pointer(hdsp);
+ hdsp_compute_period_size(hdsp);
+
+ /* silence everything */
+
+ for (i = 0; i < HDSP_MATRIX_MIXER_SIZE; ++i) {
+ hdsp->mixer_matrix[i] = MINUS_INFINITY_GAIN;
+ }
+
+ for (i = 0; i < ((hdsp->io_type == H9652 || hdsp->io_type == H9632) ? 1352 : HDSP_MATRIX_MIXER_SIZE); ++i) {
+ if (hdsp_write_gain (hdsp, i, MINUS_INFINITY_GAIN)) {
+ return -EIO;
+ }
+ }
+
+ /* H9632 specific defaults */
+ if (hdsp->io_type == H9632) {
+ hdsp->control_register |= (HDSP_DAGainPlus4dBu | HDSP_ADGainPlus4dBu | HDSP_PhoneGain0dB);
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ }
+
+ /* set a default rate so that the channel map is set up.
+ */
+
+ hdsp_set_rate(hdsp, 48000, 1);
+
+ return 0;
+}
+
+static void hdsp_midi_tasklet(unsigned long arg)
+{
+ hdsp_t *hdsp = (hdsp_t *)arg;
+
+ if (hdsp->midi[0].pending) {
+ snd_hdsp_midi_input_read (&hdsp->midi[0]);
+ }
+ if (hdsp->midi[1].pending) {
+ snd_hdsp_midi_input_read (&hdsp->midi[1]);
+ }
+}
+
+static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ hdsp_t *hdsp = (hdsp_t *) dev_id;
+ unsigned int status;
+ int audio;
+ int midi0;
+ int midi1;
+ unsigned int midi0status;
+ unsigned int midi1status;
+ int schedule = 0;
+
+ status = hdsp_read(hdsp, HDSP_statusRegister);
+
+ audio = status & HDSP_audioIRQPending;
+ midi0 = status & HDSP_midi0IRQPending;
+ midi1 = status & HDSP_midi1IRQPending;
+
+ if (!audio && !midi0 && !midi1) {
+ return IRQ_NONE;
+ }
+
+ hdsp_write(hdsp, HDSP_interruptConfirmation, 0);
+
+ midi0status = hdsp_read (hdsp, HDSP_midiStatusIn0) & 0xff;
+ midi1status = hdsp_read (hdsp, HDSP_midiStatusIn1) & 0xff;
+
+ if (audio) {
+ if (hdsp->capture_substream) {
+ snd_pcm_period_elapsed(hdsp->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+ }
+
+ if (hdsp->playback_substream) {
+ snd_pcm_period_elapsed(hdsp->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
+ }
+ }
+
+ if (midi0 && midi0status) {
+ if (hdsp->use_midi_tasklet) {
+ /* we disable interrupts for this input until processing is done */
+ hdsp->control_register &= ~HDSP_Midi0InterruptEnable;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ hdsp->midi[0].pending = 1;
+ schedule = 1;
+ } else {
+ snd_hdsp_midi_input_read (&hdsp->midi[0]);
+ }
+ }
+ if (hdsp->io_type != Multiface && hdsp->io_type != H9632 && midi1 && midi1status) {
+ if (hdsp->use_midi_tasklet) {
+ /* we disable interrupts for this input until processing is done */
+ hdsp->control_register &= ~HDSP_Midi1InterruptEnable;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ hdsp->midi[1].pending = 1;
+ schedule = 1;
+ } else {
+ snd_hdsp_midi_input_read (&hdsp->midi[1]);
+ }
+ }
+ if (hdsp->use_midi_tasklet && schedule)
+ tasklet_hi_schedule(&hdsp->midi_tasklet);
+ return IRQ_HANDLED;
+}
+
+static snd_pcm_uframes_t snd_hdsp_hw_pointer(snd_pcm_substream_t *substream)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ return hdsp_hw_pointer(hdsp);
+}
+
+static char *hdsp_channel_buffer_location(hdsp_t *hdsp,
+ int stream,
+ int channel)
+
+{
+ int mapped_channel;
+
+ snd_assert(channel >= 0 && channel < hdsp->max_channels, return NULL);
+
+ if ((mapped_channel = hdsp->channel_map[channel]) < 0) {
+ return NULL;
+ }
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ return hdsp->capture_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES);
+ } else {
+ return hdsp->playback_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES);
+ }
+}
+
+static int snd_hdsp_playback_copy(snd_pcm_substream_t *substream, int channel,
+ snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ char *channel_buf;
+
+ snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+
+ channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
+ snd_assert(channel_buf != NULL, return -EIO);
+ if (copy_from_user(channel_buf + pos * 4, src, count * 4))
+ return -EFAULT;
+ return count;
+}
+
+static int snd_hdsp_capture_copy(snd_pcm_substream_t *substream, int channel,
+ snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ char *channel_buf;
+
+ snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+
+ channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
+ snd_assert(channel_buf != NULL, return -EIO);
+ if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
+ return -EFAULT;
+ return count;
+}
+
+static int snd_hdsp_hw_silence(snd_pcm_substream_t *substream, int channel,
+ snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ char *channel_buf;
+
+ channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
+ snd_assert(channel_buf != NULL, return -EIO);
+ memset(channel_buf + pos * 4, 0, count * 4);
+ return count;
+}
+
+static int snd_hdsp_reset(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ snd_pcm_substream_t *other;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ other = hdsp->capture_substream;
+ else
+ other = hdsp->playback_substream;
+ if (hdsp->running)
+ runtime->status->hw_ptr = hdsp_hw_pointer(hdsp);
+ else
+ runtime->status->hw_ptr = 0;
+ if (other) {
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ snd_pcm_runtime_t *oruntime = other->runtime;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == other) {
+ oruntime->status->hw_ptr = runtime->status->hw_ptr;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int snd_hdsp_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *params)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ int err;
+ pid_t this_pid;
+ pid_t other_pid;
+
+ if (hdsp_check_for_iobox (hdsp)) {
+ return -EIO;
+ }
+
+ if (hdsp_check_for_firmware(hdsp)) {
+ if (hdsp->state & HDSP_FirmwareCached) {
+ if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
+ snd_printk("Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n");
+ }
+ } else {
+ snd_printk("Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n");
+ }
+ return -EIO;
+ }
+
+ spin_lock_irq(&hdsp->lock);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ hdsp->control_register &= ~(HDSP_SPDIFProfessional | HDSP_SPDIFNonAudio | HDSP_SPDIFEmphasis);
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register |= hdsp->creg_spdif_stream);
+ this_pid = hdsp->playback_pid;
+ other_pid = hdsp->capture_pid;
+ } else {
+ this_pid = hdsp->capture_pid;
+ other_pid = hdsp->playback_pid;
+ }
+
+ if ((other_pid > 0) && (this_pid != other_pid)) {
+
+ /* The other stream is open, and not by the same
+ task as this one. Make sure that the parameters
+ that matter are the same.
+ */
+
+ if (params_rate(params) != hdsp->system_sample_rate) {
+ spin_unlock_irq(&hdsp->lock);
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+ return -EBUSY;
+ }
+
+ if (params_period_size(params) != hdsp->period_bytes / 4) {
+ spin_unlock_irq(&hdsp->lock);
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return -EBUSY;
+ }
+
+ /* We're fine. */
+
+ spin_unlock_irq(&hdsp->lock);
+ return 0;
+
+ } else {
+ spin_unlock_irq(&hdsp->lock);
+ }
+
+ /* how to make sure that the rate matches an externally-set one ?
+ */
+
+ spin_lock_irq(&hdsp->lock);
+ if ((err = hdsp_set_rate(hdsp, params_rate(params), 0)) < 0) {
+ spin_unlock_irq(&hdsp->lock);
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+ return err;
+ } else {
+ spin_unlock_irq(&hdsp->lock);
+ }
+
+ if ((err = hdsp_set_interrupt_interval(hdsp, params_period_size(params))) < 0) {
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_hdsp_channel_info(snd_pcm_substream_t *substream,
+ snd_pcm_channel_info_t *info)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ int mapped_channel;
+
+ snd_assert(info->channel < hdsp->max_channels, return -EINVAL);
+
+ if ((mapped_channel = hdsp->channel_map[info->channel]) < 0) {
+ return -EINVAL;
+ }
+
+ info->offset = mapped_channel * HDSP_CHANNEL_BUFFER_BYTES;
+ info->first = 0;
+ info->step = 32;
+ return 0;
+}
+
+static int snd_hdsp_ioctl(snd_pcm_substream_t *substream,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL1_RESET:
+ {
+ return snd_hdsp_reset(substream);
+ }
+ case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+ {
+ snd_pcm_channel_info_t *info = arg;
+ return snd_hdsp_channel_info(substream, info);
+ }
+ default:
+ break;
+ }
+
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_hdsp_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ snd_pcm_substream_t *other;
+ int running;
+
+ if (hdsp_check_for_iobox (hdsp)) {
+ return -EIO;
+ }
+
+ if (hdsp_check_for_firmware(hdsp)) {
+ if (hdsp->state & HDSP_FirmwareCached) {
+ if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
+ snd_printk("Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n");
+ }
+ } else {
+ snd_printk("Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n");
+ }
+ return -EIO;
+ }
+
+ spin_lock(&hdsp->lock);
+ running = hdsp->running;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ running |= 1 << substream->stream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ running &= ~(1 << substream->stream);
+ break;
+ default:
+ snd_BUG();
+ spin_unlock(&hdsp->lock);
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ other = hdsp->capture_substream;
+ else
+ other = hdsp->playback_substream;
+
+ if (other) {
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == other) {
+ snd_pcm_trigger_done(s, substream);
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ running |= 1 << s->stream;
+ else
+ running &= ~(1 << s->stream);
+ goto _ok;
+ }
+ }
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
+ substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ hdsp_silence_playback(hdsp);
+ } else {
+ if (running &&
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ hdsp_silence_playback(hdsp);
+ }
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ hdsp_silence_playback(hdsp);
+ }
+ _ok:
+ snd_pcm_trigger_done(substream, substream);
+ if (!hdsp->running && running)
+ hdsp_start_audio(hdsp);
+ else if (hdsp->running && !running)
+ hdsp_stop_audio(hdsp);
+ hdsp->running = running;
+ spin_unlock(&hdsp->lock);
+
+ return 0;
+}
+
+static int snd_hdsp_prepare(snd_pcm_substream_t *substream)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ int result = 0;
+
+ if (hdsp_check_for_iobox (hdsp)) {
+ return -EIO;
+ }
+
+ if (hdsp_check_for_firmware(hdsp)) {
+ if (hdsp->state & HDSP_FirmwareCached) {
+ if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
+ snd_printk("Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n");
+ }
+ } else {
+ snd_printk("Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n");
+ }
+ return -EIO;
+ }
+
+ spin_lock_irq(&hdsp->lock);
+ if (!hdsp->running)
+ hdsp_reset_hw_pointer(hdsp);
+ spin_unlock_irq(&hdsp->lock);
+ return result;
+}
+
+static snd_pcm_hardware_t snd_hdsp_playback_subinfo =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_DOUBLE),
+#ifdef SNDRV_BIG_ENDIAN
+ .formats = SNDRV_PCM_FMTBIT_S32_BE,
+#else
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+#endif
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 14,
+ .channels_max = HDSP_MAX_CHANNELS,
+ .buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
+ .period_bytes_min = (64 * 4) * 10,
+ .period_bytes_max = (8192 * 4) * HDSP_MAX_CHANNELS,
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0
+};
+
+static snd_pcm_hardware_t snd_hdsp_capture_subinfo =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_SYNC_START),
+#ifdef SNDRV_BIG_ENDIAN
+ .formats = SNDRV_PCM_FMTBIT_S32_BE,
+#else
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+#endif
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 14,
+ .channels_max = HDSP_MAX_CHANNELS,
+ .buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
+ .period_bytes_min = (64 * 4) * 10,
+ .period_bytes_max = (8192 * 4) * HDSP_MAX_CHANNELS,
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0
+};
+
+static unsigned int hdsp_period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+
+static snd_pcm_hw_constraint_list_t hdsp_hw_constraints_period_sizes = {
+ .count = ARRAY_SIZE(hdsp_period_sizes),
+ .list = hdsp_period_sizes,
+ .mask = 0
+};
+
+static unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 };
+
+static snd_pcm_hw_constraint_list_t hdsp_hw_constraints_9632_sample_rates = {
+ .count = ARRAY_SIZE(hdsp_9632_sample_rates),
+ .list = hdsp_9632_sample_rates,
+ .mask = 0
+};
+
+static int snd_hdsp_hw_rule_in_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ hdsp_t *hdsp = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (hdsp->io_type == H9632) {
+ unsigned int list[3];
+ list[0] = hdsp->qs_in_channels;
+ list[1] = hdsp->ds_in_channels;
+ list[2] = hdsp->ss_in_channels;
+ return snd_interval_list(c, 3, list, 0);
+ } else {
+ unsigned int list[2];
+ list[0] = hdsp->ds_in_channels;
+ list[1] = hdsp->ss_in_channels;
+ return snd_interval_list(c, 2, list, 0);
+ }
+}
+
+static int snd_hdsp_hw_rule_out_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ unsigned int list[3];
+ hdsp_t *hdsp = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (hdsp->io_type == H9632) {
+ list[0] = hdsp->qs_out_channels;
+ list[1] = hdsp->ds_out_channels;
+ list[2] = hdsp->ss_out_channels;
+ return snd_interval_list(c, 3, list, 0);
+ } else {
+ list[0] = hdsp->ds_out_channels;
+ list[1] = hdsp->ss_out_channels;
+ }
+ return snd_interval_list(c, 2, list, 0);
+}
+
+static int snd_hdsp_hw_rule_in_channels_rate(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ hdsp_t *hdsp = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (r->min > 96000 && hdsp->io_type == H9632) {
+ snd_interval_t t = {
+ .min = hdsp->qs_in_channels,
+ .max = hdsp->qs_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->min > 48000 && r->max <= 96000) {
+ snd_interval_t t = {
+ .min = hdsp->ds_in_channels,
+ .max = hdsp->ds_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->max < 64000) {
+ snd_interval_t t = {
+ .min = hdsp->ss_in_channels,
+ .max = hdsp->ss_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ }
+ return 0;
+}
+
+static int snd_hdsp_hw_rule_out_channels_rate(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ hdsp_t *hdsp = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (r->min > 96000 && hdsp->io_type == H9632) {
+ snd_interval_t t = {
+ .min = hdsp->qs_out_channels,
+ .max = hdsp->qs_out_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->min > 48000 && r->max <= 96000) {
+ snd_interval_t t = {
+ .min = hdsp->ds_out_channels,
+ .max = hdsp->ds_out_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->max < 64000) {
+ snd_interval_t t = {
+ .min = hdsp->ss_out_channels,
+ .max = hdsp->ss_out_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ }
+ return 0;
+}
+
+static int snd_hdsp_hw_rule_rate_out_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ hdsp_t *hdsp = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (c->min >= hdsp->ss_out_channels) {
+ snd_interval_t t = {
+ .min = 32000,
+ .max = 48000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdsp->qs_out_channels && hdsp->io_type == H9632) {
+ snd_interval_t t = {
+ .min = 128000,
+ .max = 192000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdsp->ds_out_channels) {
+ snd_interval_t t = {
+ .min = 64000,
+ .max = 96000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ }
+ return 0;
+}
+
+static int snd_hdsp_hw_rule_rate_in_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ hdsp_t *hdsp = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (c->min >= hdsp->ss_in_channels) {
+ snd_interval_t t = {
+ .min = 32000,
+ .max = 48000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdsp->qs_in_channels && hdsp->io_type == H9632) {
+ snd_interval_t t = {
+ .min = 128000,
+ .max = 192000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdsp->ds_in_channels) {
+ snd_interval_t t = {
+ .min = 64000,
+ .max = 96000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ }
+ return 0;
+}
+
+static int snd_hdsp_playback_open(snd_pcm_substream_t *substream)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (hdsp_check_for_iobox (hdsp)) {
+ return -EIO;
+ }
+
+ if (hdsp_check_for_firmware(hdsp)) {
+ if (hdsp->state & HDSP_FirmwareCached) {
+ if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
+ snd_printk("Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n");
+ }
+ } else {
+ snd_printk("Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n");
+ }
+ return -EIO;
+ }
+
+ spin_lock_irq(&hdsp->lock);
+
+ snd_pcm_set_sync(substream);
+
+ runtime->hw = snd_hdsp_playback_subinfo;
+ runtime->dma_area = hdsp->playback_buffer;
+ runtime->dma_bytes = HDSP_DMA_AREA_BYTES;
+
+ hdsp->playback_pid = current->pid;
+ hdsp->playback_substream = substream;
+
+ spin_unlock_irq(&hdsp->lock);
+
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes);
+ if (hdsp->io_type == H9632) {
+ runtime->hw.channels_min = hdsp->qs_out_channels;
+ runtime->hw.channels_max = hdsp->ss_out_channels;
+ runtime->hw.rate_max = 192000;
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates);
+ }
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_hdsp_hw_rule_out_channels, hdsp,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_hdsp_hw_rule_out_channels_rate, hdsp,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_hdsp_hw_rule_rate_out_channels, hdsp,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ hdsp->creg_spdif_stream = hdsp->creg_spdif;
+ hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+ return 0;
+}
+
+static int snd_hdsp_playback_release(snd_pcm_substream_t *substream)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&hdsp->lock);
+
+ hdsp->playback_pid = -1;
+ hdsp->playback_substream = NULL;
+
+ spin_unlock_irq(&hdsp->lock);
+
+ hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+ return 0;
+}
+
+
+static int snd_hdsp_capture_open(snd_pcm_substream_t *substream)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (hdsp_check_for_iobox (hdsp)) {
+ return -EIO;
+ }
+
+ if (hdsp_check_for_firmware(hdsp)) {
+ if (hdsp->state & HDSP_FirmwareCached) {
+ if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
+ snd_printk("Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n");
+ }
+ } else {
+ snd_printk("Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n");
+ }
+ return -EIO;
+ }
+
+ spin_lock_irq(&hdsp->lock);
+
+ snd_pcm_set_sync(substream);
+
+ runtime->hw = snd_hdsp_capture_subinfo;
+ runtime->dma_area = hdsp->capture_buffer;
+ runtime->dma_bytes = HDSP_DMA_AREA_BYTES;
+
+ hdsp->capture_pid = current->pid;
+ hdsp->capture_substream = substream;
+
+ spin_unlock_irq(&hdsp->lock);
+
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes);
+ if (hdsp->io_type == H9632) {
+ runtime->hw.channels_min = hdsp->qs_in_channels;
+ runtime->hw.channels_max = hdsp->ss_in_channels;
+ runtime->hw.rate_max = 192000;
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates);
+ }
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_hdsp_hw_rule_in_channels, hdsp,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_hdsp_hw_rule_in_channels_rate, hdsp,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_hdsp_hw_rule_rate_in_channels, hdsp,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ return 0;
+}
+
+static int snd_hdsp_capture_release(snd_pcm_substream_t *substream)
+{
+ hdsp_t *hdsp = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&hdsp->lock);
+
+ hdsp->capture_pid = -1;
+ hdsp->capture_substream = NULL;
+
+ spin_unlock_irq(&hdsp->lock);
+ return 0;
+}
+
+static int snd_hdsp_hwdep_dummy_op(snd_hwdep_t *hw, struct file *file)
+{
+ /* we have nothing to initialize but the call is required */
+ return 0;
+}
+
+
+/* helper functions for copying meter values */
+static inline int copy_u32_le(void __user *dest, void __iomem *src)
+{
+ u32 val = readl(src);
+ return copy_to_user(dest, &val, 4);
+}
+
+static inline int copy_u64_le(void __user *dest, void __iomem *src_low, void __iomem *src_high)
+{
+ u32 rms_low, rms_high;
+ u64 rms;
+ rms_low = readl(src_low);
+ rms_high = readl(src_high);
+ rms = ((u64)rms_high << 32) | rms_low;
+ return copy_to_user(dest, &rms, 8);
+}
+
+static inline int copy_u48_le(void __user *dest, void __iomem *src_low, void __iomem *src_high)
+{
+ u32 rms_low, rms_high;
+ u64 rms;
+ rms_low = readl(src_low) & 0xffffff00;
+ rms_high = readl(src_high) & 0xffffff00;
+ rms = ((u64)rms_high << 32) | rms_low;
+ return copy_to_user(dest, &rms, 8);
+}
+
+static int hdsp_9652_get_peak(hdsp_t *hdsp, hdsp_peak_rms_t __user *peak_rms)
+{
+ int doublespeed = 0;
+ int i, j, channels, ofs;
+
+ if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DoubleSpeedStatus)
+ doublespeed = 1;
+ channels = doublespeed ? 14 : 26;
+ for (i = 0, j = 0; i < 26; ++i) {
+ if (doublespeed && (i & 4))
+ continue;
+ ofs = HDSP_9652_peakBase - j * 4;
+ if (copy_u32_le(&peak_rms->input_peaks[i], hdsp->iobase + ofs))
+ return -EFAULT;
+ ofs -= channels * 4;
+ if (copy_u32_le(&peak_rms->playback_peaks[i], hdsp->iobase + ofs))
+ return -EFAULT;
+ ofs -= channels * 4;
+ if (copy_u32_le(&peak_rms->output_peaks[i], hdsp->iobase + ofs))
+ return -EFAULT;
+ ofs = HDSP_9652_rmsBase + j * 8;
+ if (copy_u48_le(&peak_rms->input_rms[i], hdsp->iobase + ofs,
+ hdsp->iobase + ofs + 4))
+ return -EFAULT;
+ ofs += channels * 8;
+ if (copy_u48_le(&peak_rms->playback_rms[i], hdsp->iobase + ofs,
+ hdsp->iobase + ofs + 4))
+ return -EFAULT;
+ ofs += channels * 8;
+ if (copy_u48_le(&peak_rms->output_rms[i], hdsp->iobase + ofs,
+ hdsp->iobase + ofs + 4))
+ return -EFAULT;
+ j++;
+ }
+ return 0;
+}
+
+static int hdsp_9632_get_peak(hdsp_t *hdsp, hdsp_peak_rms_t __user *peak_rms)
+{
+ int i, j;
+ hdsp_9632_meters_t __iomem *m;
+ int doublespeed = 0;
+
+ if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DoubleSpeedStatus)
+ doublespeed = 1;
+ m = (hdsp_9632_meters_t __iomem *)(hdsp->iobase+HDSP_9632_metersBase);
+ for (i = 0, j = 0; i < 16; ++i, ++j) {
+ if (copy_u32_le(&peak_rms->input_peaks[i], &m->input_peak[j]))
+ return -EFAULT;
+ if (copy_u32_le(&peak_rms->playback_peaks[i], &m->playback_peak[j]))
+ return -EFAULT;
+ if (copy_u32_le(&peak_rms->output_peaks[i], &m->output_peak[j]))
+ return -EFAULT;
+ if (copy_u64_le(&peak_rms->input_rms[i], &m->input_rms_low[j],
+ &m->input_rms_high[j]))
+ return -EFAULT;
+ if (copy_u64_le(&peak_rms->playback_rms[i], &m->playback_rms_low[j],
+ &m->playback_rms_high[j]))
+ return -EFAULT;
+ if (copy_u64_le(&peak_rms->output_rms[i], &m->output_rms_low[j],
+ &m->output_rms_high[j]))
+ return -EFAULT;
+ if (doublespeed && i == 3) i += 4;
+ }
+ return 0;
+}
+
+static int hdsp_get_peak(hdsp_t *hdsp, hdsp_peak_rms_t __user *peak_rms)
+{
+ int i;
+
+ for (i = 0; i < 26; i++) {
+ if (copy_u32_le(&peak_rms->playback_peaks[i],
+ hdsp->iobase + HDSP_playbackPeakLevel + i * 4))
+ return -EFAULT;
+ if (copy_u32_le(&peak_rms->input_peaks[i],
+ hdsp->iobase + HDSP_inputPeakLevel + i * 4))
+ return -EFAULT;
+ }
+ for (i = 0; i < 28; i++) {
+ if (copy_u32_le(&peak_rms->output_peaks[i],
+ hdsp->iobase + HDSP_outputPeakLevel + i * 4))
+ return -EFAULT;
+ }
+ for (i = 0; i < 26; ++i) {
+ if (copy_u64_le(&peak_rms->playback_rms[i],
+ hdsp->iobase + HDSP_playbackRmsLevel + i * 8 + 4,
+ hdsp->iobase + HDSP_playbackRmsLevel + i * 8))
+ return -EFAULT;
+ if (copy_u64_le(&peak_rms->input_rms[i],
+ hdsp->iobase + HDSP_inputRmsLevel + i * 8 + 4,
+ hdsp->iobase + HDSP_inputRmsLevel + i * 8))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int snd_hdsp_hwdep_ioctl(snd_hwdep_t *hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ hdsp_t *hdsp = (hdsp_t *)hw->private_data;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case SNDRV_HDSP_IOCTL_GET_PEAK_RMS: {
+ hdsp_peak_rms_t __user *peak_rms = (hdsp_peak_rms_t __user *)arg;
+
+ if (!(hdsp->state & HDSP_FirmwareLoaded)) {
+ snd_printk(KERN_ERR "Hammerfall-DSP: firmware needs to be uploaded to the card.\n");
+ return -EINVAL;
+ }
+
+ switch (hdsp->io_type) {
+ case H9652:
+ return hdsp_9652_get_peak(hdsp, peak_rms);
+ case H9632:
+ return hdsp_9632_get_peak(hdsp, peak_rms);
+ default:
+ return hdsp_get_peak(hdsp, peak_rms);
+ }
+ }
+ case SNDRV_HDSP_IOCTL_GET_CONFIG_INFO: {
+ hdsp_config_info_t info;
+ unsigned long flags;
+ int i;
+
+ if (!(hdsp->state & HDSP_FirmwareLoaded)) {
+ snd_printk("Hammerfall-DSP: Firmware needs to be uploaded to the card.\n");
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&hdsp->lock, flags);
+ info.pref_sync_ref = (unsigned char)hdsp_pref_sync_ref(hdsp);
+ info.wordclock_sync_check = (unsigned char)hdsp_wc_sync_check(hdsp);
+ if (hdsp->io_type != H9632) {
+ info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp);
+ }
+ info.spdif_sync_check = (unsigned char)hdsp_spdif_sync_check(hdsp);
+ for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != H9632) ? 3 : 1); ++i) {
+ info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
+ }
+ info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
+ info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp);
+ info.spdif_professional = (unsigned char)hdsp_spdif_professional(hdsp);
+ info.spdif_emphasis = (unsigned char)hdsp_spdif_emphasis(hdsp);
+ info.spdif_nonaudio = (unsigned char)hdsp_spdif_nonaudio(hdsp);
+ info.spdif_sample_rate = hdsp_spdif_sample_rate(hdsp);
+ info.system_sample_rate = hdsp->system_sample_rate;
+ info.autosync_sample_rate = hdsp_external_sample_rate(hdsp);
+ info.system_clock_mode = (unsigned char)hdsp_system_clock_mode(hdsp);
+ info.clock_source = (unsigned char)hdsp_clock_source(hdsp);
+ info.autosync_ref = (unsigned char)hdsp_autosync_ref(hdsp);
+ info.line_out = (unsigned char)hdsp_line_out(hdsp);
+ if (hdsp->io_type == H9632) {
+ info.da_gain = (unsigned char)hdsp_da_gain(hdsp);
+ info.ad_gain = (unsigned char)hdsp_ad_gain(hdsp);
+ info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
+ info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
+
+ }
+ if (hdsp->io_type == H9632 || hdsp->io_type == H9652) {
+ info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
+ }
+ spin_unlock_irqrestore(&hdsp->lock, flags);
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+ }
+ case SNDRV_HDSP_IOCTL_GET_9632_AEB: {
+ hdsp_9632_aeb_t h9632_aeb;
+
+ if (hdsp->io_type != H9632) return -EINVAL;
+ h9632_aeb.aebi = hdsp->ss_in_channels - H9632_SS_CHANNELS;
+ h9632_aeb.aebo = hdsp->ss_out_channels - H9632_SS_CHANNELS;
+ if (copy_to_user(argp, &h9632_aeb, sizeof(h9632_aeb)))
+ return -EFAULT;
+ break;
+ }
+ case SNDRV_HDSP_IOCTL_GET_VERSION: {
+ hdsp_version_t hdsp_version;
+ int err;
+
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return -EINVAL;
+ if (hdsp->io_type == Undefined) {
+ if ((err = hdsp_get_iobox_version(hdsp)) < 0) {
+ return err;
+ }
+ }
+ hdsp_version.io_type = hdsp->io_type;
+ hdsp_version.firmware_rev = hdsp->firmware_rev;
+ if ((err = copy_to_user(argp, &hdsp_version, sizeof(hdsp_version)))) {
+ return -EFAULT;
+ }
+ break;
+ }
+ case SNDRV_HDSP_IOCTL_UPLOAD_FIRMWARE: {
+ hdsp_firmware_t __user *firmware;
+ u32 __user *firmware_data;
+ int err;
+
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return -EINVAL;
+ /* SNDRV_HDSP_IOCTL_GET_VERSION must have been called */
+ if (hdsp->io_type == Undefined) return -EINVAL;
+
+ if (hdsp->state & (HDSP_FirmwareCached | HDSP_FirmwareLoaded))
+ return -EBUSY;
+
+ snd_printk("Hammerfall-DSP: initializing firmware upload\n");
+ firmware = (hdsp_firmware_t __user *)argp;
+
+ if (get_user(firmware_data, &firmware->firmware_data)) {
+ return -EFAULT;
+ }
+
+ if (hdsp_check_for_iobox (hdsp)) {
+ return -EIO;
+ }
+
+ if (copy_from_user(hdsp->firmware_cache, firmware_data, sizeof(hdsp->firmware_cache)) != 0) {
+ return -EFAULT;
+ }
+
+ hdsp->state |= HDSP_FirmwareCached;
+
+ if ((err = snd_hdsp_load_firmware_from_cache(hdsp)) < 0) {
+ return err;
+ }
+
+ if (!(hdsp->state & HDSP_InitializationComplete)) {
+ if ((err = snd_hdsp_enable_io(hdsp)) < 0) {
+ return err;
+ }
+
+ snd_hdsp_initialize_channels(hdsp);
+ snd_hdsp_initialize_midi_flush(hdsp);
+
+ if ((err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp)) < 0) {
+ snd_printk("Hammerfall-DSP: error creating alsa devices\n");
+ return err;
+ }
+ }
+ break;
+ }
+ case SNDRV_HDSP_IOCTL_GET_MIXER: {
+ hdsp_mixer_t __user *mixer = (hdsp_mixer_t __user *)argp;
+ if (copy_to_user(mixer->matrix, hdsp->mixer_matrix, sizeof(unsigned short)*HDSP_MATRIX_MIXER_SIZE))
+ return -EFAULT;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_ops_t snd_hdsp_playback_ops = {
+ .open = snd_hdsp_playback_open,
+ .close = snd_hdsp_playback_release,
+ .ioctl = snd_hdsp_ioctl,
+ .hw_params = snd_hdsp_hw_params,
+ .prepare = snd_hdsp_prepare,
+ .trigger = snd_hdsp_trigger,
+ .pointer = snd_hdsp_hw_pointer,
+ .copy = snd_hdsp_playback_copy,
+ .silence = snd_hdsp_hw_silence,
+};
+
+static snd_pcm_ops_t snd_hdsp_capture_ops = {
+ .open = snd_hdsp_capture_open,
+ .close = snd_hdsp_capture_release,
+ .ioctl = snd_hdsp_ioctl,
+ .hw_params = snd_hdsp_hw_params,
+ .prepare = snd_hdsp_prepare,
+ .trigger = snd_hdsp_trigger,
+ .pointer = snd_hdsp_hw_pointer,
+ .copy = snd_hdsp_capture_copy,
+};
+
+static int __devinit snd_hdsp_create_hwdep(snd_card_t *card,
+ hdsp_t *hdsp)
+{
+ snd_hwdep_t *hw;
+ int err;
+
+ if ((err = snd_hwdep_new(card, "HDSP hwdep", 0, &hw)) < 0)
+ return err;
+
+ hdsp->hwdep = hw;
+ hw->private_data = hdsp;
+ strcpy(hw->name, "HDSP hwdep interface");
+
+ hw->ops.open = snd_hdsp_hwdep_dummy_op;
+ hw->ops.ioctl = snd_hdsp_hwdep_ioctl;
+ hw->ops.release = snd_hdsp_hwdep_dummy_op;
+
+ return 0;
+}
+
+static int snd_hdsp_create_pcm(snd_card_t *card, hdsp_t *hdsp)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(card, hdsp->card_name, 0, 1, 1, &pcm)) < 0)
+ return err;
+
+ hdsp->pcm = pcm;
+ pcm->private_data = hdsp;
+ strcpy(pcm->name, hdsp->card_name);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops);
+
+ pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+ return 0;
+}
+
+static void snd_hdsp_9652_enable_mixer (hdsp_t *hdsp)
+{
+ hdsp->control2_register |= HDSP_9652_ENABLE_MIXER;
+ hdsp_write (hdsp, HDSP_control2Reg, hdsp->control2_register);
+}
+
+static int snd_hdsp_enable_io (hdsp_t *hdsp)
+{
+ int i;
+
+ if (hdsp_fifo_wait (hdsp, 0, 100)) {
+ snd_printk("Hammerfall-DSP: enable_io fifo_wait failed\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < hdsp->max_channels; ++i) {
+ hdsp_write (hdsp, HDSP_inputEnable + (4 * i), 1);
+ hdsp_write (hdsp, HDSP_outputEnable + (4 * i), 1);
+ }
+
+ return 0;
+}
+
+static void snd_hdsp_initialize_channels(hdsp_t *hdsp)
+{
+ int status, aebi_channels, aebo_channels;
+
+ switch (hdsp->io_type) {
+ case Digiface:
+ hdsp->card_name = "RME Hammerfall DSP + Digiface";
+ hdsp->ss_in_channels = hdsp->ss_out_channels = DIGIFACE_SS_CHANNELS;
+ hdsp->ds_in_channels = hdsp->ds_out_channels = DIGIFACE_DS_CHANNELS;
+ break;
+
+ case H9652:
+ hdsp->card_name = "RME Hammerfall HDSP 9652";
+ hdsp->ss_in_channels = hdsp->ss_out_channels = H9652_SS_CHANNELS;
+ hdsp->ds_in_channels = hdsp->ds_out_channels = H9652_DS_CHANNELS;
+ break;
+
+ case H9632:
+ status = hdsp_read(hdsp, HDSP_statusRegister);
+ /* HDSP_AEBx bits are low when AEB are connected */
+ aebi_channels = (status & HDSP_AEBI) ? 0 : 4;
+ aebo_channels = (status & HDSP_AEBO) ? 0 : 4;
+ hdsp->card_name = "RME Hammerfall HDSP 9632";
+ hdsp->ss_in_channels = H9632_SS_CHANNELS+aebi_channels;
+ hdsp->ds_in_channels = H9632_DS_CHANNELS+aebi_channels;
+ hdsp->qs_in_channels = H9632_QS_CHANNELS+aebi_channels;
+ hdsp->ss_out_channels = H9632_SS_CHANNELS+aebo_channels;
+ hdsp->ds_out_channels = H9632_DS_CHANNELS+aebo_channels;
+ hdsp->qs_out_channels = H9632_QS_CHANNELS+aebo_channels;
+ break;
+
+ case Multiface:
+ hdsp->card_name = "RME Hammerfall DSP + Multiface";
+ hdsp->ss_in_channels = hdsp->ss_out_channels = MULTIFACE_SS_CHANNELS;
+ hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
+ break;
+
+ default:
+ /* should never get here */
+ break;
+ }
+}
+
+static void snd_hdsp_initialize_midi_flush (hdsp_t *hdsp)
+{
+ snd_hdsp_flush_midi_input (hdsp, 0);
+ snd_hdsp_flush_midi_input (hdsp, 1);
+}
+
+static int snd_hdsp_create_alsa_devices(snd_card_t *card, hdsp_t *hdsp)
+{
+ int err;
+
+ if ((err = snd_hdsp_create_pcm(card, hdsp)) < 0) {
+ snd_printk("Hammerfall-DSP: Error creating pcm interface\n");
+ return err;
+ }
+
+
+ if ((err = snd_hdsp_create_midi(card, hdsp, 0)) < 0) {
+ snd_printk("Hammerfall-DSP: Error creating first midi interface\n");
+ return err;
+ }
+
+ if (hdsp->io_type == Digiface || hdsp->io_type == H9652) {
+ if ((err = snd_hdsp_create_midi(card, hdsp, 1)) < 0) {
+ snd_printk("Hammerfall-DSP: Error creating second midi interface\n");
+ return err;
+ }
+ }
+
+ if ((err = snd_hdsp_create_controls(card, hdsp)) < 0) {
+ snd_printk("Hammerfall-DSP: Error creating ctl interface\n");
+ return err;
+ }
+
+ snd_hdsp_proc_init(hdsp);
+
+ hdsp->system_sample_rate = -1;
+ hdsp->playback_pid = -1;
+ hdsp->capture_pid = -1;
+ hdsp->capture_substream = NULL;
+ hdsp->playback_substream = NULL;
+
+ if ((err = snd_hdsp_set_defaults(hdsp)) < 0) {
+ snd_printk("Hammerfall-DSP: Error setting default values\n");
+ return err;
+ }
+
+ if (!(hdsp->state & HDSP_InitializationComplete)) {
+ sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
+ hdsp->port, hdsp->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_printk("Hammerfall-DSP: error registering card\n");
+ return err;
+ }
+ hdsp->state |= HDSP_InitializationComplete;
+ }
+
+ return 0;
+}
+
+#ifdef HDSP_FW_LOADER
+/* load firmware via hotplug fw loader */
+static int __devinit hdsp_request_fw_loader(hdsp_t *hdsp)
+{
+ const char *fwfile;
+ const struct firmware *fw;
+ int err;
+
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
+ return 0;
+ if (hdsp->io_type == Undefined) {
+ if ((err = hdsp_get_iobox_version(hdsp)) < 0)
+ return err;
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
+ return 0;
+ }
+
+ /* caution: max length of firmware filename is 30! */
+ switch (hdsp->io_type) {
+ case Multiface:
+ if (hdsp->firmware_rev == 0xa)
+ fwfile = "multiface_firmware.bin";
+ else
+ fwfile = "multiface_firmware_rev11.bin";
+ break;
+ case Digiface:
+ if (hdsp->firmware_rev == 0xa)
+ fwfile = "digiface_firmware.bin";
+ else
+ fwfile = "digiface_firmware_rev11.bin";
+ break;
+ default:
+ snd_printk(KERN_ERR "Hammerfall-DSP: invalid io_type %d\n", hdsp->io_type);
+ return -EINVAL;
+ }
+
+ if (request_firmware(&fw, fwfile, &hdsp->pci->dev)) {
+ snd_printk(KERN_ERR "Hammerfall-DSP: cannot load firmware %s\n", fwfile);
+ return -ENOENT;
+ }
+ if (fw->size < sizeof(hdsp->firmware_cache)) {
+ snd_printk(KERN_ERR "Hammerfall-DSP: too short firmware size %d (expected %d)\n",
+ (int)fw->size, (int)sizeof(hdsp->firmware_cache));
+ release_firmware(fw);
+ return -EINVAL;
+ }
+#ifdef SNDRV_BIG_ENDIAN
+ {
+ int i;
+ u32 *src = (u32*)fw->data;
+ for (i = 0; i < ARRAY_SIZE(hdsp->firmware_cache); i++, src++)
+ hdsp->firmware_cache[i] = ((*src & 0x000000ff) << 16) |
+ ((*src & 0x0000ff00) << 8) |
+ ((*src & 0x00ff0000) >> 8) |
+ ((*src & 0xff000000) >> 16);
+ }
+#else
+ memcpy(hdsp->firmware_cache, fw->data, sizeof(hdsp->firmware_cache));
+#endif
+ release_firmware(fw);
+
+ hdsp->state |= HDSP_FirmwareCached;
+
+ if ((err = snd_hdsp_load_firmware_from_cache(hdsp)) < 0)
+ return err;
+
+ if (!(hdsp->state & HDSP_InitializationComplete)) {
+ if ((err = snd_hdsp_enable_io(hdsp)) < 0) {
+ return err;
+ }
+
+ if ((err = snd_hdsp_create_hwdep(hdsp->card, hdsp)) < 0) {
+ snd_printk("Hammerfall-DSP: error creating hwdep device\n");
+ return err;
+ }
+ snd_hdsp_initialize_channels(hdsp);
+ snd_hdsp_initialize_midi_flush(hdsp);
+ if ((err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp)) < 0) {
+ snd_printk("Hammerfall-DSP: error creating alsa devices\n");
+ return err;
+ }
+ }
+ return 0;
+}
+#endif
+
+static int __devinit snd_hdsp_create(snd_card_t *card,
+ hdsp_t *hdsp)
+{
+ struct pci_dev *pci = hdsp->pci;
+ int err;
+ int is_9652 = 0;
+ int is_9632 = 0;
+
+ hdsp->irq = -1;
+ hdsp->state = 0;
+ hdsp->midi[0].rmidi = NULL;
+ hdsp->midi[1].rmidi = NULL;
+ hdsp->midi[0].input = NULL;
+ hdsp->midi[1].input = NULL;
+ hdsp->midi[0].output = NULL;
+ hdsp->midi[1].output = NULL;
+ hdsp->midi[0].pending = 0;
+ hdsp->midi[1].pending = 0;
+ spin_lock_init(&hdsp->midi[0].lock);
+ spin_lock_init(&hdsp->midi[1].lock);
+ hdsp->iobase = NULL;
+ hdsp->control_register = 0;
+ hdsp->control2_register = 0;
+ hdsp->io_type = Undefined;
+ hdsp->max_channels = 26;
+
+ hdsp->card = card;
+
+ spin_lock_init(&hdsp->lock);
+
+ tasklet_init(&hdsp->midi_tasklet, hdsp_midi_tasklet, (unsigned long)hdsp);
+
+ pci_read_config_word(hdsp->pci, PCI_CLASS_REVISION, &hdsp->firmware_rev);
+ hdsp->firmware_rev &= 0xff;
+
+ /* From Martin Bjoernsen :
+ "It is important that the card's latency timer register in
+ the PCI configuration space is set to a value much larger
+ than 0 by the computer's BIOS or the driver.
+ The windows driver always sets this 8 bit register [...]
+ to its maximum 255 to avoid problems with some computers."
+ */
+ pci_write_config_byte(hdsp->pci, PCI_LATENCY_TIMER, 0xFF);
+
+ strcpy(card->driver, "H-DSP");
+ strcpy(card->mixername, "Xilinx FPGA");
+
+ if (hdsp->firmware_rev < 0xa) {
+ return -ENODEV;
+ } else if (hdsp->firmware_rev < 0x64) {
+ hdsp->card_name = "RME Hammerfall DSP";
+ } else if (hdsp->firmware_rev < 0x96) {
+ hdsp->card_name = "RME HDSP 9652";
+ is_9652 = 1;
+ } else {
+ hdsp->card_name = "RME HDSP 9632";
+ hdsp->max_channels = 16;
+ is_9632 = 1;
+ }
+
+ if ((err = pci_enable_device(pci)) < 0) {
+ return err;
+ }
+
+ pci_set_master(hdsp->pci);
+
+ if ((err = pci_request_regions(pci, "hdsp")) < 0)
+ return err;
+ hdsp->port = pci_resource_start(pci, 0);
+ if ((hdsp->iobase = ioremap_nocache(hdsp->port, HDSP_IO_EXTENT)) == NULL) {
+ snd_printk("Hammerfall-DSP: unable to remap region 0x%lx-0x%lx\n", hdsp->port, hdsp->port + HDSP_IO_EXTENT - 1);
+ return -EBUSY;
+ }
+
+ if (request_irq(pci->irq, snd_hdsp_interrupt, SA_INTERRUPT|SA_SHIRQ, "hdsp", (void *)hdsp)) {
+ snd_printk("Hammerfall-DSP: unable to use IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+
+ hdsp->irq = pci->irq;
+ hdsp->precise_ptr = 1;
+ hdsp->use_midi_tasklet = 1;
+
+ if ((err = snd_hdsp_initialize_memory(hdsp)) < 0) {
+ return err;
+ }
+
+ if (!is_9652 && !is_9632) {
+ /* we wait 2 seconds to let freshly inserted cardbus cards do their hardware init */
+ if ((1000 / HZ) < 2000) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((2000 * HZ + 999) / 1000);
+ } else {
+ mdelay(2000);
+ }
+
+ if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
+#ifdef HDSP_FW_LOADER
+ if ((err = hdsp_request_fw_loader(hdsp)) < 0) {
+ /* we don't fail as this can happen
+ if userspace is not ready for
+ firmware upload
+ */
+ snd_printk("Hammerfall-DSP: couldn't get firmware from userspace. try using hdsploader\n");
+ } else {
+ /* init is complete, we return */
+ return 0;
+ }
+#endif
+ /* no iobox connected, we defer initialization */
+ snd_printk("Hammerfall-DSP: card initialization pending : waiting for firmware\n");
+ if ((err = snd_hdsp_create_hwdep(card, hdsp)) < 0) {
+ return err;
+ }
+ return 0;
+ } else {
+ snd_printk("Hammerfall-DSP: Firmware already present, initializing card.\n");
+ if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1) {
+ hdsp->io_type = Multiface;
+ } else {
+ hdsp->io_type = Digiface;
+ }
+ }
+ }
+
+ if ((err = snd_hdsp_enable_io(hdsp)) != 0) {
+ return err;
+ }
+
+ if (is_9652) {
+ hdsp->io_type = H9652;
+ }
+
+ if (is_9632) {
+ hdsp->io_type = H9632;
+ }
+
+ if ((err = snd_hdsp_create_hwdep(card, hdsp)) < 0) {
+ return err;
+ }
+
+ snd_hdsp_initialize_channels(hdsp);
+ snd_hdsp_initialize_midi_flush(hdsp);
+
+ hdsp->state |= HDSP_FirmwareLoaded;
+
+ if ((err = snd_hdsp_create_alsa_devices(card, hdsp)) < 0) {
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_hdsp_free(hdsp_t *hdsp)
+{
+ if (hdsp->port) {
+ /* stop the audio, and cancel all interrupts */
+ tasklet_kill(&hdsp->midi_tasklet);
+ hdsp->control_register &= ~(HDSP_Start|HDSP_AudioInterruptEnable|HDSP_Midi0InterruptEnable|HDSP_Midi1InterruptEnable);
+ hdsp_write (hdsp, HDSP_controlRegister, hdsp->control_register);
+ }
+
+ if (hdsp->irq >= 0)
+ free_irq(hdsp->irq, (void *)hdsp);
+
+ snd_hdsp_free_buffers(hdsp);
+
+ if (hdsp->iobase)
+ iounmap(hdsp->iobase);
+
+ if (hdsp->port)
+ pci_release_regions(hdsp->pci);
+
+ pci_disable_device(hdsp->pci);
+ return 0;
+}
+
+static void snd_hdsp_card_free(snd_card_t *card)
+{
+ hdsp_t *hdsp = (hdsp_t *) card->private_data;
+
+ if (hdsp)
+ snd_hdsp_free(hdsp);
+}
+
+static int __devinit snd_hdsp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ hdsp_t *hdsp;
+ snd_card_t *card;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ if (!(card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(hdsp_t))))
+ return -ENOMEM;
+
+ hdsp = (hdsp_t *) card->private_data;
+ card->private_free = snd_hdsp_card_free;
+ hdsp->dev = dev;
+ hdsp->pci = pci;
+ snd_card_set_dev(card, &pci->dev);
+
+ if ((err = snd_hdsp_create(card, hdsp)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->shortname, "Hammerfall DSP");
+ sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
+ hdsp->port, hdsp->irq);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_hdsp_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "RME Hammerfall DSP",
+ .id_table = snd_hdsp_ids,
+ .probe = snd_hdsp_probe,
+ .remove = __devexit_p(snd_hdsp_remove),
+};
+
+static int __init alsa_card_hdsp_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_hdsp_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_hdsp_init)
+module_exit(alsa_card_hdsp_exit)
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
new file mode 100644
index 00000000000..69cd81eaa11
--- /dev/null
+++ b/sound/pci/rme9652/rme9652.c
@@ -0,0 +1,2676 @@
+/*
+ * ALSA driver for RME Digi9652 audio interfaces
+ *
+ * Copyright (c) 1999 IEM - Winfried Ritsch
+ * Copyright (c) 1999-2001 Paul Davis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+
+#include <asm/current.h>
+#include <asm/io.h>
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for RME Digi9652 (Hammerfall) soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for RME Digi9652 (Hammerfall) soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable/disable specific RME96{52,36} soundcards.");
+module_param_array(precise_ptr, bool, NULL, 0444);
+MODULE_PARM_DESC(precise_ptr, "Enable precise pointer (doesn't work reliably).");
+MODULE_AUTHOR("Paul Davis <pbd@op.net>, Winfried Ritsch");
+MODULE_DESCRIPTION("RME Digi9652/Digi9636");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall},"
+ "{RME,Hammerfall-Light}}");
+
+/* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for
+ capture, one for playback. Both the ADAT and S/PDIF channels appear
+ to the host CPU in the same block of memory. There is no functional
+ difference between them in terms of access.
+
+ The Hammerfall Light is identical to the Hammerfall, except that it
+ has 2 sets 18 channels (16 ADAT + 2 S/PDIF) for capture and playback.
+*/
+
+#define RME9652_NCHANNELS 26
+#define RME9636_NCHANNELS 18
+
+/* Preferred sync source choices - used by "sync_pref" control switch */
+
+#define RME9652_SYNC_FROM_SPDIF 0
+#define RME9652_SYNC_FROM_ADAT1 1
+#define RME9652_SYNC_FROM_ADAT2 2
+#define RME9652_SYNC_FROM_ADAT3 3
+
+/* Possible sources of S/PDIF input */
+
+#define RME9652_SPDIFIN_OPTICAL 0 /* optical (ADAT1) */
+#define RME9652_SPDIFIN_COAXIAL 1 /* coaxial (RCA) */
+#define RME9652_SPDIFIN_INTERN 2 /* internal (CDROM) */
+
+/* ------------- Status-Register bits --------------------- */
+
+#define RME9652_IRQ (1<<0) /* IRQ is High if not reset by irq_clear */
+#define RME9652_lock_2 (1<<1) /* ADAT 3-PLL: 1=locked, 0=unlocked */
+#define RME9652_lock_1 (1<<2) /* ADAT 2-PLL: 1=locked, 0=unlocked */
+#define RME9652_lock_0 (1<<3) /* ADAT 1-PLL: 1=locked, 0=unlocked */
+#define RME9652_fs48 (1<<4) /* sample rate is 0=44.1/88.2,1=48/96 Khz */
+#define RME9652_wsel_rd (1<<5) /* if Word-Clock is used and valid then 1 */
+ /* bits 6-15 encode h/w buffer pointer position */
+#define RME9652_sync_2 (1<<16) /* if ADAT-IN 3 in sync to system clock */
+#define RME9652_sync_1 (1<<17) /* if ADAT-IN 2 in sync to system clock */
+#define RME9652_sync_0 (1<<18) /* if ADAT-IN 1 in sync to system clock */
+#define RME9652_DS_rd (1<<19) /* 1=Double Speed Mode, 0=Normal Speed */
+#define RME9652_tc_busy (1<<20) /* 1=time-code copy in progress (960ms) */
+#define RME9652_tc_out (1<<21) /* time-code out bit */
+#define RME9652_F_0 (1<<22) /* 000=64kHz, 100=88.2kHz, 011=96kHz */
+#define RME9652_F_1 (1<<23) /* 111=32kHz, 110=44.1kHz, 101=48kHz, */
+#define RME9652_F_2 (1<<24) /* external Crystal Chip if ERF=1 */
+#define RME9652_ERF (1<<25) /* Error-Flag of SDPIF Receiver (1=No Lock) */
+#define RME9652_buffer_id (1<<26) /* toggles by each interrupt on rec/play */
+#define RME9652_tc_valid (1<<27) /* 1 = a signal is detected on time-code input */
+#define RME9652_SPDIF_READ (1<<28) /* byte available from Rev 1.5+ S/PDIF interface */
+
+#define RME9652_sync (RME9652_sync_0|RME9652_sync_1|RME9652_sync_2)
+#define RME9652_lock (RME9652_lock_0|RME9652_lock_1|RME9652_lock_2)
+#define RME9652_F (RME9652_F_0|RME9652_F_1|RME9652_F_2)
+#define rme9652_decode_spdif_rate(x) ((x)>>22)
+
+/* Bit 6..15 : h/w buffer pointer */
+
+#define RME9652_buf_pos 0x000FFC0
+
+/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later
+ Rev G EEPROMS and Rev 1.5 cards or later.
+*/
+
+#define RME9652_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME9652_buf_pos))
+
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL 0x3fc4
+#endif
+
+/* amount of io space we remap for register access. i'm not sure we
+ even need this much, but 1K is nice round number :)
+*/
+
+#define RME9652_IO_EXTENT 1024
+
+#define RME9652_init_buffer 0
+#define RME9652_play_buffer 32 /* holds ptr to 26x64kBit host RAM */
+#define RME9652_rec_buffer 36 /* holds ptr to 26x64kBit host RAM */
+#define RME9652_control_register 64
+#define RME9652_irq_clear 96
+#define RME9652_time_code 100 /* useful if used with alesis adat */
+#define RME9652_thru_base 128 /* 132...228 Thru for 26 channels */
+
+/* Read-only registers */
+
+/* Writing to any of the register locations writes to the status
+ register. We'll use the first location as our point of access.
+*/
+
+#define RME9652_status_register 0
+
+/* --------- Control-Register Bits ---------------- */
+
+
+#define RME9652_start_bit (1<<0) /* start record/play */
+ /* bits 1-3 encode buffersize/latency */
+#define RME9652_Master (1<<4) /* Clock Mode Master=1,Slave/Auto=0 */
+#define RME9652_IE (1<<5) /* Interupt Enable */
+#define RME9652_freq (1<<6) /* samplerate 0=44.1/88.2, 1=48/96 kHz */
+#define RME9652_freq1 (1<<7) /* if 0, 32kHz, else always 1 */
+#define RME9652_DS (1<<8) /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */
+#define RME9652_PRO (1<<9) /* S/PDIF out: 0=consumer, 1=professional */
+#define RME9652_EMP (1<<10) /* Emphasis 0=None, 1=ON */
+#define RME9652_Dolby (1<<11) /* Non-audio bit 1=set, 0=unset */
+#define RME9652_opt_out (1<<12) /* Use 1st optical OUT as SPDIF: 1=yes,0=no */
+#define RME9652_wsel (1<<13) /* use Wordclock as sync (overwrites master) */
+#define RME9652_inp_0 (1<<14) /* SPDIF-IN: 00=optical (ADAT1), */
+#define RME9652_inp_1 (1<<15) /* 01=koaxial (Cinch), 10=Internal CDROM */
+#define RME9652_SyncPref_ADAT2 (1<<16)
+#define RME9652_SyncPref_ADAT3 (1<<17)
+#define RME9652_SPDIF_RESET (1<<18) /* Rev 1.5+: h/w S/PDIF receiver */
+#define RME9652_SPDIF_SELECT (1<<19)
+#define RME9652_SPDIF_CLOCK (1<<20)
+#define RME9652_SPDIF_WRITE (1<<21)
+#define RME9652_ADAT1_INTERNAL (1<<22) /* Rev 1.5+: if set, internal CD connector carries ADAT */
+
+/* buffersize = 512Bytes * 2^n, where n is made from Bit2 ... Bit0 */
+
+#define RME9652_latency 0x0e
+#define rme9652_encode_latency(x) (((x)&0x7)<<1)
+#define rme9652_decode_latency(x) (((x)>>1)&0x7)
+#define rme9652_running_double_speed(s) ((s)->control_register & RME9652_DS)
+#define RME9652_inp (RME9652_inp_0|RME9652_inp_1)
+#define rme9652_encode_spdif_in(x) (((x)&0x3)<<14)
+#define rme9652_decode_spdif_in(x) (((x)>>14)&0x3)
+
+#define RME9652_SyncPref_Mask (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3)
+#define RME9652_SyncPref_ADAT1 0
+#define RME9652_SyncPref_SPDIF (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3)
+
+/* the size of a substream (1 mono data stream) */
+
+#define RME9652_CHANNEL_BUFFER_SAMPLES (16*1024)
+#define RME9652_CHANNEL_BUFFER_BYTES (4*RME9652_CHANNEL_BUFFER_SAMPLES)
+
+/* the size of the area we need to allocate for DMA transfers. the
+ size is the same regardless of the number of channels - the
+ 9636 still uses the same memory area.
+
+ Note that we allocate 1 more channel than is apparently needed
+ because the h/w seems to write 1 byte beyond the end of the last
+ page. Sigh.
+*/
+
+#define RME9652_DMA_AREA_BYTES ((RME9652_NCHANNELS+1) * RME9652_CHANNEL_BUFFER_BYTES)
+#define RME9652_DMA_AREA_KILOBYTES (RME9652_DMA_AREA_BYTES/1024)
+
+typedef struct snd_rme9652 {
+ int dev;
+
+ spinlock_t lock;
+ int irq;
+ unsigned long port;
+ void __iomem *iobase;
+
+ int precise_ptr;
+
+ u32 control_register; /* cached value */
+ u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */
+
+ u32 creg_spdif;
+ u32 creg_spdif_stream;
+
+ char *card_name; /* hammerfall or hammerfall light names */
+
+ size_t hw_offsetmask; /* &-with status register to get real hw_offset */
+ size_t prev_hw_offset; /* previous hw offset */
+ size_t max_jitter; /* maximum jitter in frames for
+ hw pointer */
+ size_t period_bytes; /* guess what this is */
+
+ unsigned char ds_channels;
+ unsigned char ss_channels; /* different for hammerfall/hammerfall-light */
+
+ struct snd_dma_buffer playback_dma_buf;
+ struct snd_dma_buffer capture_dma_buf;
+
+ unsigned char *capture_buffer; /* suitably aligned address */
+ unsigned char *playback_buffer; /* suitably aligned address */
+
+ pid_t capture_pid;
+ pid_t playback_pid;
+
+ snd_pcm_substream_t *capture_substream;
+ snd_pcm_substream_t *playback_substream;
+ int running;
+
+ int passthru; /* non-zero if doing pass-thru */
+ int hw_rev; /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */
+
+ int last_spdif_sample_rate; /* so that we can catch externally ... */
+ int last_adat_sample_rate; /* ... induced rate changes */
+
+ char *channel_map;
+
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ struct pci_dev *pci;
+ snd_kcontrol_t *spdif_ctl;
+
+} rme9652_t;
+
+/* These tables map the ALSA channels 1..N to the channels that we
+ need to use in order to find the relevant channel buffer. RME
+ refer to this kind of mapping as between "the ADAT channel and
+ the DMA channel." We index it using the logical audio channel,
+ and the value is the DMA channel (i.e. channel buffer number)
+ where the data for that channel can be read/written from/to.
+*/
+
+static char channel_map_9652_ss[26] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25
+};
+
+static char channel_map_9636_ss[26] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ /* channels 16 and 17 are S/PDIF */
+ 24, 25,
+ /* channels 18-25 don't exist */
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_9652_ds[26] = {
+ /* ADAT channels are remapped */
+ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23,
+ /* channels 12 and 13 are S/PDIF */
+ 24, 25,
+ /* others don't exist */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_9636_ds[26] = {
+ /* ADAT channels are remapped */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ /* channels 8 and 9 are S/PDIF */
+ 24, 25
+ /* others don't exist */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer *dmab, size_t size)
+{
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+ dmab->dev.dev = snd_dma_pci_data(pci);
+ if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ size, dmab) < 0)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
+{
+ if (dmab->area)
+ snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
+}
+
+
+static struct pci_device_id snd_rme9652_ids[] = {
+ {
+ .vendor = 0x10ee,
+ .device = 0x3fc4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ }, /* RME Digi9652 */
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme9652_ids);
+
+static inline void rme9652_write(rme9652_t *rme9652, int reg, int val)
+{
+ writel(val, rme9652->iobase + reg);
+}
+
+static inline unsigned int rme9652_read(rme9652_t *rme9652, int reg)
+{
+ return readl(rme9652->iobase + reg);
+}
+
+static inline int snd_rme9652_use_is_exclusive(rme9652_t *rme9652)
+{
+ unsigned long flags;
+ int ret = 1;
+
+ spin_lock_irqsave(&rme9652->lock, flags);
+ if ((rme9652->playback_pid != rme9652->capture_pid) &&
+ (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0)) {
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&rme9652->lock, flags);
+ return ret;
+}
+
+static inline int rme9652_adat_sample_rate(rme9652_t *rme9652)
+{
+ if (rme9652_running_double_speed(rme9652)) {
+ return (rme9652_read(rme9652, RME9652_status_register) &
+ RME9652_fs48) ? 96000 : 88200;
+ } else {
+ return (rme9652_read(rme9652, RME9652_status_register) &
+ RME9652_fs48) ? 48000 : 44100;
+ }
+}
+
+static inline void rme9652_compute_period_size(rme9652_t *rme9652)
+{
+ unsigned int i;
+
+ i = rme9652->control_register & RME9652_latency;
+ rme9652->period_bytes = 1 << ((rme9652_decode_latency(i) + 8));
+ rme9652->hw_offsetmask =
+ (rme9652->period_bytes * 2 - 1) & RME9652_buf_pos;
+ rme9652->max_jitter = 80;
+}
+
+static snd_pcm_uframes_t rme9652_hw_pointer(rme9652_t *rme9652)
+{
+ int status;
+ unsigned int offset, frag;
+ snd_pcm_uframes_t period_size = rme9652->period_bytes / 4;
+ snd_pcm_sframes_t delta;
+
+ status = rme9652_read(rme9652, RME9652_status_register);
+ if (!rme9652->precise_ptr)
+ return (status & RME9652_buffer_id) ? period_size : 0;
+ offset = status & RME9652_buf_pos;
+
+ /* The hardware may give a backward movement for up to 80 frames
+ Martin Kirst <martin.kirst@freenet.de> knows the details.
+ */
+
+ delta = rme9652->prev_hw_offset - offset;
+ delta &= 0xffff;
+ if (delta <= (snd_pcm_sframes_t)rme9652->max_jitter * 4)
+ offset = rme9652->prev_hw_offset;
+ else
+ rme9652->prev_hw_offset = offset;
+ offset &= rme9652->hw_offsetmask;
+ offset /= 4;
+ frag = status & RME9652_buffer_id;
+
+ if (offset < period_size) {
+ if (offset > rme9652->max_jitter) {
+ if (frag)
+ printk(KERN_ERR "Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset);
+ } else if (!frag)
+ return 0;
+ offset -= rme9652->max_jitter;
+ if (offset < 0)
+ offset += period_size * 2;
+ } else {
+ if (offset > period_size + rme9652->max_jitter) {
+ if (!frag)
+ printk(KERN_ERR "Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset);
+ } else if (frag)
+ return period_size;
+ offset -= rme9652->max_jitter;
+ }
+
+ return offset;
+}
+
+static inline void rme9652_reset_hw_pointer(rme9652_t *rme9652)
+{
+ int i;
+
+ /* reset the FIFO pointer to zero. We do this by writing to 8
+ registers, each of which is a 32bit wide register, and set
+ them all to zero. Note that s->iobase is a pointer to
+ int32, not pointer to char.
+ */
+
+ for (i = 0; i < 8; i++) {
+ rme9652_write(rme9652, i * 4, 0);
+ udelay(10);
+ }
+ rme9652->prev_hw_offset = 0;
+}
+
+static inline void rme9652_start(rme9652_t *s)
+{
+ s->control_register |= (RME9652_IE | RME9652_start_bit);
+ rme9652_write(s, RME9652_control_register, s->control_register);
+}
+
+static inline void rme9652_stop(rme9652_t *s)
+{
+ s->control_register &= ~(RME9652_start_bit | RME9652_IE);
+ rme9652_write(s, RME9652_control_register, s->control_register);
+}
+
+static int rme9652_set_interrupt_interval(rme9652_t *s,
+ unsigned int frames)
+{
+ int restart = 0;
+ int n;
+
+ spin_lock_irq(&s->lock);
+
+ if ((restart = s->running)) {
+ rme9652_stop(s);
+ }
+
+ frames >>= 7;
+ n = 0;
+ while (frames) {
+ n++;
+ frames >>= 1;
+ }
+
+ s->control_register &= ~RME9652_latency;
+ s->control_register |= rme9652_encode_latency(n);
+
+ rme9652_write(s, RME9652_control_register, s->control_register);
+
+ rme9652_compute_period_size(s);
+
+ if (restart)
+ rme9652_start(s);
+
+ spin_unlock_irq(&s->lock);
+
+ return 0;
+}
+
+static int rme9652_set_rate(rme9652_t *rme9652, int rate)
+{
+ int restart;
+ int reject_if_open = 0;
+ int xrate;
+
+ if (!snd_rme9652_use_is_exclusive (rme9652)) {
+ return -EBUSY;
+ }
+
+ /* Changing from a "single speed" to a "double speed" rate is
+ not allowed if any substreams are open. This is because
+ such a change causes a shift in the location of
+ the DMA buffers and a reduction in the number of available
+ buffers.
+
+ Note that a similar but essentially insoluble problem
+ exists for externally-driven rate changes. All we can do
+ is to flag rate changes in the read/write routines.
+ */
+
+ spin_lock_irq(&rme9652->lock);
+ xrate = rme9652_adat_sample_rate(rme9652);
+
+ switch (rate) {
+ case 44100:
+ if (xrate > 48000) {
+ reject_if_open = 1;
+ }
+ rate = 0;
+ break;
+ case 48000:
+ if (xrate > 48000) {
+ reject_if_open = 1;
+ }
+ rate = RME9652_freq;
+ break;
+ case 88200:
+ if (xrate < 48000) {
+ reject_if_open = 1;
+ }
+ rate = RME9652_DS;
+ break;
+ case 96000:
+ if (xrate < 48000) {
+ reject_if_open = 1;
+ }
+ rate = RME9652_DS | RME9652_freq;
+ break;
+ default:
+ spin_unlock_irq(&rme9652->lock);
+ return -EINVAL;
+ }
+
+ if (reject_if_open && (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0)) {
+ spin_unlock_irq(&rme9652->lock);
+ return -EBUSY;
+ }
+
+ if ((restart = rme9652->running)) {
+ rme9652_stop(rme9652);
+ }
+ rme9652->control_register &= ~(RME9652_freq | RME9652_DS);
+ rme9652->control_register |= rate;
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+ if (restart) {
+ rme9652_start(rme9652);
+ }
+
+ if (rate & RME9652_DS) {
+ if (rme9652->ss_channels == RME9652_NCHANNELS) {
+ rme9652->channel_map = channel_map_9652_ds;
+ } else {
+ rme9652->channel_map = channel_map_9636_ds;
+ }
+ } else {
+ if (rme9652->ss_channels == RME9652_NCHANNELS) {
+ rme9652->channel_map = channel_map_9652_ss;
+ } else {
+ rme9652->channel_map = channel_map_9636_ss;
+ }
+ }
+
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static void rme9652_set_thru(rme9652_t *rme9652, int channel, int enable)
+{
+ int i;
+
+ rme9652->passthru = 0;
+
+ if (channel < 0) {
+
+ /* set thru for all channels */
+
+ if (enable) {
+ for (i = 0; i < RME9652_NCHANNELS; i++) {
+ rme9652->thru_bits |= (1 << i);
+ rme9652_write(rme9652, RME9652_thru_base + i * 4, 1);
+ }
+ } else {
+ for (i = 0; i < RME9652_NCHANNELS; i++) {
+ rme9652->thru_bits &= ~(1 << i);
+ rme9652_write(rme9652, RME9652_thru_base + i * 4, 0);
+ }
+ }
+
+ } else {
+ int mapped_channel;
+
+ snd_assert(channel == RME9652_NCHANNELS, return);
+
+ mapped_channel = rme9652->channel_map[channel];
+
+ if (enable) {
+ rme9652->thru_bits |= (1 << mapped_channel);
+ } else {
+ rme9652->thru_bits &= ~(1 << mapped_channel);
+ }
+
+ rme9652_write(rme9652,
+ RME9652_thru_base + mapped_channel * 4,
+ enable ? 1 : 0);
+ }
+}
+
+static int rme9652_set_passthru(rme9652_t *rme9652, int onoff)
+{
+ if (onoff) {
+ rme9652_set_thru(rme9652, -1, 1);
+
+ /* we don't want interrupts, so do a
+ custom version of rme9652_start().
+ */
+
+ rme9652->control_register =
+ RME9652_inp_0 |
+ rme9652_encode_latency(7) |
+ RME9652_start_bit;
+
+ rme9652_reset_hw_pointer(rme9652);
+
+ rme9652_write(rme9652, RME9652_control_register,
+ rme9652->control_register);
+ rme9652->passthru = 1;
+ } else {
+ rme9652_set_thru(rme9652, -1, 0);
+ rme9652_stop(rme9652);
+ rme9652->passthru = 0;
+ }
+
+ return 0;
+}
+
+static void rme9652_spdif_set_bit (rme9652_t *rme9652, int mask, int onoff)
+{
+ if (onoff)
+ rme9652->control_register |= mask;
+ else
+ rme9652->control_register &= ~mask;
+
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+}
+
+static void rme9652_spdif_write_byte (rme9652_t *rme9652, const int val)
+{
+ long mask;
+ long i;
+
+ for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) {
+ if (val & mask)
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 1);
+ else
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 0);
+
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1);
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0);
+ }
+}
+
+static int rme9652_spdif_read_byte (rme9652_t *rme9652)
+{
+ long mask;
+ long val;
+ long i;
+
+ val = 0;
+
+ for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) {
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1);
+ if (rme9652_read (rme9652, RME9652_status_register) & RME9652_SPDIF_READ)
+ val |= mask;
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0);
+ }
+
+ return val;
+}
+
+static void rme9652_write_spdif_codec (rme9652_t *rme9652, const int address, const int data)
+{
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+ rme9652_spdif_write_byte (rme9652, 0x20);
+ rme9652_spdif_write_byte (rme9652, address);
+ rme9652_spdif_write_byte (rme9652, data);
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+}
+
+
+static int rme9652_spdif_read_codec (rme9652_t *rme9652, const int address)
+{
+ int ret;
+
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+ rme9652_spdif_write_byte (rme9652, 0x20);
+ rme9652_spdif_write_byte (rme9652, address);
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+
+ rme9652_spdif_write_byte (rme9652, 0x21);
+ ret = rme9652_spdif_read_byte (rme9652);
+ rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+
+ return ret;
+}
+
+static void rme9652_initialize_spdif_receiver (rme9652_t *rme9652)
+{
+ /* XXX what unsets this ? */
+
+ rme9652->control_register |= RME9652_SPDIF_RESET;
+
+ rme9652_write_spdif_codec (rme9652, 4, 0x40);
+ rme9652_write_spdif_codec (rme9652, 17, 0x13);
+ rme9652_write_spdif_codec (rme9652, 6, 0x02);
+}
+
+static inline int rme9652_spdif_sample_rate(rme9652_t *s)
+{
+ unsigned int rate_bits;
+
+ if (rme9652_read(s, RME9652_status_register) & RME9652_ERF) {
+ return -1; /* error condition */
+ }
+
+ if (s->hw_rev == 15) {
+
+ int x, y, ret;
+
+ x = rme9652_spdif_read_codec (s, 30);
+
+ if (x != 0)
+ y = 48000 * 64 / x;
+ else
+ y = 0;
+
+ if (y > 30400 && y < 33600) ret = 32000;
+ else if (y > 41900 && y < 46000) ret = 44100;
+ else if (y > 46000 && y < 50400) ret = 48000;
+ else if (y > 60800 && y < 67200) ret = 64000;
+ else if (y > 83700 && y < 92000) ret = 88200;
+ else if (y > 92000 && y < 100000) ret = 96000;
+ else ret = 0;
+ return ret;
+ }
+
+ rate_bits = rme9652_read(s, RME9652_status_register) & RME9652_F;
+
+ switch (rme9652_decode_spdif_rate(rate_bits)) {
+ case 0x7:
+ return 32000;
+ break;
+
+ case 0x6:
+ return 44100;
+ break;
+
+ case 0x5:
+ return 48000;
+ break;
+
+ case 0x4:
+ return 88200;
+ break;
+
+ case 0x3:
+ return 96000;
+ break;
+
+ case 0x0:
+ return 64000;
+ break;
+
+ default:
+ snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n",
+ s->card_name, rate_bits);
+ return 0;
+ break;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ Control Interface
+ ----------------------------------------------------------------------------*/
+
+static u32 snd_rme9652_convert_from_aes(snd_aes_iec958_t *aes)
+{
+ u32 val = 0;
+ val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME9652_PRO : 0;
+ val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME9652_Dolby : 0;
+ if (val & RME9652_PRO)
+ val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME9652_EMP : 0;
+ else
+ val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME9652_EMP : 0;
+ return val;
+}
+
+static void snd_rme9652_convert_to_aes(snd_aes_iec958_t *aes, u32 val)
+{
+ aes->status[0] = ((val & RME9652_PRO) ? IEC958_AES0_PROFESSIONAL : 0) |
+ ((val & RME9652_Dolby) ? IEC958_AES0_NONAUDIO : 0);
+ if (val & RME9652_PRO)
+ aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+ else
+ aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme9652_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme9652_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif);
+ return 0;
+}
+
+static int snd_rme9652_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&rme9652->lock);
+ change = val != rme9652->creg_spdif;
+ rme9652->creg_spdif = val;
+ spin_unlock_irq(&rme9652->lock);
+ return change;
+}
+
+static int snd_rme9652_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme9652_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif_stream);
+ return 0;
+}
+
+static int snd_rme9652_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 val;
+
+ val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
+ spin_lock_irq(&rme9652->lock);
+ change = val != rme9652->creg_spdif_stream;
+ rme9652->creg_spdif_stream = val;
+ rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val);
+ spin_unlock_irq(&rme9652->lock);
+ return change;
+}
+
+static int snd_rme9652_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_rme9652_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = kcontrol->private_value;
+ return 0;
+}
+
+#define RME9652_ADAT1_IN(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .info = snd_rme9652_info_adat1_in, \
+ .get = snd_rme9652_get_adat1_in, \
+ .put = snd_rme9652_put_adat1_in }
+
+static unsigned int rme9652_adat1_in(rme9652_t *rme9652)
+{
+ if (rme9652->control_register & RME9652_ADAT1_INTERNAL)
+ return 1;
+ return 0;
+}
+
+static int rme9652_set_adat1_input(rme9652_t *rme9652, int internal)
+{
+ int restart = 0;
+
+ if (internal) {
+ rme9652->control_register |= RME9652_ADAT1_INTERNAL;
+ } else {
+ rme9652->control_register &= ~RME9652_ADAT1_INTERNAL;
+ }
+
+ /* XXX do we actually need to stop the card when we do this ? */
+
+ if ((restart = rme9652->running)) {
+ rme9652_stop(rme9652);
+ }
+
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+ if (restart) {
+ rme9652_start(rme9652);
+ }
+
+ return 0;
+}
+
+static int snd_rme9652_info_adat1_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[2] = {"ADAT1", "Internal"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_rme9652_get_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme9652->lock);
+ ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652);
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_rme9652_use_is_exclusive(rme9652))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0] % 2;
+ spin_lock_irq(&rme9652->lock);
+ change = val != rme9652_adat1_in(rme9652);
+ if (change)
+ rme9652_set_adat1_input(rme9652, val);
+ spin_unlock_irq(&rme9652->lock);
+ return change;
+}
+
+#define RME9652_SPDIF_IN(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .info = snd_rme9652_info_spdif_in, \
+ .get = snd_rme9652_get_spdif_in, .put = snd_rme9652_put_spdif_in }
+
+static unsigned int rme9652_spdif_in(rme9652_t *rme9652)
+{
+ return rme9652_decode_spdif_in(rme9652->control_register &
+ RME9652_inp);
+}
+
+static int rme9652_set_spdif_input(rme9652_t *rme9652, int in)
+{
+ int restart = 0;
+
+ rme9652->control_register &= ~RME9652_inp;
+ rme9652->control_register |= rme9652_encode_spdif_in(in);
+
+ if ((restart = rme9652->running)) {
+ rme9652_stop(rme9652);
+ }
+
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+ if (restart) {
+ rme9652_start(rme9652);
+ }
+
+ return 0;
+}
+
+static int snd_rme9652_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[3] = {"ADAT1", "Coaxial", "Internal"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_rme9652_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme9652->lock);
+ ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652);
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_rme9652_use_is_exclusive(rme9652))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0] % 3;
+ spin_lock_irq(&rme9652->lock);
+ change = val != rme9652_spdif_in(rme9652);
+ if (change)
+ rme9652_set_spdif_input(rme9652, val);
+ spin_unlock_irq(&rme9652->lock);
+ return change;
+}
+
+#define RME9652_SPDIF_OUT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .info = snd_rme9652_info_spdif_out, \
+ .get = snd_rme9652_get_spdif_out, .put = snd_rme9652_put_spdif_out }
+
+static int rme9652_spdif_out(rme9652_t *rme9652)
+{
+ return (rme9652->control_register & RME9652_opt_out) ? 1 : 0;
+}
+
+static int rme9652_set_spdif_output(rme9652_t *rme9652, int out)
+{
+ int restart = 0;
+
+ if (out) {
+ rme9652->control_register |= RME9652_opt_out;
+ } else {
+ rme9652->control_register &= ~RME9652_opt_out;
+ }
+
+ if ((restart = rme9652->running)) {
+ rme9652_stop(rme9652);
+ }
+
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+ if (restart) {
+ rme9652_start(rme9652);
+ }
+
+ return 0;
+}
+
+static int snd_rme9652_info_spdif_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_rme9652_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme9652->lock);
+ ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652);
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_rme9652_use_is_exclusive(rme9652))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&rme9652->lock);
+ change = (int)val != rme9652_spdif_out(rme9652);
+ rme9652_set_spdif_output(rme9652, val);
+ spin_unlock_irq(&rme9652->lock);
+ return change;
+}
+
+#define RME9652_SYNC_MODE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .info = snd_rme9652_info_sync_mode, \
+ .get = snd_rme9652_get_sync_mode, .put = snd_rme9652_put_sync_mode }
+
+static int rme9652_sync_mode(rme9652_t *rme9652)
+{
+ if (rme9652->control_register & RME9652_wsel) {
+ return 2;
+ } else if (rme9652->control_register & RME9652_Master) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int rme9652_set_sync_mode(rme9652_t *rme9652, int mode)
+{
+ int restart = 0;
+
+ switch (mode) {
+ case 0:
+ rme9652->control_register &=
+ ~(RME9652_Master | RME9652_wsel);
+ break;
+ case 1:
+ rme9652->control_register =
+ (rme9652->control_register & ~RME9652_wsel) | RME9652_Master;
+ break;
+ case 2:
+ rme9652->control_register |=
+ (RME9652_Master | RME9652_wsel);
+ break;
+ }
+
+ if ((restart = rme9652->running)) {
+ rme9652_stop(rme9652);
+ }
+
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+ if (restart) {
+ rme9652_start(rme9652);
+ }
+
+ return 0;
+}
+
+static int snd_rme9652_info_sync_mode(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[3] = {"AutoSync", "Master", "Word Clock"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_rme9652_get_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme9652->lock);
+ ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652);
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ val = ucontrol->value.enumerated.item[0] % 3;
+ spin_lock_irq(&rme9652->lock);
+ change = (int)val != rme9652_sync_mode(rme9652);
+ rme9652_set_sync_mode(rme9652, val);
+ spin_unlock_irq(&rme9652->lock);
+ return change;
+}
+
+#define RME9652_SYNC_PREF(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .info = snd_rme9652_info_sync_pref, \
+ .get = snd_rme9652_get_sync_pref, .put = snd_rme9652_put_sync_pref }
+
+static int rme9652_sync_pref(rme9652_t *rme9652)
+{
+ switch (rme9652->control_register & RME9652_SyncPref_Mask) {
+ case RME9652_SyncPref_ADAT1:
+ return RME9652_SYNC_FROM_ADAT1;
+ case RME9652_SyncPref_ADAT2:
+ return RME9652_SYNC_FROM_ADAT2;
+ case RME9652_SyncPref_ADAT3:
+ return RME9652_SYNC_FROM_ADAT3;
+ case RME9652_SyncPref_SPDIF:
+ return RME9652_SYNC_FROM_SPDIF;
+ }
+ /* Not reachable */
+ return 0;
+}
+
+static int rme9652_set_sync_pref(rme9652_t *rme9652, int pref)
+{
+ int restart;
+
+ rme9652->control_register &= ~RME9652_SyncPref_Mask;
+ switch (pref) {
+ case RME9652_SYNC_FROM_ADAT1:
+ rme9652->control_register |= RME9652_SyncPref_ADAT1;
+ break;
+ case RME9652_SYNC_FROM_ADAT2:
+ rme9652->control_register |= RME9652_SyncPref_ADAT2;
+ break;
+ case RME9652_SYNC_FROM_ADAT3:
+ rme9652->control_register |= RME9652_SyncPref_ADAT3;
+ break;
+ case RME9652_SYNC_FROM_SPDIF:
+ rme9652->control_register |= RME9652_SyncPref_SPDIF;
+ break;
+ }
+
+ if ((restart = rme9652->running)) {
+ rme9652_stop(rme9652);
+ }
+
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+ if (restart) {
+ rme9652_start(rme9652);
+ }
+
+ return 0;
+}
+
+static int snd_rme9652_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"};
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_rme9652_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme9652->lock);
+ ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652);
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static int snd_rme9652_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change, max;
+ unsigned int val;
+
+ if (!snd_rme9652_use_is_exclusive(rme9652))
+ return -EBUSY;
+ max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
+ val = ucontrol->value.enumerated.item[0] % max;
+ spin_lock_irq(&rme9652->lock);
+ change = (int)val != rme9652_sync_pref(rme9652);
+ rme9652_set_sync_pref(rme9652, val);
+ spin_unlock_irq(&rme9652->lock);
+ return change;
+}
+
+static int snd_rme9652_info_thru(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = rme9652->ss_channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_rme9652_get_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ unsigned int k;
+ u32 thru_bits = rme9652->thru_bits;
+
+ for (k = 0; k < rme9652->ss_channels; ++k) {
+ ucontrol->value.integer.value[k] = !!(thru_bits & (1 << k));
+ }
+ return 0;
+}
+
+static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int chn;
+ u32 thru_bits = 0;
+
+ if (!snd_rme9652_use_is_exclusive(rme9652))
+ return -EBUSY;
+
+ for (chn = 0; chn < rme9652->ss_channels; ++chn) {
+ if (ucontrol->value.integer.value[chn])
+ thru_bits |= 1 << chn;
+ }
+
+ spin_lock_irq(&rme9652->lock);
+ change = thru_bits ^ rme9652->thru_bits;
+ if (change) {
+ for (chn = 0; chn < rme9652->ss_channels; ++chn) {
+ if (!(change & (1 << chn)))
+ continue;
+ rme9652_set_thru(rme9652,chn,thru_bits&(1<<chn));
+ }
+ }
+ spin_unlock_irq(&rme9652->lock);
+ return !!change;
+}
+
+#define RME9652_PASSTHRU(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .info = snd_rme9652_info_passthru, \
+ .put = snd_rme9652_put_passthru, \
+ .get = snd_rme9652_get_passthru }
+
+static int snd_rme9652_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_rme9652_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme9652->lock);
+ ucontrol->value.integer.value[0] = rme9652->passthru;
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+ int err = 0;
+
+ if (!snd_rme9652_use_is_exclusive(rme9652))
+ return -EBUSY;
+
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&rme9652->lock);
+ change = (ucontrol->value.integer.value[0] != rme9652->passthru);
+ if (change)
+ err = rme9652_set_passthru(rme9652, val);
+ spin_unlock_irq(&rme9652->lock);
+ return err ? err : change;
+}
+
+/* Read-only switches */
+
+#define RME9652_SPDIF_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_rme9652_info_spdif_rate, \
+ .get = snd_rme9652_get_spdif_rate }
+
+static int snd_rme9652_info_spdif_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 96000;
+ return 0;
+}
+
+static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&rme9652->lock);
+ ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652);
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+#define RME9652_ADAT_SYNC(xname, xindex, xidx) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_rme9652_info_adat_sync, \
+ .get = snd_rme9652_get_adat_sync, .private_value = xidx }
+
+static int snd_rme9652_info_adat_sync(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+ unsigned int mask1, mask2, val;
+
+ switch (kcontrol->private_value) {
+ case 0: mask1 = RME9652_lock_0; mask2 = RME9652_sync_0; break;
+ case 1: mask1 = RME9652_lock_1; mask2 = RME9652_sync_1; break;
+ case 2: mask1 = RME9652_lock_2; mask2 = RME9652_sync_2; break;
+ default: return -EINVAL;
+ }
+ val = rme9652_read(rme9652, RME9652_status_register);
+ ucontrol->value.enumerated.item[0] = (val & mask1) ? 1 : 0;
+ ucontrol->value.enumerated.item[0] |= (val & mask2) ? 2 : 0;
+ return 0;
+}
+
+#define RME9652_TC_VALID(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_rme9652_info_tc_valid, \
+ .get = snd_rme9652_get_tc_valid }
+
+static int snd_rme9652_info_tc_valid(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ (rme9652_read(rme9652, RME9652_status_register) & RME9652_tc_valid) ? 1 : 0;
+ return 0;
+}
+
+#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE
+
+/* FIXME: this routine needs a port to the new control API --jk */
+
+static int snd_rme9652_get_tc_value(void *private_data,
+ snd_kswitch_t *kswitch,
+ snd_switch_t *uswitch)
+{
+ rme9652_t *s = (rme9652_t *) private_data;
+ u32 value;
+ int i;
+
+ uswitch->type = SNDRV_SW_TYPE_DWORD;
+
+ if ((rme9652_read(s, RME9652_status_register) &
+ RME9652_tc_valid) == 0) {
+ uswitch->value.data32[0] = 0;
+ return 0;
+ }
+
+ /* timecode request */
+
+ rme9652_write(s, RME9652_time_code, 0);
+
+ /* XXX bug alert: loop-based timing !!!! */
+
+ for (i = 0; i < 50; i++) {
+ if (!(rme9652_read(s, i * 4) & RME9652_tc_busy))
+ break;
+ }
+
+ if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) {
+ return -EIO;
+ }
+
+ value = 0;
+
+ for (i = 0; i < 32; i++) {
+ value >>= 1;
+
+ if (rme9652_read(s, i * 4) & RME9652_tc_out)
+ value |= 0x80000000;
+ }
+
+ if (value > 2 * 60 * 48000) {
+ value -= 2 * 60 * 48000;
+ } else {
+ value = 0;
+ }
+
+ uswitch->value.data32[0] = value;
+
+ return 0;
+}
+
+#endif /* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */
+
+static snd_kcontrol_new_t snd_rme9652_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_rme9652_control_spdif_info,
+ .get = snd_rme9652_control_spdif_get,
+ .put = snd_rme9652_control_spdif_put,
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_rme9652_control_spdif_stream_info,
+ .get = snd_rme9652_control_spdif_stream_get,
+ .put = snd_rme9652_control_spdif_stream_put,
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_rme9652_control_spdif_mask_info,
+ .get = snd_rme9652_control_spdif_mask_get,
+ .private_value = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_CON_EMPHASIS,
+},
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .info = snd_rme9652_control_spdif_mask_info,
+ .get = snd_rme9652_control_spdif_mask_get,
+ .private_value = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_PRO_EMPHASIS,
+},
+RME9652_SPDIF_IN("IEC958 Input Connector", 0),
+RME9652_SPDIF_OUT("IEC958 Output also on ADAT1", 0),
+RME9652_SYNC_MODE("Sync Mode", 0),
+RME9652_SYNC_PREF("Preferred Sync Source", 0),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Channels Thru",
+ .index = 0,
+ .info = snd_rme9652_info_thru,
+ .get = snd_rme9652_get_thru,
+ .put = snd_rme9652_put_thru,
+},
+RME9652_SPDIF_RATE("IEC958 Sample Rate", 0),
+RME9652_ADAT_SYNC("ADAT1 Sync Check", 0, 0),
+RME9652_ADAT_SYNC("ADAT2 Sync Check", 0, 1),
+RME9652_TC_VALID("Timecode Valid", 0),
+RME9652_PASSTHRU("Passthru", 0)
+};
+
+static snd_kcontrol_new_t snd_rme9652_adat3_check =
+RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2);
+
+static snd_kcontrol_new_t snd_rme9652_adat1_input =
+RME9652_ADAT1_IN("ADAT1 Input Source", 0);
+
+static int snd_rme9652_create_controls(snd_card_t *card, rme9652_t *rme9652)
+{
+ unsigned int idx;
+ int err;
+ snd_kcontrol_t *kctl;
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_rme9652_controls); idx++) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0)
+ return err;
+ if (idx == 1) /* IEC958 (S/PDIF) Stream */
+ rme9652->spdif_ctl = kctl;
+ }
+
+ if (rme9652->ss_channels == RME9652_NCHANNELS)
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652))) < 0)
+ return err;
+
+ if (rme9652->hw_rev >= 15)
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652))) < 0)
+ return err;
+
+ return 0;
+}
+
+/*------------------------------------------------------------
+ /proc interface
+ ------------------------------------------------------------*/
+
+static void
+snd_rme9652_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ rme9652_t *rme9652 = (rme9652_t *) entry->private_data;
+ u32 thru_bits = rme9652->thru_bits;
+ int show_auto_sync_source = 0;
+ int i;
+ unsigned int status;
+ int x;
+
+ status = rme9652_read(rme9652, RME9652_status_register);
+
+ snd_iprintf(buffer, "%s (Card #%d)\n", rme9652->card_name, rme9652->card->number + 1);
+ snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
+ rme9652->capture_buffer, rme9652->playback_buffer);
+ snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+ rme9652->irq, rme9652->port, (unsigned long)rme9652->iobase);
+ snd_iprintf(buffer, "Control register: %x\n", rme9652->control_register);
+
+ snd_iprintf(buffer, "\n");
+
+ x = 1 << (6 + rme9652_decode_latency(rme9652->control_register &
+ RME9652_latency));
+
+ snd_iprintf(buffer, "Latency: %d samples (2 periods of %lu bytes)\n",
+ x, (unsigned long) rme9652->period_bytes);
+ snd_iprintf(buffer, "Hardware pointer (frames): %ld\n",
+ rme9652_hw_pointer(rme9652));
+ snd_iprintf(buffer, "Passthru: %s\n",
+ rme9652->passthru ? "yes" : "no");
+
+ if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) {
+ snd_iprintf(buffer, "Clock mode: autosync\n");
+ show_auto_sync_source = 1;
+ } else if (rme9652->control_register & RME9652_wsel) {
+ if (status & RME9652_wsel_rd) {
+ snd_iprintf(buffer, "Clock mode: word clock\n");
+ } else {
+ snd_iprintf(buffer, "Clock mode: word clock (no signal)\n");
+ }
+ } else {
+ snd_iprintf(buffer, "Clock mode: master\n");
+ }
+
+ if (show_auto_sync_source) {
+ switch (rme9652->control_register & RME9652_SyncPref_Mask) {
+ case RME9652_SyncPref_ADAT1:
+ snd_iprintf(buffer, "Pref. sync source: ADAT1\n");
+ break;
+ case RME9652_SyncPref_ADAT2:
+ snd_iprintf(buffer, "Pref. sync source: ADAT2\n");
+ break;
+ case RME9652_SyncPref_ADAT3:
+ snd_iprintf(buffer, "Pref. sync source: ADAT3\n");
+ break;
+ case RME9652_SyncPref_SPDIF:
+ snd_iprintf(buffer, "Pref. sync source: IEC958\n");
+ break;
+ default:
+ snd_iprintf(buffer, "Pref. sync source: ???\n");
+ }
+ }
+
+ if (rme9652->hw_rev >= 15)
+ snd_iprintf(buffer, "\nADAT1 Input source: %s\n",
+ (rme9652->control_register & RME9652_ADAT1_INTERNAL) ?
+ "Internal" : "ADAT1 optical");
+
+ snd_iprintf(buffer, "\n");
+
+ switch (rme9652_decode_spdif_in(rme9652->control_register &
+ RME9652_inp)) {
+ case RME9652_SPDIFIN_OPTICAL:
+ snd_iprintf(buffer, "IEC958 input: ADAT1\n");
+ break;
+ case RME9652_SPDIFIN_COAXIAL:
+ snd_iprintf(buffer, "IEC958 input: Coaxial\n");
+ break;
+ case RME9652_SPDIFIN_INTERN:
+ snd_iprintf(buffer, "IEC958 input: Internal\n");
+ break;
+ default:
+ snd_iprintf(buffer, "IEC958 input: ???\n");
+ break;
+ }
+
+ if (rme9652->control_register & RME9652_opt_out) {
+ snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+ }
+
+ if (rme9652->control_register & RME9652_PRO) {
+ snd_iprintf(buffer, "IEC958 quality: Professional\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+ }
+
+ if (rme9652->control_register & RME9652_EMP) {
+ snd_iprintf(buffer, "IEC958 emphasis: on\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 emphasis: off\n");
+ }
+
+ if (rme9652->control_register & RME9652_Dolby) {
+ snd_iprintf(buffer, "IEC958 Dolby: on\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 Dolby: off\n");
+ }
+
+ i = rme9652_spdif_sample_rate(rme9652);
+
+ if (i < 0) {
+ snd_iprintf(buffer,
+ "IEC958 sample rate: error flag set\n");
+ } else if (i == 0) {
+ snd_iprintf(buffer, "IEC958 sample rate: undetermined\n");
+ } else {
+ snd_iprintf(buffer, "IEC958 sample rate: %d\n", i);
+ }
+
+ snd_iprintf(buffer, "\n");
+
+ snd_iprintf(buffer, "ADAT Sample rate: %dHz\n",
+ rme9652_adat_sample_rate(rme9652));
+
+ /* Sync Check */
+
+ x = status & RME9652_sync_0;
+ if (status & RME9652_lock_0) {
+ snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf(buffer, "ADAT1: No Lock\n");
+ }
+
+ x = status & RME9652_sync_1;
+ if (status & RME9652_lock_1) {
+ snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf(buffer, "ADAT2: No Lock\n");
+ }
+
+ x = status & RME9652_sync_2;
+ if (status & RME9652_lock_2) {
+ snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock");
+ } else {
+ snd_iprintf(buffer, "ADAT3: No Lock\n");
+ }
+
+ snd_iprintf(buffer, "\n");
+
+ snd_iprintf(buffer, "Timecode signal: %s\n",
+ (status & RME9652_tc_valid) ? "yes" : "no");
+
+ /* thru modes */
+
+ snd_iprintf(buffer, "Punch Status:\n\n");
+
+ for (i = 0; i < rme9652->ss_channels; i++) {
+ if (thru_bits & (1 << i)) {
+ snd_iprintf(buffer, "%2d: on ", i + 1);
+ } else {
+ snd_iprintf(buffer, "%2d: off ", i + 1);
+ }
+
+ if (((i + 1) % 8) == 0) {
+ snd_iprintf(buffer, "\n");
+ }
+ }
+
+ snd_iprintf(buffer, "\n");
+}
+
+static void __devinit snd_rme9652_proc_init(rme9652_t *rme9652)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(rme9652->card, "rme9652", &entry))
+ snd_info_set_text_ops(entry, rme9652, 1024, snd_rme9652_proc_read);
+}
+
+static void snd_rme9652_free_buffers(rme9652_t *rme9652)
+{
+ snd_hammerfall_free_buffer(&rme9652->capture_dma_buf, rme9652->pci);
+ snd_hammerfall_free_buffer(&rme9652->playback_dma_buf, rme9652->pci);
+}
+
+static int snd_rme9652_free(rme9652_t *rme9652)
+{
+ if (rme9652->irq >= 0)
+ rme9652_stop(rme9652);
+ snd_rme9652_free_buffers(rme9652);
+
+ if (rme9652->irq >= 0)
+ free_irq(rme9652->irq, (void *)rme9652);
+ if (rme9652->iobase)
+ iounmap(rme9652->iobase);
+ if (rme9652->port)
+ pci_release_regions(rme9652->pci);
+
+ pci_disable_device(rme9652->pci);
+ return 0;
+}
+
+static int __devinit snd_rme9652_initialize_memory(rme9652_t *rme9652)
+{
+ unsigned long pb_bus, cb_bus;
+
+ if (snd_hammerfall_get_buffer(rme9652->pci, &rme9652->capture_dma_buf, RME9652_DMA_AREA_BYTES) < 0 ||
+ snd_hammerfall_get_buffer(rme9652->pci, &rme9652->playback_dma_buf, RME9652_DMA_AREA_BYTES) < 0) {
+ if (rme9652->capture_dma_buf.area)
+ snd_dma_free_pages(&rme9652->capture_dma_buf);
+ printk(KERN_ERR "%s: no buffers available\n", rme9652->card_name);
+ return -ENOMEM;
+ }
+
+ /* Align to bus-space 64K boundary */
+
+ cb_bus = (rme9652->capture_dma_buf.addr + 0xFFFF) & ~0xFFFFl;
+ pb_bus = (rme9652->playback_dma_buf.addr + 0xFFFF) & ~0xFFFFl;
+
+ /* Tell the card where it is */
+
+ rme9652_write(rme9652, RME9652_rec_buffer, cb_bus);
+ rme9652_write(rme9652, RME9652_play_buffer, pb_bus);
+
+ rme9652->capture_buffer = rme9652->capture_dma_buf.area + (cb_bus - rme9652->capture_dma_buf.addr);
+ rme9652->playback_buffer = rme9652->playback_dma_buf.area + (pb_bus - rme9652->playback_dma_buf.addr);
+
+ return 0;
+}
+
+static void snd_rme9652_set_defaults(rme9652_t *rme9652)
+{
+ unsigned int k;
+
+ /* ASSUMPTION: rme9652->lock is either held, or
+ there is no need to hold it (e.g. during module
+ initalization).
+ */
+
+ /* set defaults:
+
+ SPDIF Input via Coax
+ autosync clock mode
+ maximum latency (7 = 8192 samples, 64Kbyte buffer,
+ which implies 2 4096 sample, 32Kbyte periods).
+
+ if rev 1.5, initialize the S/PDIF receiver.
+
+ */
+
+ rme9652->control_register =
+ RME9652_inp_0 | rme9652_encode_latency(7);
+
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+ rme9652_reset_hw_pointer(rme9652);
+ rme9652_compute_period_size(rme9652);
+
+ /* default: thru off for all channels */
+
+ for (k = 0; k < RME9652_NCHANNELS; ++k)
+ rme9652_write(rme9652, RME9652_thru_base + k * 4, 0);
+
+ rme9652->thru_bits = 0;
+ rme9652->passthru = 0;
+
+ /* set a default rate so that the channel map is set up */
+
+ rme9652_set_rate(rme9652, 48000);
+}
+
+static irqreturn_t snd_rme9652_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ rme9652_t *rme9652 = (rme9652_t *) dev_id;
+
+ if (!(rme9652_read(rme9652, RME9652_status_register) & RME9652_IRQ)) {
+ return IRQ_NONE;
+ }
+
+ rme9652_write(rme9652, RME9652_irq_clear, 0);
+
+ if (rme9652->capture_substream) {
+ snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+ }
+
+ if (rme9652->playback_substream) {
+ snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
+ }
+ return IRQ_HANDLED;
+}
+
+static snd_pcm_uframes_t snd_rme9652_hw_pointer(snd_pcm_substream_t *substream)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ return rme9652_hw_pointer(rme9652);
+}
+
+static char *rme9652_channel_buffer_location(rme9652_t *rme9652,
+ int stream,
+ int channel)
+
+{
+ int mapped_channel;
+
+ snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL);
+
+ if ((mapped_channel = rme9652->channel_map[channel]) < 0) {
+ return NULL;
+ }
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ return rme9652->capture_buffer +
+ (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES);
+ } else {
+ return rme9652->playback_buffer +
+ (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES);
+ }
+}
+
+static int snd_rme9652_playback_copy(snd_pcm_substream_t *substream, int channel,
+ snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ char *channel_buf;
+
+ snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+
+ channel_buf = rme9652_channel_buffer_location (rme9652,
+ substream->pstr->stream,
+ channel);
+ snd_assert(channel_buf != NULL, return -EIO);
+ if (copy_from_user(channel_buf + pos * 4, src, count * 4))
+ return -EFAULT;
+ return count;
+}
+
+static int snd_rme9652_capture_copy(snd_pcm_substream_t *substream, int channel,
+ snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ char *channel_buf;
+
+ snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+
+ channel_buf = rme9652_channel_buffer_location (rme9652,
+ substream->pstr->stream,
+ channel);
+ snd_assert(channel_buf != NULL, return -EIO);
+ if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
+ return -EFAULT;
+ return count;
+}
+
+static int snd_rme9652_hw_silence(snd_pcm_substream_t *substream, int channel,
+ snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ char *channel_buf;
+
+ channel_buf = rme9652_channel_buffer_location (rme9652,
+ substream->pstr->stream,
+ channel);
+ snd_assert(channel_buf != NULL, return -EIO);
+ memset(channel_buf + pos * 4, 0, count * 4);
+ return count;
+}
+
+static int snd_rme9652_reset(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ snd_pcm_substream_t *other;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ other = rme9652->capture_substream;
+ else
+ other = rme9652->playback_substream;
+ if (rme9652->running)
+ runtime->status->hw_ptr = rme9652_hw_pointer(rme9652);
+ else
+ runtime->status->hw_ptr = 0;
+ if (other) {
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ snd_pcm_runtime_t *oruntime = other->runtime;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == other) {
+ oruntime->status->hw_ptr = runtime->status->hw_ptr;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int snd_rme9652_hw_params(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *params)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ int err;
+ pid_t this_pid;
+ pid_t other_pid;
+
+ spin_lock_irq(&rme9652->lock);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
+ rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream);
+ this_pid = rme9652->playback_pid;
+ other_pid = rme9652->capture_pid;
+ } else {
+ this_pid = rme9652->capture_pid;
+ other_pid = rme9652->playback_pid;
+ }
+
+ if ((other_pid > 0) && (this_pid != other_pid)) {
+
+ /* The other stream is open, and not by the same
+ task as this one. Make sure that the parameters
+ that matter are the same.
+ */
+
+ if ((int)params_rate(params) !=
+ rme9652_adat_sample_rate(rme9652)) {
+ spin_unlock_irq(&rme9652->lock);
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+ return -EBUSY;
+ }
+
+ if (params_period_size(params) != rme9652->period_bytes / 4) {
+ spin_unlock_irq(&rme9652->lock);
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return -EBUSY;
+ }
+
+ /* We're fine. */
+
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+
+ } else {
+ spin_unlock_irq(&rme9652->lock);
+ }
+
+ /* how to make sure that the rate matches an externally-set one ?
+ */
+
+ if ((err = rme9652_set_rate(rme9652, params_rate(params))) < 0) {
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+ return err;
+ }
+
+ if ((err = rme9652_set_interrupt_interval(rme9652, params_period_size(params))) < 0) {
+ _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_rme9652_channel_info(snd_pcm_substream_t *substream,
+ snd_pcm_channel_info_t *info)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ int chn;
+
+ snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL);
+
+ if ((chn = rme9652->channel_map[info->channel]) < 0) {
+ return -EINVAL;
+ }
+
+ info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES;
+ info->first = 0;
+ info->step = 32;
+ return 0;
+}
+
+static int snd_rme9652_ioctl(snd_pcm_substream_t *substream,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL1_RESET:
+ {
+ return snd_rme9652_reset(substream);
+ }
+ case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+ {
+ snd_pcm_channel_info_t *info = arg;
+ return snd_rme9652_channel_info(substream, info);
+ }
+ default:
+ break;
+ }
+
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static void rme9652_silence_playback(rme9652_t *rme9652)
+{
+ memset(rme9652->playback_buffer, 0, RME9652_DMA_AREA_BYTES);
+}
+
+static int snd_rme9652_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ snd_pcm_substream_t *other;
+ int running;
+ spin_lock(&rme9652->lock);
+ running = rme9652->running;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ running |= 1 << substream->stream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ running &= ~(1 << substream->stream);
+ break;
+ default:
+ snd_BUG();
+ spin_unlock(&rme9652->lock);
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ other = rme9652->capture_substream;
+ else
+ other = rme9652->playback_substream;
+
+ if (other) {
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == other) {
+ snd_pcm_trigger_done(s, substream);
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ running |= 1 << s->stream;
+ else
+ running &= ~(1 << s->stream);
+ goto _ok;
+ }
+ }
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
+ substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rme9652_silence_playback(rme9652);
+ } else {
+ if (running &&
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rme9652_silence_playback(rme9652);
+ }
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rme9652_silence_playback(rme9652);
+ }
+ _ok:
+ snd_pcm_trigger_done(substream, substream);
+ if (!rme9652->running && running)
+ rme9652_start(rme9652);
+ else if (rme9652->running && !running)
+ rme9652_stop(rme9652);
+ rme9652->running = running;
+ spin_unlock(&rme9652->lock);
+
+ return 0;
+}
+
+static int snd_rme9652_prepare(snd_pcm_substream_t *substream)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ unsigned long flags;
+ int result = 0;
+
+ spin_lock_irqsave(&rme9652->lock, flags);
+ if (!rme9652->running)
+ rme9652_reset_hw_pointer(rme9652);
+ spin_unlock_irqrestore(&rme9652->lock, flags);
+ return result;
+}
+
+static snd_pcm_hardware_t snd_rme9652_playback_subinfo =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_DOUBLE),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 26,
+ .buffer_bytes_max = RME9652_CHANNEL_BUFFER_BYTES * 26,
+ .period_bytes_min = (64 * 4) * 10,
+ .period_bytes_max = (8192 * 4) * 26,
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_rme9652_capture_subinfo =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 26,
+ .buffer_bytes_max = RME9652_CHANNEL_BUFFER_BYTES *26,
+ .period_bytes_min = (64 * 4) * 10,
+ .period_bytes_max = (8192 * 4) * 26,
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
+ .count = ARRAY_SIZE(period_sizes),
+ .list = period_sizes,
+ .mask = 0
+};
+
+static int snd_rme9652_hw_rule_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ rme9652_t *rme9652 = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ unsigned int list[2] = { rme9652->ds_channels, rme9652->ss_channels };
+ return snd_interval_list(c, 2, list, 0);
+}
+
+static int snd_rme9652_hw_rule_channels_rate(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ rme9652_t *rme9652 = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (r->min > 48000) {
+ snd_interval_t t = {
+ .min = rme9652->ds_channels,
+ .max = rme9652->ds_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->max < 88200) {
+ snd_interval_t t = {
+ .min = rme9652->ss_channels,
+ .max = rme9652->ss_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ }
+ return 0;
+}
+
+static int snd_rme9652_hw_rule_rate_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ rme9652_t *rme9652 = rule->private;
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (c->min >= rme9652->ss_channels) {
+ snd_interval_t t = {
+ .min = 44100,
+ .max = 48000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= rme9652->ds_channels) {
+ snd_interval_t t = {
+ .min = 88200,
+ .max = 96000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ }
+ return 0;
+}
+
+static int snd_rme9652_playback_open(snd_pcm_substream_t *substream)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ spin_lock_irq(&rme9652->lock);
+
+ snd_pcm_set_sync(substream);
+
+ runtime->hw = snd_rme9652_playback_subinfo;
+ runtime->dma_area = rme9652->playback_buffer;
+ runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+
+ if (rme9652->capture_substream == NULL) {
+ rme9652_stop(rme9652);
+ rme9652_set_thru(rme9652, -1, 0);
+ }
+
+ rme9652->playback_pid = current->pid;
+ rme9652->playback_substream = substream;
+
+ spin_unlock_irq(&rme9652->lock);
+
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_rme9652_hw_rule_channels, rme9652,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_rme9652_hw_rule_channels_rate, rme9652,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_rme9652_hw_rule_rate_channels, rme9652,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ rme9652->creg_spdif_stream = rme9652->creg_spdif;
+ rme9652->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id);
+ return 0;
+}
+
+static int snd_rme9652_playback_release(snd_pcm_substream_t *substream)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme9652->lock);
+
+ rme9652->playback_pid = -1;
+ rme9652->playback_substream = NULL;
+
+ spin_unlock_irq(&rme9652->lock);
+
+ rme9652->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id);
+ return 0;
+}
+
+
+static int snd_rme9652_capture_open(snd_pcm_substream_t *substream)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ spin_lock_irq(&rme9652->lock);
+
+ snd_pcm_set_sync(substream);
+
+ runtime->hw = snd_rme9652_capture_subinfo;
+ runtime->dma_area = rme9652->capture_buffer;
+ runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+
+ if (rme9652->playback_substream == NULL) {
+ rme9652_stop(rme9652);
+ rme9652_set_thru(rme9652, -1, 0);
+ }
+
+ rme9652->capture_pid = current->pid;
+ rme9652->capture_substream = substream;
+
+ spin_unlock_irq(&rme9652->lock);
+
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_rme9652_hw_rule_channels, rme9652,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_rme9652_hw_rule_channels_rate, rme9652,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_rme9652_hw_rule_rate_channels, rme9652,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ return 0;
+}
+
+static int snd_rme9652_capture_release(snd_pcm_substream_t *substream)
+{
+ rme9652_t *rme9652 = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&rme9652->lock);
+
+ rme9652->capture_pid = -1;
+ rme9652->capture_substream = NULL;
+
+ spin_unlock_irq(&rme9652->lock);
+ return 0;
+}
+
+static snd_pcm_ops_t snd_rme9652_playback_ops = {
+ .open = snd_rme9652_playback_open,
+ .close = snd_rme9652_playback_release,
+ .ioctl = snd_rme9652_ioctl,
+ .hw_params = snd_rme9652_hw_params,
+ .prepare = snd_rme9652_prepare,
+ .trigger = snd_rme9652_trigger,
+ .pointer = snd_rme9652_hw_pointer,
+ .copy = snd_rme9652_playback_copy,
+ .silence = snd_rme9652_hw_silence,
+};
+
+static snd_pcm_ops_t snd_rme9652_capture_ops = {
+ .open = snd_rme9652_capture_open,
+ .close = snd_rme9652_capture_release,
+ .ioctl = snd_rme9652_ioctl,
+ .hw_params = snd_rme9652_hw_params,
+ .prepare = snd_rme9652_prepare,
+ .trigger = snd_rme9652_trigger,
+ .pointer = snd_rme9652_hw_pointer,
+ .copy = snd_rme9652_capture_copy,
+};
+
+static int __devinit snd_rme9652_create_pcm(snd_card_t *card,
+ rme9652_t *rme9652)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(card,
+ rme9652->card_name,
+ 0, 1, 1, &pcm)) < 0) {
+ return err;
+ }
+
+ rme9652->pcm = pcm;
+ pcm->private_data = rme9652;
+ strcpy(pcm->name, rme9652->card_name);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops);
+
+ pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+ return 0;
+}
+
+static int __devinit snd_rme9652_create(snd_card_t *card,
+ rme9652_t *rme9652,
+ int precise_ptr)
+{
+ struct pci_dev *pci = rme9652->pci;
+ int err;
+ int status;
+ unsigned short rev;
+
+ rme9652->irq = -1;
+ rme9652->card = card;
+
+ pci_read_config_word(rme9652->pci, PCI_CLASS_REVISION, &rev);
+
+ switch (rev & 0xff) {
+ case 3:
+ case 4:
+ case 8:
+ case 9:
+ break;
+
+ default:
+ /* who knows? */
+ return -ENODEV;
+ }
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ spin_lock_init(&rme9652->lock);
+
+ if ((err = pci_request_regions(pci, "rme9652")) < 0)
+ return err;
+ rme9652->port = pci_resource_start(pci, 0);
+ rme9652->iobase = ioremap_nocache(rme9652->port, RME9652_IO_EXTENT);
+ if (rme9652->iobase == NULL) {
+ snd_printk("unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1);
+ return -EBUSY;
+ }
+
+ if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) {
+ snd_printk("unable to request IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ rme9652->irq = pci->irq;
+ rme9652->precise_ptr = precise_ptr;
+
+ /* Determine the h/w rev level of the card. This seems like
+ a particularly kludgy way to encode it, but its what RME
+ chose to do, so we follow them ...
+ */
+
+ status = rme9652_read(rme9652, RME9652_status_register);
+ if (rme9652_decode_spdif_rate(status&RME9652_F) == 1) {
+ rme9652->hw_rev = 15;
+ } else {
+ rme9652->hw_rev = 11;
+ }
+
+ /* Differentiate between the standard Hammerfall, and the
+ "Light", which does not have the expansion board. This
+ method comes from information received from Mathhias
+ Clausen at RME. Display the EEPROM and h/w revID where
+ relevant.
+ */
+
+ switch (rev) {
+ case 8: /* original eprom */
+ strcpy(card->driver, "RME9636");
+ if (rme9652->hw_rev == 15) {
+ rme9652->card_name = "RME Digi9636 (Rev 1.5)";
+ } else {
+ rme9652->card_name = "RME Digi9636";
+ }
+ rme9652->ss_channels = RME9636_NCHANNELS;
+ break;
+ case 9: /* W36_G EPROM */
+ strcpy(card->driver, "RME9636");
+ rme9652->card_name = "RME Digi9636 (Rev G)";
+ rme9652->ss_channels = RME9636_NCHANNELS;
+ break;
+ case 4: /* W52_G EPROM */
+ strcpy(card->driver, "RME9652");
+ rme9652->card_name = "RME Digi9652 (Rev G)";
+ rme9652->ss_channels = RME9652_NCHANNELS;
+ break;
+ case 3: /* original eprom */
+ strcpy(card->driver, "RME9652");
+ if (rme9652->hw_rev == 15) {
+ rme9652->card_name = "RME Digi9652 (Rev 1.5)";
+ } else {
+ rme9652->card_name = "RME Digi9652";
+ }
+ rme9652->ss_channels = RME9652_NCHANNELS;
+ break;
+ }
+
+ rme9652->ds_channels = (rme9652->ss_channels - 2) / 2 + 2;
+
+ pci_set_master(rme9652->pci);
+
+ if ((err = snd_rme9652_initialize_memory(rme9652)) < 0) {
+ return err;
+ }
+
+ if ((err = snd_rme9652_create_pcm(card, rme9652)) < 0) {
+ return err;
+ }
+
+ if ((err = snd_rme9652_create_controls(card, rme9652)) < 0) {
+ return err;
+ }
+
+ snd_rme9652_proc_init(rme9652);
+
+ rme9652->last_spdif_sample_rate = -1;
+ rme9652->last_adat_sample_rate = -1;
+ rme9652->playback_pid = -1;
+ rme9652->capture_pid = -1;
+ rme9652->capture_substream = NULL;
+ rme9652->playback_substream = NULL;
+
+ snd_rme9652_set_defaults(rme9652);
+
+ if (rme9652->hw_rev == 15) {
+ rme9652_initialize_spdif_receiver (rme9652);
+ }
+
+ return 0;
+}
+
+static void snd_rme9652_card_free(snd_card_t *card)
+{
+ rme9652_t *rme9652 = (rme9652_t *) card->private_data;
+
+ if (rme9652)
+ snd_rme9652_free(rme9652);
+}
+
+static int __devinit snd_rme9652_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ rme9652_t *rme9652;
+ snd_card_t *card;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(rme9652_t));
+
+ if (!card)
+ return -ENOMEM;
+
+ rme9652 = (rme9652_t *) card->private_data;
+ card->private_free = snd_rme9652_card_free;
+ rme9652->dev = dev;
+ rme9652->pci = pci;
+ snd_card_set_dev(card, &pci->dev);
+
+ if ((err = snd_rme9652_create(card, rme9652, precise_ptr[dev])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->shortname, rme9652->card_name);
+
+ sprintf(card->longname, "%s at 0x%lx, irq %d",
+ card->shortname, rme9652->port, rme9652->irq);
+
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_rme9652_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "RME Digi9652 (Hammerfall)",
+ .id_table = snd_rme9652_ids,
+ .probe = snd_rme9652_probe,
+ .remove = __devexit_p(snd_rme9652_remove),
+};
+
+static int __init alsa_card_hammerfall_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_hammerfall_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_hammerfall_init)
+module_exit(alsa_card_hammerfall_exit)
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
new file mode 100644
index 00000000000..cfd2c5fd6dd
--- /dev/null
+++ b/sound/pci/sonicvibes.c
@@ -0,0 +1,1534 @@
+/*
+ * Driver for S3 SonicVibes soundcard
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ * BUGS:
+ * It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB?
+ * Driver sometimes hangs... Nobody knows why at this moment...
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("S3 SonicVibes PCI");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+#ifndef PCI_VENDOR_ID_S3
+#define PCI_VENDOR_ID_S3 0x5333
+#endif
+#ifndef PCI_DEVICE_ID_S3_SONICVIBES
+#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static unsigned int dmaio = 0x7a00; /* DDMA i/o address */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for S3 SonicVibes soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for S3 SonicVibes soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable S3 SonicVibes soundcard.");
+module_param_array(reverb, bool, NULL, 0444);
+MODULE_PARM_DESC(reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard.");
+module_param_array(mge, bool, NULL, 0444);
+MODULE_PARM_DESC(mge, "MIC Gain Enable for S3 SonicVibes soundcard.");
+module_param(dmaio, uint, 0444);
+MODULE_PARM_DESC(dmaio, "DDMA i/o base address for S3 SonicVibes soundcard.");
+
+/*
+ * Enhanced port direct registers
+ */
+
+#define SV_REG(sonic, x) ((sonic)->enh_port + SV_REG_##x)
+
+#define SV_REG_CONTROL 0x00 /* R/W: CODEC/Mixer control register */
+#define SV_ENHANCED 0x01 /* audio mode select - enhanced mode */
+#define SV_TEST 0x02 /* test bit */
+#define SV_REVERB 0x04 /* reverb enable */
+#define SV_WAVETABLE 0x08 /* wavetable active / FM active if not set */
+#define SV_INTA 0x20 /* INTA driving - should be always 1 */
+#define SV_RESET 0x80 /* reset chip */
+#define SV_REG_IRQMASK 0x01 /* R/W: CODEC/Mixer interrupt mask register */
+#define SV_DMAA_MASK 0x01 /* mask DMA-A interrupt */
+#define SV_DMAC_MASK 0x04 /* mask DMA-C interrupt */
+#define SV_SPEC_MASK 0x08 /* special interrupt mask - should be always masked */
+#define SV_UD_MASK 0x40 /* Up/Down button interrupt mask */
+#define SV_MIDI_MASK 0x80 /* mask MIDI interrupt */
+#define SV_REG_STATUS 0x02 /* R/O: CODEC/Mixer status register */
+#define SV_DMAA_IRQ 0x01 /* DMA-A interrupt */
+#define SV_DMAC_IRQ 0x04 /* DMA-C interrupt */
+#define SV_SPEC_IRQ 0x08 /* special interrupt */
+#define SV_UD_IRQ 0x40 /* Up/Down interrupt */
+#define SV_MIDI_IRQ 0x80 /* MIDI interrupt */
+#define SV_REG_INDEX 0x04 /* R/W: CODEC/Mixer index address register */
+#define SV_MCE 0x40 /* mode change enable */
+#define SV_TRD 0x80 /* DMA transfer request disabled */
+#define SV_REG_DATA 0x05 /* R/W: CODEC/Mixer index data register */
+
+/*
+ * Enhanced port indirect registers
+ */
+
+#define SV_IREG_LEFT_ADC 0x00 /* Left ADC Input Control */
+#define SV_IREG_RIGHT_ADC 0x01 /* Right ADC Input Control */
+#define SV_IREG_LEFT_AUX1 0x02 /* Left AUX1 Input Control */
+#define SV_IREG_RIGHT_AUX1 0x03 /* Right AUX1 Input Control */
+#define SV_IREG_LEFT_CD 0x04 /* Left CD Input Control */
+#define SV_IREG_RIGHT_CD 0x05 /* Right CD Input Control */
+#define SV_IREG_LEFT_LINE 0x06 /* Left Line Input Control */
+#define SV_IREG_RIGHT_LINE 0x07 /* Right Line Input Control */
+#define SV_IREG_MIC 0x08 /* MIC Input Control */
+#define SV_IREG_GAME_PORT 0x09 /* Game Port Control */
+#define SV_IREG_LEFT_SYNTH 0x0a /* Left Synth Input Control */
+#define SV_IREG_RIGHT_SYNTH 0x0b /* Right Synth Input Control */
+#define SV_IREG_LEFT_AUX2 0x0c /* Left AUX2 Input Control */
+#define SV_IREG_RIGHT_AUX2 0x0d /* Right AUX2 Input Control */
+#define SV_IREG_LEFT_ANALOG 0x0e /* Left Analog Mixer Output Control */
+#define SV_IREG_RIGHT_ANALOG 0x0f /* Right Analog Mixer Output Control */
+#define SV_IREG_LEFT_PCM 0x10 /* Left PCM Input Control */
+#define SV_IREG_RIGHT_PCM 0x11 /* Right PCM Input Control */
+#define SV_IREG_DMA_DATA_FMT 0x12 /* DMA Data Format */
+#define SV_IREG_PC_ENABLE 0x13 /* Playback/Capture Enable Register */
+#define SV_IREG_UD_BUTTON 0x14 /* Up/Down Button Register */
+#define SV_IREG_REVISION 0x15 /* Revision */
+#define SV_IREG_ADC_OUTPUT_CTRL 0x16 /* ADC Output Control */
+#define SV_IREG_DMA_A_UPPER 0x18 /* DMA A Upper Base Count */
+#define SV_IREG_DMA_A_LOWER 0x19 /* DMA A Lower Base Count */
+#define SV_IREG_DMA_C_UPPER 0x1c /* DMA C Upper Base Count */
+#define SV_IREG_DMA_C_LOWER 0x1d /* DMA C Lower Base Count */
+#define SV_IREG_PCM_RATE_LOW 0x1e /* PCM Sampling Rate Low Byte */
+#define SV_IREG_PCM_RATE_HIGH 0x1f /* PCM Sampling Rate High Byte */
+#define SV_IREG_SYNTH_RATE_LOW 0x20 /* Synthesizer Sampling Rate Low Byte */
+#define SV_IREG_SYNTH_RATE_HIGH 0x21 /* Synthesizer Sampling Rate High Byte */
+#define SV_IREG_ADC_CLOCK 0x22 /* ADC Clock Source Selection */
+#define SV_IREG_ADC_ALT_RATE 0x23 /* ADC Alternative Sampling Rate Selection */
+#define SV_IREG_ADC_PLL_M 0x24 /* ADC PLL M Register */
+#define SV_IREG_ADC_PLL_N 0x25 /* ADC PLL N Register */
+#define SV_IREG_SYNTH_PLL_M 0x26 /* Synthesizer PLL M Register */
+#define SV_IREG_SYNTH_PLL_N 0x27 /* Synthesizer PLL N Register */
+#define SV_IREG_MPU401 0x2a /* MPU-401 UART Operation */
+#define SV_IREG_DRIVE_CTRL 0x2b /* Drive Control */
+#define SV_IREG_SRS_SPACE 0x2c /* SRS Space Control */
+#define SV_IREG_SRS_CENTER 0x2d /* SRS Center Control */
+#define SV_IREG_WAVE_SOURCE 0x2e /* Wavetable Sample Source Select */
+#define SV_IREG_ANALOG_POWER 0x30 /* Analog Power Down Control */
+#define SV_IREG_DIGITAL_POWER 0x31 /* Digital Power Down Control */
+
+#define SV_IREG_ADC_PLL SV_IREG_ADC_PLL_M
+#define SV_IREG_SYNTH_PLL SV_IREG_SYNTH_PLL_M
+
+/*
+ * DMA registers
+ */
+
+#define SV_DMA_ADDR0 0x00
+#define SV_DMA_ADDR1 0x01
+#define SV_DMA_ADDR2 0x02
+#define SV_DMA_ADDR3 0x03
+#define SV_DMA_COUNT0 0x04
+#define SV_DMA_COUNT1 0x05
+#define SV_DMA_COUNT2 0x06
+#define SV_DMA_MODE 0x0b
+#define SV_DMA_RESET 0x0d
+#define SV_DMA_MASK 0x0f
+
+/*
+ * Record sources
+ */
+
+#define SV_RECSRC_RESERVED (0x00<<5)
+#define SV_RECSRC_CD (0x01<<5)
+#define SV_RECSRC_DAC (0x02<<5)
+#define SV_RECSRC_AUX2 (0x03<<5)
+#define SV_RECSRC_LINE (0x04<<5)
+#define SV_RECSRC_AUX1 (0x05<<5)
+#define SV_RECSRC_MIC (0x06<<5)
+#define SV_RECSRC_OUT (0x07<<5)
+
+/*
+ * constants
+ */
+
+#define SV_FULLRATE 48000
+#define SV_REFFREQUENCY 24576000
+#define SV_ADCMULT 512
+
+#define SV_MODE_PLAY 1
+#define SV_MODE_CAPTURE 2
+
+/*
+
+ */
+
+typedef struct _snd_sonicvibes sonicvibes_t;
+
+struct _snd_sonicvibes {
+ unsigned long dma1size;
+ unsigned long dma2size;
+ int irq;
+
+ unsigned long sb_port;
+ unsigned long enh_port;
+ unsigned long synth_port;
+ unsigned long midi_port;
+ unsigned long game_port;
+ unsigned int dmaa_port;
+ struct resource *res_dmaa;
+ unsigned int dmac_port;
+ struct resource *res_dmac;
+
+ unsigned char enable;
+ unsigned char irqmask;
+ unsigned char revision;
+ unsigned char format;
+ unsigned char srs_space;
+ unsigned char srs_center;
+ unsigned char mpu_switch;
+ unsigned char wave_source;
+
+ unsigned int mode;
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *playback_substream;
+ snd_pcm_substream_t *capture_substream;
+ snd_rawmidi_t *rmidi;
+ snd_hwdep_t *fmsynth; /* S3FM */
+
+ spinlock_t reg_lock;
+
+ unsigned int p_dma_size;
+ unsigned int c_dma_size;
+
+ snd_kcontrol_t *master_mute;
+ snd_kcontrol_t *master_volume;
+
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+};
+
+static struct pci_device_id snd_sonic_ids[] = {
+ { 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_sonic_ids);
+
+static ratden_t sonicvibes_adc_clock = {
+ .num_min = 4000 * 65536,
+ .num_max = 48000UL * 65536,
+ .num_step = 1,
+ .den = 65536,
+};
+static snd_pcm_hw_constraint_ratdens_t snd_sonicvibes_hw_constraints_adc_clock = {
+ .nrats = 1,
+ .rats = &sonicvibes_adc_clock,
+};
+
+/*
+ * common I/O routines
+ */
+
+static inline void snd_sonicvibes_setdmaa(sonicvibes_t * sonic,
+ unsigned int addr,
+ unsigned int count)
+{
+ count--;
+ outl(addr, sonic->dmaa_port + SV_DMA_ADDR0);
+ outl(count, sonic->dmaa_port + SV_DMA_COUNT0);
+ outb(0x18, sonic->dmaa_port + SV_DMA_MODE);
+#if 0
+ printk("program dmaa: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmaa_port + SV_DMA_ADDR0));
+#endif
+}
+
+static inline void snd_sonicvibes_setdmac(sonicvibes_t * sonic,
+ unsigned int addr,
+ unsigned int count)
+{
+ /* note: dmac is working in word mode!!! */
+ count >>= 1;
+ count--;
+ outl(addr, sonic->dmac_port + SV_DMA_ADDR0);
+ outl(count, sonic->dmac_port + SV_DMA_COUNT0);
+ outb(0x14, sonic->dmac_port + SV_DMA_MODE);
+#if 0
+ printk("program dmac: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmac_port + SV_DMA_ADDR0));
+#endif
+}
+
+static inline unsigned int snd_sonicvibes_getdmaa(sonicvibes_t * sonic)
+{
+ return (inl(sonic->dmaa_port + SV_DMA_COUNT0) & 0xffffff) + 1;
+}
+
+static inline unsigned int snd_sonicvibes_getdmac(sonicvibes_t * sonic)
+{
+ /* note: dmac is working in word mode!!! */
+ return ((inl(sonic->dmac_port + SV_DMA_COUNT0) & 0xffffff) + 1) << 1;
+}
+
+static void snd_sonicvibes_out1(sonicvibes_t * sonic,
+ unsigned char reg,
+ unsigned char value)
+{
+ outb(reg, SV_REG(sonic, INDEX));
+ udelay(10);
+ outb(value, SV_REG(sonic, DATA));
+ udelay(10);
+}
+
+static void snd_sonicvibes_out(sonicvibes_t * sonic,
+ unsigned char reg,
+ unsigned char value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sonic->reg_lock, flags);
+ outb(reg, SV_REG(sonic, INDEX));
+ udelay(10);
+ outb(value, SV_REG(sonic, DATA));
+ udelay(10);
+ spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static unsigned char snd_sonicvibes_in1(sonicvibes_t * sonic, unsigned char reg)
+{
+ unsigned char value;
+
+ outb(reg, SV_REG(sonic, INDEX));
+ udelay(10);
+ value = inb(SV_REG(sonic, DATA));
+ udelay(10);
+ return value;
+}
+
+static unsigned char snd_sonicvibes_in(sonicvibes_t * sonic, unsigned char reg)
+{
+ unsigned long flags;
+ unsigned char value;
+
+ spin_lock_irqsave(&sonic->reg_lock, flags);
+ outb(reg, SV_REG(sonic, INDEX));
+ udelay(10);
+ value = inb(SV_REG(sonic, DATA));
+ udelay(10);
+ spin_unlock_irqrestore(&sonic->reg_lock, flags);
+ return value;
+}
+
+#if 0
+static void snd_sonicvibes_debug(sonicvibes_t * sonic)
+{
+ printk("SV REGS: INDEX = 0x%02x ", inb(SV_REG(sonic, INDEX)));
+ printk(" STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS)));
+ printk(" 0x00: left input = 0x%02x ", snd_sonicvibes_in(sonic, 0x00));
+ printk(" 0x20: synth rate low = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20));
+ printk(" 0x01: right input = 0x%02x ", snd_sonicvibes_in(sonic, 0x01));
+ printk(" 0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21));
+ printk(" 0x02: left AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x02));
+ printk(" 0x22: ADC clock = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22));
+ printk(" 0x03: right AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x03));
+ printk(" 0x23: ADC alt rate = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23));
+ printk(" 0x04: left CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x04));
+ printk(" 0x24: ADC pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24));
+ printk(" 0x05: right CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x05));
+ printk(" 0x25: ADC pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25));
+ printk(" 0x06: left line = 0x%02x ", snd_sonicvibes_in(sonic, 0x06));
+ printk(" 0x26: Synth pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26));
+ printk(" 0x07: right line = 0x%02x ", snd_sonicvibes_in(sonic, 0x07));
+ printk(" 0x27: Synth pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27));
+ printk(" 0x08: MIC = 0x%02x ", snd_sonicvibes_in(sonic, 0x08));
+ printk(" 0x28: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28));
+ printk(" 0x09: Game port = 0x%02x ", snd_sonicvibes_in(sonic, 0x09));
+ printk(" 0x29: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29));
+ printk(" 0x0a: left synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0a));
+ printk(" 0x2a: MPU401 = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a));
+ printk(" 0x0b: right synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0b));
+ printk(" 0x2b: drive ctrl = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b));
+ printk(" 0x0c: left AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0c));
+ printk(" 0x2c: SRS space = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c));
+ printk(" 0x0d: right AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0d));
+ printk(" 0x2d: SRS center = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d));
+ printk(" 0x0e: left analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0e));
+ printk(" 0x2e: wave source = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e));
+ printk(" 0x0f: right analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0f));
+ printk(" 0x2f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f));
+ printk(" 0x10: left PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x10));
+ printk(" 0x30: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30));
+ printk(" 0x11: right PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x11));
+ printk(" 0x31: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31));
+ printk(" 0x12: DMA data format = 0x%02x ", snd_sonicvibes_in(sonic, 0x12));
+ printk(" 0x32: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32));
+ printk(" 0x13: P/C enable = 0x%02x ", snd_sonicvibes_in(sonic, 0x13));
+ printk(" 0x33: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33));
+ printk(" 0x14: U/D button = 0x%02x ", snd_sonicvibes_in(sonic, 0x14));
+ printk(" 0x34: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34));
+ printk(" 0x15: revision = 0x%02x ", snd_sonicvibes_in(sonic, 0x15));
+ printk(" 0x35: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35));
+ printk(" 0x16: ADC output ctrl = 0x%02x ", snd_sonicvibes_in(sonic, 0x16));
+ printk(" 0x36: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36));
+ printk(" 0x17: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x17));
+ printk(" 0x37: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37));
+ printk(" 0x18: DMA A upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x18));
+ printk(" 0x38: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38));
+ printk(" 0x19: DMA A lower cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x19));
+ printk(" 0x39: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39));
+ printk(" 0x1a: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1a));
+ printk(" 0x3a: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a));
+ printk(" 0x1b: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1b));
+ printk(" 0x3b: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b));
+ printk(" 0x1c: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1c));
+ printk(" 0x3c: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c));
+ printk(" 0x1d: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1d));
+ printk(" 0x3d: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d));
+ printk(" 0x1e: PCM rate low = 0x%02x ", snd_sonicvibes_in(sonic, 0x1e));
+ printk(" 0x3e: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e));
+ printk(" 0x1f: PCM rate high = 0x%02x ", snd_sonicvibes_in(sonic, 0x1f));
+ printk(" 0x3f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f));
+}
+
+#endif
+
+static void snd_sonicvibes_setfmt(sonicvibes_t * sonic,
+ unsigned char mask,
+ unsigned char value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sonic->reg_lock, flags);
+ outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX));
+ if (mask) {
+ sonic->format = inb(SV_REG(sonic, DATA));
+ udelay(10);
+ }
+ sonic->format = (sonic->format & mask) | value;
+ outb(sonic->format, SV_REG(sonic, DATA));
+ udelay(10);
+ outb(0, SV_REG(sonic, INDEX));
+ udelay(10);
+ spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static void snd_sonicvibes_pll(unsigned int rate,
+ unsigned int *res_r,
+ unsigned int *res_m,
+ unsigned int *res_n)
+{
+ unsigned int r, m = 0, n = 0;
+ unsigned int xm, xn, xr, xd, metric = ~0U;
+
+ if (rate < 625000 / SV_ADCMULT)
+ rate = 625000 / SV_ADCMULT;
+ if (rate > 150000000 / SV_ADCMULT)
+ rate = 150000000 / SV_ADCMULT;
+ /* slight violation of specs, needed for continuous sampling rates */
+ for (r = 0; rate < 75000000 / SV_ADCMULT; r += 0x20, rate <<= 1);
+ for (xn = 3; xn < 33; xn++) /* 35 */
+ for (xm = 3; xm < 257; xm++) {
+ xr = ((SV_REFFREQUENCY / SV_ADCMULT) * xm) / xn;
+ if (xr >= rate)
+ xd = xr - rate;
+ else
+ xd = rate - xr;
+ if (xd < metric) {
+ metric = xd;
+ m = xm - 2;
+ n = xn - 2;
+ }
+ }
+ *res_r = r;
+ *res_m = m;
+ *res_n = n;
+#if 0
+ printk("metric = %i, xm = %i, xn = %i\n", metric, xm, xn);
+ printk("pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n);
+#endif
+}
+
+static void snd_sonicvibes_setpll(sonicvibes_t * sonic,
+ unsigned char reg,
+ unsigned int rate)
+{
+ unsigned long flags;
+ unsigned int r, m, n;
+
+ snd_sonicvibes_pll(rate, &r, &m, &n);
+ if (sonic != NULL) {
+ spin_lock_irqsave(&sonic->reg_lock, flags);
+ snd_sonicvibes_out1(sonic, reg, m);
+ snd_sonicvibes_out1(sonic, reg + 1, r | n);
+ spin_unlock_irqrestore(&sonic->reg_lock, flags);
+ }
+}
+
+static void snd_sonicvibes_set_adc_rate(sonicvibes_t * sonic, unsigned int rate)
+{
+ unsigned long flags;
+ unsigned int div;
+ unsigned char clock;
+
+ div = 48000 / rate;
+ if (div > 8)
+ div = 8;
+ if ((48000 / div) == rate) { /* use the alternate clock */
+ clock = 0x10;
+ } else { /* use the PLL source */
+ clock = 0x00;
+ snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate);
+ }
+ spin_lock_irqsave(&sonic->reg_lock, flags);
+ snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4);
+ snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock);
+ spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static int snd_sonicvibes_hw_constraint_dac_rate(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ unsigned int rate, div, r, m, n;
+
+ if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min ==
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max) {
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min;
+ div = 48000 / rate;
+ if (div > 8)
+ div = 8;
+ if ((48000 / div) == rate) {
+ params->rate_num = rate;
+ params->rate_den = 1;
+ } else {
+ snd_sonicvibes_pll(rate, &r, &m, &n);
+ snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL);
+ snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL);
+ params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r;
+ params->rate_den = (SV_ADCMULT/512) * (m+2);
+ }
+ }
+ return 0;
+}
+
+static void snd_sonicvibes_set_dac_rate(sonicvibes_t * sonic, unsigned int rate)
+{
+ unsigned int div;
+ unsigned long flags;
+
+ div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE;
+ if (div > 65535)
+ div = 65535;
+ spin_lock_irqsave(&sonic->reg_lock, flags);
+ snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8);
+ snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div);
+ spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static int snd_sonicvibes_trigger(sonicvibes_t * sonic, int what, int cmd)
+{
+ int result = 0;
+
+ spin_lock(&sonic->reg_lock);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ if (!(sonic->enable & what)) {
+ sonic->enable |= what;
+ snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable);
+ }
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ if (sonic->enable & what) {
+ sonic->enable &= ~what;
+ snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable);
+ }
+ } else {
+ result = -EINVAL;
+ }
+ spin_unlock(&sonic->reg_lock);
+ return result;
+}
+
+static irqreturn_t snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ sonicvibes_t *sonic = dev_id;
+ unsigned char status;
+
+ status = inb(SV_REG(sonic, STATUS));
+ if (!(status & (SV_DMAA_IRQ | SV_DMAC_IRQ | SV_MIDI_IRQ)))
+ return IRQ_NONE;
+ if (status == 0xff) { /* failure */
+ outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK));
+ snd_printk("IRQ failure - interrupts disabled!!\n");
+ return IRQ_HANDLED;
+ }
+ if (sonic->pcm) {
+ if (status & SV_DMAA_IRQ)
+ snd_pcm_period_elapsed(sonic->playback_substream);
+ if (status & SV_DMAC_IRQ)
+ snd_pcm_period_elapsed(sonic->capture_substream);
+ }
+ if (sonic->rmidi) {
+ if (status & SV_MIDI_IRQ)
+ snd_mpu401_uart_interrupt(irq, sonic->rmidi->private_data, regs);
+ }
+ if (status & SV_UD_IRQ) {
+ unsigned char udreg;
+ int vol, oleft, oright, mleft, mright;
+
+ spin_lock(&sonic->reg_lock);
+ udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON);
+ vol = udreg & 0x3f;
+ if (!(udreg & 0x40))
+ vol = -vol;
+ oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG);
+ oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG);
+ oleft &= 0x1f;
+ oright &= 0x1f;
+ oleft += vol;
+ if (oleft < 0)
+ oleft = 0;
+ if (oleft > 0x1f)
+ oleft = 0x1f;
+ oright += vol;
+ if (oright < 0)
+ oright = 0;
+ if (oright > 0x1f)
+ oright = 0x1f;
+ if (udreg & 0x80) {
+ mleft ^= 0x80;
+ mright ^= 0x80;
+ }
+ oleft |= mleft & 0x80;
+ oright |= mright & 0x80;
+ snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft);
+ snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright);
+ spin_unlock(&sonic->reg_lock);
+ snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id);
+ snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id);
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * PCM part
+ */
+
+static int snd_sonicvibes_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ return snd_sonicvibes_trigger(sonic, 1, cmd);
+}
+
+static int snd_sonicvibes_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ return snd_sonicvibes_trigger(sonic, 2, cmd);
+}
+
+static int snd_sonicvibes_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sonicvibes_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sonicvibes_playback_prepare(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned char fmt = 0;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ sonic->p_dma_size = size;
+ count--;
+ if (runtime->channels > 1)
+ fmt |= 1;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ fmt |= 2;
+ snd_sonicvibes_setfmt(sonic, ~3, fmt);
+ snd_sonicvibes_set_dac_rate(sonic, runtime->rate);
+ spin_lock_irq(&sonic->reg_lock);
+ snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size);
+ snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8);
+ snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count);
+ spin_unlock_irq(&sonic->reg_lock);
+ return 0;
+}
+
+static int snd_sonicvibes_capture_prepare(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned char fmt = 0;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ sonic->c_dma_size = size;
+ count >>= 1;
+ count--;
+ if (runtime->channels > 1)
+ fmt |= 0x10;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ fmt |= 0x20;
+ snd_sonicvibes_setfmt(sonic, ~0x30, fmt);
+ snd_sonicvibes_set_adc_rate(sonic, runtime->rate);
+ spin_lock_irq(&sonic->reg_lock);
+ snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size);
+ snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8);
+ snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count);
+ spin_unlock_irq(&sonic->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_sonicvibes_playback_pointer(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(sonic->enable & 1))
+ return 0;
+ ptr = sonic->p_dma_size - snd_sonicvibes_getdmaa(sonic);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sonicvibes_capture_pointer(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ size_t ptr;
+ if (!(sonic->enable & 2))
+ return 0;
+ ptr = sonic->c_dma_size - snd_sonicvibes_getdmac(sonic);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_sonicvibes_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 32,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_sonicvibes_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 32,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static int snd_sonicvibes_playback_open(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ sonic->mode |= SV_MODE_PLAY;
+ sonic->playback_substream = substream;
+ runtime->hw = snd_sonicvibes_playback;
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_sonicvibes_hw_constraint_dac_rate, NULL, SNDRV_PCM_HW_PARAM_RATE, -1);
+ return 0;
+}
+
+static int snd_sonicvibes_capture_open(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ sonic->mode |= SV_MODE_CAPTURE;
+ sonic->capture_substream = substream;
+ runtime->hw = snd_sonicvibes_capture;
+ snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &snd_sonicvibes_hw_constraints_adc_clock);
+ return 0;
+}
+
+static int snd_sonicvibes_playback_close(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+
+ sonic->playback_substream = NULL;
+ sonic->mode &= ~SV_MODE_PLAY;
+ return 0;
+}
+
+static int snd_sonicvibes_capture_close(snd_pcm_substream_t * substream)
+{
+ sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+
+ sonic->capture_substream = NULL;
+ sonic->mode &= ~SV_MODE_CAPTURE;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_sonicvibes_playback_ops = {
+ .open = snd_sonicvibes_playback_open,
+ .close = snd_sonicvibes_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sonicvibes_hw_params,
+ .hw_free = snd_sonicvibes_hw_free,
+ .prepare = snd_sonicvibes_playback_prepare,
+ .trigger = snd_sonicvibes_playback_trigger,
+ .pointer = snd_sonicvibes_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sonicvibes_capture_ops = {
+ .open = snd_sonicvibes_capture_open,
+ .close = snd_sonicvibes_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sonicvibes_hw_params,
+ .hw_free = snd_sonicvibes_hw_free,
+ .prepare = snd_sonicvibes_capture_prepare,
+ .trigger = snd_sonicvibes_capture_trigger,
+ .pointer = snd_sonicvibes_capture_pointer,
+};
+
+static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm)
+{
+ sonicvibes_t *sonic = pcm->private_data;
+ sonic->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_sonicvibes_pcm(sonicvibes_t * sonic, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0)
+ return err;
+ snd_assert(pcm != NULL, return -EINVAL);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops);
+
+ pcm->private_data = sonic;
+ pcm->private_free = snd_sonicvibes_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "S3 SonicVibes");
+ sonic->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(sonic->pci), 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+/*
+ * Mixer part
+ */
+
+#define SONICVIBES_MUX(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_sonicvibes_info_mux, \
+ .get = snd_sonicvibes_get_mux, .put = snd_sonicvibes_put_mux }
+
+static int snd_sonicvibes_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[7] = {
+ "CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 2;
+ uinfo->value.enumerated.items = 7;
+ if (uinfo->value.enumerated.item >= 7)
+ uinfo->value.enumerated.item = 6;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_sonicvibes_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&sonic->reg_lock);
+ ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
+ ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
+ spin_unlock_irq(&sonic->reg_lock);
+ return 0;
+}
+
+static int snd_sonicvibes_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+ unsigned short left, right, oval1, oval2;
+ int change;
+
+ if (ucontrol->value.enumerated.item[0] >= 7 ||
+ ucontrol->value.enumerated.item[1] >= 7)
+ return -EINVAL;
+ left = (ucontrol->value.enumerated.item[0] + 1) << 5;
+ right = (ucontrol->value.enumerated.item[1] + 1) << 5;
+ spin_lock_irq(&sonic->reg_lock);
+ oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC);
+ oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC);
+ left = (oval1 & ~SV_RECSRC_OUT) | left;
+ right = (oval2 & ~SV_RECSRC_OUT) | right;
+ change = left != oval1 || right != oval2;
+ snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left);
+ snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right);
+ spin_unlock_irq(&sonic->reg_lock);
+ return change;
+}
+
+#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_sonicvibes_info_single, \
+ .get = snd_sonicvibes_get_single, .put = snd_sonicvibes_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_sonicvibes_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_sonicvibes_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+
+ spin_lock_irq(&sonic->reg_lock);
+ ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask;
+ spin_unlock_irq(&sonic->reg_lock);
+ if (invert)
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int snd_sonicvibes_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+ int change;
+ unsigned short val, oval;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = mask - val;
+ val <<= shift;
+ spin_lock_irq(&sonic->reg_lock);
+ oval = snd_sonicvibes_in1(sonic, reg);
+ val = (oval & ~(mask << shift)) | val;
+ change = val != oval;
+ snd_sonicvibes_out1(sonic, reg, val);
+ spin_unlock_irq(&sonic->reg_lock);
+ return change;
+}
+
+#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_sonicvibes_info_double, \
+ .get = snd_sonicvibes_get_double, .put = snd_sonicvibes_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_sonicvibes_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_sonicvibes_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x07;
+ int shift_right = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+
+ spin_lock_irq(&sonic->reg_lock);
+ ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask;
+ ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask;
+ spin_unlock_irq(&sonic->reg_lock);
+ if (invert) {
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ }
+ return 0;
+}
+
+static int snd_sonicvibes_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x07;
+ int shift_right = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ int change;
+ unsigned short val1, val2, oval1, oval2;
+
+ val1 = ucontrol->value.integer.value[0] & mask;
+ val2 = ucontrol->value.integer.value[1] & mask;
+ if (invert) {
+ val1 = mask - val1;
+ val2 = mask - val2;
+ }
+ val1 <<= shift_left;
+ val2 <<= shift_right;
+ spin_lock_irq(&sonic->reg_lock);
+ oval1 = snd_sonicvibes_in1(sonic, left_reg);
+ oval2 = snd_sonicvibes_in1(sonic, right_reg);
+ val1 = (oval1 & ~(mask << shift_left)) | val1;
+ val2 = (oval2 & ~(mask << shift_right)) | val2;
+ change = val1 != oval1 || val2 != oval2;
+ snd_sonicvibes_out1(sonic, left_reg, val1);
+ snd_sonicvibes_out1(sonic, right_reg, val2);
+ spin_unlock_irq(&sonic->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_sonicvibes_controls[] __devinitdata = {
+SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0),
+SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("CD Playback Switch", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("CD Playback Volume", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Line Playback Switch", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Line Playback Volume", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 0, 0, 31, 1),
+SONICVIBES_SINGLE("Mic Playback Switch", 0, SV_IREG_MIC, 7, 1, 1),
+SONICVIBES_SINGLE("Mic Playback Volume", 0, SV_IREG_MIC, 0, 15, 1),
+SONICVIBES_SINGLE("Mic Boost", 0, SV_IREG_LEFT_ADC, 4, 1, 0),
+SONICVIBES_DOUBLE("Synth Playback Switch", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Synth Playback Volume", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Aux Playback Switch", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Aux Playback Volume", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Master Playback Switch", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Master Playback Volume", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("PCM Playback Switch", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("PCM Playback Volume", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 0, 0, 63, 1),
+SONICVIBES_SINGLE("Loopback Capture Switch", 0, SV_IREG_ADC_OUTPUT_CTRL, 0, 1, 0),
+SONICVIBES_SINGLE("Loopback Capture Volume", 0, SV_IREG_ADC_OUTPUT_CTRL, 2, 63, 1),
+SONICVIBES_MUX("Capture Source", 0)
+};
+
+static void snd_sonicvibes_master_free(snd_kcontrol_t *kcontrol)
+{
+ sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+ sonic->master_mute = NULL;
+ sonic->master_volume = NULL;
+}
+
+static int __devinit snd_sonicvibes_mixer(sonicvibes_t * sonic)
+{
+ snd_card_t *card;
+ snd_kcontrol_t *kctl;
+ unsigned int idx;
+ int err;
+
+ snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL);
+ card = sonic->card;
+ strcpy(card->mixername, "S3 SonicVibes");
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_controls); idx++) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0)
+ return err;
+ switch (idx) {
+ case 0:
+ case 1: kctl->private_free = snd_sonicvibes_master_free; break;
+ }
+ }
+ return 0;
+}
+
+/*
+
+ */
+
+static void snd_sonicvibes_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ sonicvibes_t *sonic = entry->private_data;
+ unsigned char tmp;
+
+ tmp = sonic->srs_space & 0x0f;
+ snd_iprintf(buffer, "SRS 3D : %s\n",
+ sonic->srs_space & 0x80 ? "off" : "on");
+ snd_iprintf(buffer, "SRS Space : %s\n",
+ tmp == 0x00 ? "100%" :
+ tmp == 0x01 ? "75%" :
+ tmp == 0x02 ? "50%" :
+ tmp == 0x03 ? "25%" : "0%");
+ tmp = sonic->srs_center & 0x0f;
+ snd_iprintf(buffer, "SRS Center : %s\n",
+ tmp == 0x00 ? "100%" :
+ tmp == 0x01 ? "75%" :
+ tmp == 0x02 ? "50%" :
+ tmp == 0x03 ? "25%" : "0%");
+ tmp = sonic->wave_source & 0x03;
+ snd_iprintf(buffer, "WaveTable Source : %s\n",
+ tmp == 0x00 ? "on-board ROM" :
+ tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus");
+ tmp = sonic->mpu_switch;
+ snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off");
+ snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off");
+ snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off");
+}
+
+static void __devinit snd_sonicvibes_proc_init(sonicvibes_t * sonic)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry))
+ snd_info_set_text_ops(entry, sonic, 1024, snd_sonicvibes_proc_read);
+}
+
+/*
+
+ */
+
+#ifdef SUPPORT_JOYSTICK
+static snd_kcontrol_new_t snd_sonicvibes_game_control __devinitdata =
+SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0);
+
+static int __devinit snd_sonicvibes_create_gameport(sonicvibes_t *sonic)
+{
+ struct gameport *gp;
+
+ sonic->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "sonicvibes: cannot allocate memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "SonicVibes Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(sonic->pci));
+ gameport_set_dev_parent(gp, &sonic->pci->dev);
+ gp->io = sonic->game_port;
+
+ gameport_register_port(gp);
+
+ snd_ctl_add(sonic->card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic));
+
+ return 0;
+}
+
+static void snd_sonicvibes_free_gameport(sonicvibes_t *sonic)
+{
+ if (sonic->gameport) {
+ gameport_unregister_port(sonic->gameport);
+ sonic->gameport = NULL;
+ }
+}
+#else
+static inline int snd_sonicvibes_create_gameport(sonicvibes_t *sonic) { return -ENOSYS; }
+static inline void snd_sonicvibes_free_gameport(sonicvibes_t *sonic) { }
+#endif
+
+static int snd_sonicvibes_free(sonicvibes_t *sonic)
+{
+ snd_sonicvibes_free_gameport(sonic);
+ pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port);
+ pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port);
+ if (sonic->irq >= 0)
+ free_irq(sonic->irq, (void *)sonic);
+ if (sonic->res_dmaa) {
+ release_resource(sonic->res_dmaa);
+ kfree_nocheck(sonic->res_dmaa);
+ }
+ if (sonic->res_dmac) {
+ release_resource(sonic->res_dmac);
+ kfree_nocheck(sonic->res_dmac);
+ }
+ pci_release_regions(sonic->pci);
+ pci_disable_device(sonic->pci);
+ kfree(sonic);
+ return 0;
+}
+
+static int snd_sonicvibes_dev_free(snd_device_t *device)
+{
+ sonicvibes_t *sonic = device->device_data;
+ return snd_sonicvibes_free(sonic);
+}
+
+static int __devinit snd_sonicvibes_create(snd_card_t * card,
+ struct pci_dev *pci,
+ int reverb,
+ int mge,
+ sonicvibes_t ** rsonic)
+{
+ sonicvibes_t *sonic;
+ unsigned int dmaa, dmac;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_sonicvibes_dev_free,
+ };
+
+ *rsonic = NULL;
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ /* check, if we can restrict PCI DMA transfers to 24 bits */
+ if (pci_set_dma_mask(pci, 0x00ffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) {
+ snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ sonic = kcalloc(1, sizeof(*sonic), GFP_KERNEL);
+ if (sonic == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ spin_lock_init(&sonic->reg_lock);
+ sonic->card = card;
+ sonic->pci = pci;
+ sonic->irq = -1;
+
+ if ((err = pci_request_regions(pci, "S3 SonicVibes")) < 0) {
+ kfree(sonic);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ sonic->sb_port = pci_resource_start(pci, 0);
+ sonic->enh_port = pci_resource_start(pci, 1);
+ sonic->synth_port = pci_resource_start(pci, 2);
+ sonic->midi_port = pci_resource_start(pci, 3);
+ sonic->game_port = pci_resource_start(pci, 4);
+
+ if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_sonicvibes_free(sonic);
+ return -EBUSY;
+ }
+ sonic->irq = pci->irq;
+
+ pci_read_config_dword(pci, 0x40, &dmaa);
+ pci_read_config_dword(pci, 0x48, &dmac);
+ dmaio &= ~0x0f;
+ dmaa &= ~0x0f;
+ dmac &= ~0x0f;
+ if (!dmaa) {
+ dmaa = dmaio;
+ dmaio += 0x10;
+ snd_printk("BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", dmaa);
+ }
+ if (!dmac) {
+ dmac = dmaio;
+ dmaio += 0x10;
+ snd_printk("BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", dmac);
+ }
+ pci_write_config_dword(pci, 0x40, dmaa);
+ pci_write_config_dword(pci, 0x48, dmac);
+
+ if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) {
+ snd_sonicvibes_free(sonic);
+ snd_printk("unable to grab DDMA-A port at 0x%x-0x%x\n", dmaa, dmaa + 0x10 - 1);
+ return -EBUSY;
+ }
+ if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) {
+ snd_sonicvibes_free(sonic);
+ snd_printk("unable to grab DDMA-C port at 0x%x-0x%x\n", dmac, dmac + 0x10 - 1);
+ return -EBUSY;
+ }
+
+ pci_read_config_dword(pci, 0x40, &sonic->dmaa_port);
+ pci_read_config_dword(pci, 0x48, &sonic->dmac_port);
+ sonic->dmaa_port &= ~0x0f;
+ sonic->dmac_port &= ~0x0f;
+ pci_write_config_dword(pci, 0x40, sonic->dmaa_port | 9); /* enable + enhanced */
+ pci_write_config_dword(pci, 0x48, sonic->dmac_port | 9); /* enable */
+ /* ok.. initialize S3 SonicVibes chip */
+ outb(SV_RESET, SV_REG(sonic, CONTROL)); /* reset chip */
+ udelay(100);
+ outb(0, SV_REG(sonic, CONTROL)); /* release reset */
+ udelay(100);
+ outb(SV_ENHANCED | SV_INTA | (reverb ? SV_REVERB : 0), SV_REG(sonic, CONTROL));
+ inb(SV_REG(sonic, STATUS)); /* clear IRQs */
+#if 1
+ snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0); /* drive current 16mA */
+#else
+ snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0x40); /* drive current 8mA */
+#endif
+ snd_sonicvibes_out(sonic, SV_IREG_PC_ENABLE, sonic->enable = 0); /* disable playback & capture */
+ outb(sonic->irqmask = ~(SV_DMAA_MASK | SV_DMAC_MASK | SV_UD_MASK), SV_REG(sonic, IRQMASK));
+ inb(SV_REG(sonic, STATUS)); /* clear IRQs */
+ snd_sonicvibes_out(sonic, SV_IREG_ADC_CLOCK, 0); /* use PLL as clock source */
+ snd_sonicvibes_out(sonic, SV_IREG_ANALOG_POWER, 0); /* power up analog parts */
+ snd_sonicvibes_out(sonic, SV_IREG_DIGITAL_POWER, 0); /* power up digital parts */
+ snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, 8000);
+ snd_sonicvibes_out(sonic, SV_IREG_SRS_SPACE, sonic->srs_space = 0x80); /* SRS space off */
+ snd_sonicvibes_out(sonic, SV_IREG_SRS_CENTER, sonic->srs_center = 0x00);/* SRS center off */
+ snd_sonicvibes_out(sonic, SV_IREG_MPU401, sonic->mpu_switch = 0x05); /* MPU-401 switch */
+ snd_sonicvibes_out(sonic, SV_IREG_WAVE_SOURCE, sonic->wave_source = 0x00); /* onboard ROM */
+ snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_LOW, (8000 * 65536 / SV_FULLRATE) & 0xff);
+ snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_HIGH, ((8000 * 65536 / SV_FULLRATE) >> 8) & 0xff);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_ADC, mge ? 0xd0 : 0xc0);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ADC, 0xc0);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX1, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX1, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_CD, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_CD, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_LINE, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_LINE, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_MIC, 0x8f);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_SYNTH, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_SYNTH, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX2, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX2, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_ANALOG, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ANALOG, 0x9f);
+ snd_sonicvibes_out(sonic, SV_IREG_LEFT_PCM, 0xbf);
+ snd_sonicvibes_out(sonic, SV_IREG_RIGHT_PCM, 0xbf);
+ snd_sonicvibes_out(sonic, SV_IREG_ADC_OUTPUT_CTRL, 0xfc);
+#if 0
+ snd_sonicvibes_debug(sonic);
+#endif
+ sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION);
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) {
+ snd_sonicvibes_free(sonic);
+ return err;
+ }
+
+ snd_sonicvibes_proc_init(sonic);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rsonic = sonic;
+ return 0;
+}
+
+/*
+ * MIDI section
+ */
+
+static snd_kcontrol_new_t snd_sonicvibes_midi_controls[] __devinitdata = {
+SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0),
+SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0),
+SONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0),
+SONICVIBES_SINGLE("SonicVibes External Rx to Synth", 0, SV_IREG_MPU401, 1, 1, 0),
+SONICVIBES_SINGLE("SonicVibes External Tx", 0, SV_IREG_MPU401, 2, 1, 0)
+};
+
+static int snd_sonicvibes_midi_input_open(mpu401_t * mpu)
+{
+ sonicvibes_t *sonic = mpu->private_data;
+ outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK));
+ return 0;
+}
+
+static void snd_sonicvibes_midi_input_close(mpu401_t * mpu)
+{
+ sonicvibes_t *sonic = mpu->private_data;
+ outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK));
+}
+
+static int __devinit snd_sonicvibes_midi(sonicvibes_t * sonic, snd_rawmidi_t * rmidi)
+{
+ mpu401_t * mpu = rmidi->private_data;
+ snd_card_t *card = sonic->card;
+ snd_rawmidi_str_t *dir;
+ unsigned int idx;
+ int err;
+
+ mpu->private_data = sonic;
+ mpu->open_input = snd_sonicvibes_midi_input_open;
+ mpu->close_input = snd_sonicvibes_midi_input_close;
+ dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_midi_controls); idx++)
+ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0)
+ return err;
+ return 0;
+}
+
+static int __devinit snd_sonic_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ sonicvibes_t *sonic;
+ snd_rawmidi_t *midi_uart;
+ opl3_t *opl3;
+ int idx, err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+ for (idx = 0; idx < 5; idx++) {
+ if (pci_resource_start(pci, idx) == 0 ||
+ !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
+ snd_card_free(card);
+ return -ENODEV;
+ }
+ }
+ if ((err = snd_sonicvibes_create(card, pci,
+ reverb[dev] ? 1 : 0,
+ mge[dev] ? 1 : 0,
+ &sonic)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "SonicVibes");
+ strcpy(card->shortname, "S3 SonicVibes");
+ sprintf(card->longname, "%s rev %i at 0x%lx, irq %i",
+ card->shortname,
+ sonic->revision,
+ pci_resource_start(pci, 1),
+ sonic->irq);
+
+ if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_sonicvibes_mixer(sonic)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES,
+ sonic->midi_port, 1,
+ sonic->irq, 0,
+ &midi_uart)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_sonicvibes_midi(sonic, midi_uart);
+ if ((err = snd_opl3_create(card, sonic->synth_port,
+ sonic->synth_port + 2,
+ OPL3_HW_OPL3_SV, 1, &opl3)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_sonicvibes_create_gameport(sonic);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_sonic_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "S3 SonicVibes",
+ .id_table = snd_sonic_ids,
+ .probe = snd_sonic_probe,
+ .remove = __devexit_p(snd_sonic_remove),
+};
+
+static int __init alsa_card_sonicvibes_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_sonicvibes_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_sonicvibes_init)
+module_exit(alsa_card_sonicvibes_exit)
diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile
new file mode 100644
index 00000000000..65bc5b70323
--- /dev/null
+++ b/sound/pci/trident/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-trident-objs := trident.o trident_main.o trident_memory.o
+snd-trident-synth-objs := trident_synth.o
+
+#
+# this function returns:
+# "m" - CONFIG_SND_SEQUENCER is m
+# <empty string> - CONFIG_SND_SEQUENCER is undefined
+# otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
+obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
new file mode 100644
index 00000000000..ad58e08d66e
--- /dev/null
+++ b/sound/pci/trident/trident.c
@@ -0,0 +1,196 @@
+/*
+ * Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard
+ *
+ * Driver was originated by Trident <audio@tridentmicro.com>
+ * Fri Feb 19 15:55:28 MST 1999
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/trident.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, <audio@tridentmicro.com>");
+MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Trident,4DWave DX},"
+ "{Trident,4DWave NX},"
+ "{SiS,SI7018 PCI Audio},"
+ "{Best Union,Miss Melody 4DWave PCI},"
+ "{HIS,4DWave PCI},"
+ "{Warpspeed,ONSpeed 4DWave PCI},"
+ "{Aztech Systems,PCI 64-Q3D},"
+ "{Addonics,SV 750},"
+ "{CHIC,True Sound 4Dwave},"
+ "{Shark,Predator4D-PCI},"
+ "{Jaton,SonicWave 4D},"
+ "{Hoontech,SoundTrack Digital 4DWave NX}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32};
+static int wavetable_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8192};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Trident 4DWave PCI soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Trident 4DWave PCI soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Trident 4DWave PCI soundcard.");
+module_param_array(pcm_channels, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM.");
+module_param_array(wavetable_size, int, NULL, 0444);
+MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth.");
+
+static struct pci_device_id snd_trident_ids[] = {
+ { 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave DX PCI Audio */
+ { 0x1023, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave NX PCI Audio */
+ { 0x1039, 0x7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* SiS SI7018 PCI Audio */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_trident_ids);
+
+static int __devinit snd_trident_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ trident_t *trident;
+ const char *str;
+ int err, pcm_dev = 0;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_trident_create(card, pci,
+ pcm_channels[dev],
+ ((pci->vendor << 16) | pci->device) == TRIDENT_DEVICE_ID_SI7018 ? 1 : 2,
+ wavetable_size[dev],
+ &trident)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX:
+ str = "TRID4DWAVEDX";
+ break;
+ case TRIDENT_DEVICE_ID_NX:
+ str = "TRID4DWAVENX";
+ break;
+ case TRIDENT_DEVICE_ID_SI7018:
+ str = "SI7018";
+ break;
+ default:
+ str = "Unknown";
+ }
+ strcpy(card->driver, str);
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ strcpy(card->shortname, "SiS ");
+ } else {
+ strcpy(card->shortname, "Trident ");
+ }
+ strcat(card->shortname, card->driver);
+ sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d",
+ card->shortname, trident->port, trident->irq);
+
+ if ((err = snd_trident_pcm(trident, pcm_dev++, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX:
+ case TRIDENT_DEVICE_ID_NX:
+ if ((err = snd_trident_foldback_pcm(trident, pcm_dev++, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ break;
+ }
+ if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ if ((err = snd_trident_spdif_pcm(trident, pcm_dev++, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
+ trident->midi_port, 1,
+ trident->irq, 0, &trident->rmidi)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+ if ((err = snd_trident_attach_synthesizer(trident)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+#endif
+
+ snd_trident_create_gameport(trident);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_trident_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Trident4DWaveAudio",
+ .id_table = snd_trident_ids,
+ .probe = snd_trident_probe,
+ .remove = __devexit_p(snd_trident_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_trident_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_trident_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_trident_init)
+module_exit(alsa_card_trident_exit)
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
new file mode 100644
index 00000000000..ccd5ca2ba16
--- /dev/null
+++ b/sound/pci/trident/trident_main.c
@@ -0,0 +1,3991 @@
+/*
+ * Maintained by Jaroslav Kysela <perex@suse.cz>
+ * Originated by audio@tridentmicro.com
+ * Fri Feb 19 15:55:28 MST 1999
+ * Routines for control of Trident 4DWave (DX and NX) chip
+ *
+ * BUGS:
+ *
+ * TODO:
+ * ---
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/gameport.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/trident.h>
+#include <sound/asoundef.h>
+
+#include <asm/io.h>
+
+static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);
+static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);
+static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_PM
+static int snd_trident_suspend(snd_card_t *card, pm_message_t state);
+static int snd_trident_resume(snd_card_t *card);
+#endif
+static int snd_trident_sis_reset(trident_t *trident);
+
+static void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max);
+static int snd_trident_free(trident_t *trident);
+
+/*
+ * common I/O routines
+ */
+
+
+#if 0
+static void snd_trident_print_voice_regs(trident_t *trident, int voice)
+{
+ unsigned int val, tmp;
+
+ printk("Trident voice %i:\n", voice);
+ outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
+ val = inl(TRID_REG(trident, CH_LBA));
+ printk("LBA: 0x%x\n", val);
+ val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+ printk("GVSel: %i\n", val >> 31);
+ printk("Pan: 0x%x\n", (val >> 24) & 0x7f);
+ printk("Vol: 0x%x\n", (val >> 16) & 0xff);
+ printk("CTRL: 0x%x\n", (val >> 12) & 0x0f);
+ printk("EC: 0x%x\n", val & 0x0fff);
+ if (trident->device != TRIDENT_DEVICE_ID_NX) {
+ val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
+ printk("CSO: 0x%x\n", val >> 16);
+ printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff);
+ printk("FMS: 0x%x\n", val & 0x0f);
+ val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
+ printk("ESO: 0x%x\n", val >> 16);
+ printk("Delta: 0x%x\n", val & 0xffff);
+ val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+ } else { // TRIDENT_DEVICE_ID_NX
+ val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
+ tmp = (val >> 24) & 0xff;
+ printk("CSO: 0x%x\n", val & 0x00ffffff);
+ val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
+ tmp |= (val >> 16) & 0xff00;
+ printk("Delta: 0x%x\n", tmp);
+ printk("ESO: 0x%x\n", val & 0x00ffffff);
+ val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
+ printk("Alpha: 0x%x\n", val >> 20);
+ printk("FMS: 0x%x\n", (val >> 16) & 0x0f);
+ }
+ printk("FMC: 0x%x\n", (val >> 14) & 3);
+ printk("RVol: 0x%x\n", (val >> 7) & 0x7f);
+ printk("CVol: 0x%x\n", val & 0x7f);
+}
+#endif
+
+/*---------------------------------------------------------------------------
+ unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+
+ Description: This routine will do all of the reading from the external
+ CODEC (AC97).
+
+ Parameters: ac97 - ac97 codec structure
+ reg - CODEC register index, from AC97 Hal.
+
+ returns: 16 bit value read from the AC97.
+
+ ---------------------------------------------------------------------------*/
+static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+{
+ unsigned int data = 0, treg;
+ unsigned short count = 0xffff;
+ unsigned long flags;
+ trident_t *trident = ac97->private_data;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ if (trident->device == TRIDENT_DEVICE_ID_DX) {
+ data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
+ outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
+ do {
+ data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
+ if ((data & DX_AC97_BUSY_READ) == 0)
+ break;
+ } while (--count);
+ } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
+ treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
+ outl(data, TRID_REG(trident, treg));
+ do {
+ data = inl(TRID_REG(trident, treg));
+ if ((data & 0x00000C00) == 0)
+ break;
+ } while (--count);
+ } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+ if (ac97->num == 1)
+ data |= SI_AC97_SECONDARY;
+ outl(data, TRID_REG(trident, SI_AC97_READ));
+ do {
+ data = inl(TRID_REG(trident, SI_AC97_READ));
+ if ((data & (SI_AC97_BUSY_READ)) == 0)
+ break;
+ } while (--count);
+ }
+
+ if (count == 0 && !trident->ac97_detect) {
+ snd_printk("ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data);
+ data = 0;
+ }
+
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return ((unsigned short) (data >> 16));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+
+ Description: This routine will do all of the writing to the external
+ CODEC (AC97).
+
+ Parameters: ac97 - ac97 codec structure
+ reg - CODEC register index, from AC97 Hal.
+ data - Lower 16 bits are the data to write to CODEC.
+
+ returns: TRUE if everything went ok, else FALSE.
+
+ ---------------------------------------------------------------------------*/
+static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+{
+ unsigned int address, data;
+ unsigned short count = 0xffff;
+ unsigned long flags;
+ trident_t *trident = ac97->private_data;
+
+ data = ((unsigned long) wdata) << 16;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ if (trident->device == TRIDENT_DEVICE_ID_DX) {
+ address = DX_ACR0_AC97_W;
+
+ /* read AC-97 write register status */
+ do {
+ if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
+ break;
+ } while (--count);
+
+ data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
+ } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ address = NX_ACR1_AC97_W;
+
+ /* read AC-97 write register status */
+ do {
+ if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
+ break;
+ } while (--count);
+
+ data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
+ } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ address = SI_AC97_WRITE;
+
+ /* read AC-97 write register status */
+ do {
+ if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
+ break;
+ } while (--count);
+
+ data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+ if (ac97->num == 1)
+ data |= SI_AC97_SECONDARY;
+ } else {
+ address = 0; /* keep GCC happy */
+ count = 0; /* return */
+ }
+
+ if (count == 0) {
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return;
+ }
+ outl(data, TRID_REG(trident, address));
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_enable_eso(trident_t *trident)
+
+ Description: This routine will enable end of loop interrupts.
+ End of loop interrupts will occur when a running
+ channel reaches ESO.
+ Also enables middle of loop interrupts.
+
+ Parameters: trident - pointer to target device class for 4DWave.
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_enable_eso(trident_t * trident)
+{
+ unsigned int val;
+
+ val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+ val |= ENDLP_IE;
+ val |= MIDLP_IE;
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+ val |= BANK_B_EN;
+ outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_disable_eso(trident_t *trident)
+
+ Description: This routine will disable end of loop interrupts.
+ End of loop interrupts will occur when a running
+ channel reaches ESO.
+ Also disables middle of loop interrupts.
+
+ Parameters:
+ trident - pointer to target device class for 4DWave.
+
+ returns: TRUE if everything went ok, else FALSE.
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_disable_eso(trident_t * trident)
+{
+ unsigned int tmp;
+
+ tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+ tmp &= ~ENDLP_IE;
+ tmp &= ~MIDLP_IE;
+ outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_start_voice(trident_t * trident, unsigned int voice)
+
+ Description: Start a voice, any channel 0 thru 63.
+ This routine automatically handles the fact that there are
+ more than 32 channels available.
+
+ Parameters : voice - Voice number 0 thru n.
+ trident - pointer to target device class for 4DWave.
+
+ Return Value: None.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_start_voice(trident_t * trident, unsigned int voice)
+{
+ unsigned int mask = 1 << (voice & 0x1f);
+ unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;
+
+ outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_stop_voice(trident_t * trident, unsigned int voice)
+
+ Description: Stop a voice, any channel 0 thru 63.
+ This routine automatically handles the fact that there are
+ more than 32 channels available.
+
+ Parameters : voice - Voice number 0 thru n.
+ trident - pointer to target device class for 4DWave.
+
+ Return Value: None.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_stop_voice(trident_t * trident, unsigned int voice)
+{
+ unsigned int mask = 1 << (voice & 0x1f);
+ unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;
+
+ outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+ int snd_trident_allocate_pcm_channel(trident_t *trident)
+
+ Description: Allocate hardware channel in Bank B (32-63).
+
+ Parameters : trident - pointer to target device class for 4DWave.
+
+ Return Value: hardware channel - 32-63 or -1 when no channel is available
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_pcm_channel(trident_t * trident)
+{
+ int idx;
+
+ if (trident->ChanPCMcnt >= trident->ChanPCM)
+ return -1;
+ for (idx = 31; idx >= 0; idx--) {
+ if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
+ trident->ChanMap[T4D_BANK_B] |= 1 << idx;
+ trident->ChanPCMcnt++;
+ return idx + 32;
+ }
+ }
+ return -1;
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_free_pcm_channel(int channel)
+
+ Description: Free hardware channel in Bank B (32-63)
+
+ Parameters : trident - pointer to target device class for 4DWave.
+ channel - hardware channel number 0-63
+
+ Return Value: none
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_pcm_channel(trident_t *trident, int channel)
+{
+ if (channel < 32 || channel > 63)
+ return;
+ channel &= 0x1f;
+ if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
+ trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
+ trident->ChanPCMcnt--;
+ }
+}
+
+/*---------------------------------------------------------------------------
+ unsigned int snd_trident_allocate_synth_channel(void)
+
+ Description: Allocate hardware channel in Bank A (0-31).
+
+ Parameters : trident - pointer to target device class for 4DWave.
+
+ Return Value: hardware channel - 0-31 or -1 when no channel is available
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_synth_channel(trident_t * trident)
+{
+ int idx;
+
+ for (idx = 31; idx >= 0; idx--) {
+ if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
+ trident->ChanMap[T4D_BANK_A] |= 1 << idx;
+ trident->synth.ChanSynthCount++;
+ return idx;
+ }
+ }
+ return -1;
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_free_synth_channel( int channel )
+
+ Description: Free hardware channel in Bank B (0-31).
+
+ Parameters : trident - pointer to target device class for 4DWave.
+ channel - hardware channel number 0-63
+
+ Return Value: none
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_synth_channel(trident_t *trident, int channel)
+{
+ if (channel < 0 || channel > 31)
+ return;
+ channel &= 0x1f;
+ if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) {
+ trident->ChanMap[T4D_BANK_A] &= ~(1 << channel);
+ trident->synth.ChanSynthCount--;
+ }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_voice_regs
+
+ Description: This routine will complete and write the 5 hardware channel
+ registers to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Each register field.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_write_voice_regs(trident_t * trident,
+ snd_trident_voice_t * voice)
+{
+ unsigned int FmcRvolCvol;
+ unsigned int regs[5];
+
+ regs[1] = voice->LBA;
+ regs[4] = (voice->GVSel << 31) |
+ ((voice->Pan & 0x0000007f) << 24) |
+ ((voice->CTRL & 0x0000000f) << 12);
+ FmcRvolCvol = ((voice->FMC & 3) << 14) |
+ ((voice->RVol & 0x7f) << 7) |
+ (voice->CVol & 0x7f);
+
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_SI7018:
+ regs[4] |= voice->number > 31 ?
+ (voice->Vol & 0x000003ff) :
+ ((voice->Vol & 0x00003fc) << (16-2)) |
+ (voice->EC & 0x00000fff);
+ regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f);
+ regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
+ regs[3] = (voice->Attribute << 16) | FmcRvolCvol;
+ break;
+ case TRIDENT_DEVICE_ID_DX:
+ regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
+ (voice->EC & 0x00000fff);
+ regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f);
+ regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
+ regs[3] = FmcRvolCvol;
+ break;
+ case TRIDENT_DEVICE_ID_NX:
+ regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
+ (voice->EC & 0x00000fff);
+ regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff);
+ regs[2] = ((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff);
+ regs[3] = (voice->Alpha << 20) | ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol;
+ break;
+ default:
+ snd_BUG();
+ }
+
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outl(regs[0], TRID_REG(trident, CH_START + 0));
+ outl(regs[1], TRID_REG(trident, CH_START + 4));
+ outl(regs[2], TRID_REG(trident, CH_START + 8));
+ outl(regs[3], TRID_REG(trident, CH_START + 12));
+ outl(regs[4], TRID_REG(trident, CH_START + 16));
+
+#if 0
+ printk("written %i channel:\n", voice->number);
+ printk(" regs[0] = 0x%x/0x%x\n", regs[0], inl(TRID_REG(trident, CH_START + 0)));
+ printk(" regs[1] = 0x%x/0x%x\n", regs[1], inl(TRID_REG(trident, CH_START + 4)));
+ printk(" regs[2] = 0x%x/0x%x\n", regs[2], inl(TRID_REG(trident, CH_START + 8)));
+ printk(" regs[3] = 0x%x/0x%x\n", regs[3], inl(TRID_REG(trident, CH_START + 12)));
+ printk(" regs[4] = 0x%x/0x%x\n", regs[4], inl(TRID_REG(trident, CH_START + 16)));
+#endif
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_cso_reg
+
+ Description: This routine will write the new CSO offset
+ register to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ CSO - new CSO value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cso_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CSO)
+{
+ voice->CSO = CSO;
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ if (trident->device != TRIDENT_DEVICE_ID_NX) {
+ outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
+ } else {
+ outl((voice->Delta << 24) | (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+ }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_eso_reg
+
+ Description: This routine will write the new ESO offset
+ register to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ ESO - new ESO value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_eso_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int ESO)
+{
+ voice->ESO = ESO;
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ if (trident->device != TRIDENT_DEVICE_ID_NX) {
+ outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2);
+ } else {
+ outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_ESO));
+ }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_vol_reg
+
+ Description: This routine will write the new voice volume
+ register to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Vol - new voice volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_vol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Vol)
+{
+ voice->Vol = Vol;
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX:
+ case TRIDENT_DEVICE_ID_NX:
+ outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
+ break;
+ case TRIDENT_DEVICE_ID_SI7018:
+ // printk("voice->Vol = 0x%x\n", voice->Vol);
+ outw((voice->CTRL << 12) | voice->Vol, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_pan_reg
+
+ Description: This routine will write the new voice pan
+ register to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Pan - new pan value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_pan_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Pan)
+{
+ voice->Pan = Pan;
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3));
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_rvol_reg
+
+ Description: This routine will write the new reverb volume
+ register to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ RVol - new reverb volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_rvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int RVol)
+{
+ voice->RVol = RVol;
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f),
+ TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_cvol_reg
+
+ Description: This routine will write the new chorus volume
+ register to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ CVol - new chorus volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CVol)
+{
+ voice->CVol = CVol;
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f),
+ TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_convert_rate
+
+ Description: This routine converts rate in HZ to hardware delta value.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ rate - Real or Virtual channel number.
+
+ Returns: Delta value.
+
+ ---------------------------------------------------------------------------*/
+static unsigned int snd_trident_convert_rate(unsigned int rate)
+{
+ unsigned int delta;
+
+ // We special case 44100 and 8000 since rounding with the equation
+ // does not give us an accurate enough value. For 11025 and 22050
+ // the equation gives us the best answer. All other frequencies will
+ // also use the equation. JDW
+ if (rate == 44100)
+ delta = 0xeb3;
+ else if (rate == 8000)
+ delta = 0x2ab;
+ else if (rate == 48000)
+ delta = 0x1000;
+ else
+ delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
+ return delta;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_convert_adc_rate
+
+ Description: This routine converts rate in HZ to hardware delta value.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ rate - Real or Virtual channel number.
+
+ Returns: Delta value.
+
+ ---------------------------------------------------------------------------*/
+static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
+{
+ unsigned int delta;
+
+ // We special case 44100 and 8000 since rounding with the equation
+ // does not give us an accurate enough value. For 11025 and 22050
+ // the equation gives us the best answer. All other frequencies will
+ // also use the equation. JDW
+ if (rate == 44100)
+ delta = 0x116a;
+ else if (rate == 8000)
+ delta = 0x6000;
+ else if (rate == 48000)
+ delta = 0x1000;
+ else
+ delta = ((48000 << 12) / rate) & 0x0000ffff;
+ return delta;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spurious_threshold
+
+ Description: This routine converts rate in HZ to spurious threshold.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ rate - Real or Virtual channel number.
+
+ Returns: Delta value.
+
+ ---------------------------------------------------------------------------*/
+static unsigned int snd_trident_spurious_threshold(unsigned int rate, unsigned int period_size)
+{
+ unsigned int res = (rate * period_size) / 48000;
+ if (res < 64)
+ res = res / 2;
+ else
+ res -= 32;
+ return res;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_control_mode
+
+ Description: This routine returns a control mode for a PCM channel.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ substream - PCM substream
+
+ Returns: Control value.
+
+ ---------------------------------------------------------------------------*/
+static unsigned int snd_trident_control_mode(snd_pcm_substream_t *substream)
+{
+ unsigned int CTRL;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ /* set ctrl mode
+ CTRL default: 8-bit (unsigned) mono, loop mode enabled
+ */
+ CTRL = 0x00000001;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ CTRL |= 0x00000008; // 16-bit data
+ if (snd_pcm_format_signed(runtime->format))
+ CTRL |= 0x00000002; // signed data
+ if (runtime->channels > 1)
+ CTRL |= 0x00000004; // stereo data
+ return CTRL;
+}
+
+/*
+ * PCM part
+ */
+
+/*---------------------------------------------------------------------------
+ snd_trident_ioctl
+
+ Description: Device I/O control handler for playback/capture parameters.
+
+ Paramters: substream - PCM substream class
+ cmd - what ioctl message to process
+ arg - additional message infoarg
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_ioctl(snd_pcm_substream_t * substream,
+ unsigned int cmd,
+ void *arg)
+{
+ /* FIXME: it seems that with small periods the behaviour of
+ trident hardware is unpredictable and interrupt generator
+ is broken */
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_allocate_pcm_mem
+
+ Description: Allocate PCM ring buffer for given substream
+
+ Parameters: substream - PCM substream class
+ hw_params - hardware parameters
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_pcm_mem(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ int err;
+
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ if (trident->tlb.entries) {
+ if (err > 0) { /* change */
+ if (voice->memblk)
+ snd_trident_free_pages(trident, voice->memblk);
+ voice->memblk = snd_trident_alloc_pages(trident, substream);
+ if (voice->memblk == NULL)
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_allocate_evoice
+
+ Description: Allocate extra voice as interrupt generator
+
+ Parameters: substream - PCM substream class
+ hw_params - hardware parameters
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_evoice(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice->extra;
+
+ /* voice management */
+
+ if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
+ if (evoice == NULL) {
+ evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+ if (evoice == NULL)
+ return -ENOMEM;
+ voice->extra = evoice;
+ evoice->substream = substream;
+ }
+ } else {
+ if (evoice != NULL) {
+ snd_trident_free_voice(trident, evoice);
+ voice->extra = evoice = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_hw_params
+
+ Description: Set the hardware parameters for the playback device.
+
+ Parameters: substream - PCM substream class
+ hw_params - hardware parameters
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ int err;
+
+ err = snd_trident_allocate_pcm_mem(substream, hw_params);
+ if (err >= 0)
+ err = snd_trident_allocate_evoice(substream, hw_params);
+ return err;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_hw_free
+
+ Description: Release the hardware resources for the playback device.
+
+ Parameters: substream - PCM substream class
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_hw_free(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice ? voice->extra : NULL;
+
+ if (trident->tlb.entries) {
+ if (voice && voice->memblk) {
+ snd_trident_free_pages(trident, voice->memblk);
+ voice->memblk = NULL;
+ }
+ }
+ snd_pcm_lib_free_pages(substream);
+ if (evoice != NULL) {
+ snd_trident_free_voice(trident, evoice);
+ voice->extra = NULL;
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_prepare
+
+ Description: Prepare playback device for playback.
+
+ Parameters: substream - PCM substream class
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_playback_prepare(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice->extra;
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number];
+
+ spin_lock_irq(&trident->reg_lock);
+
+ /* set delta (rate) value */
+ voice->Delta = snd_trident_convert_rate(runtime->rate);
+ voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+ /* set Loop Begin Address */
+ if (voice->memblk)
+ voice->LBA = voice->memblk->offset;
+ else
+ voice->LBA = runtime->dma_addr;
+
+ voice->CSO = 0;
+ voice->ESO = runtime->buffer_size - 1; /* in samples */
+ voice->CTRL = snd_trident_control_mode(substream);
+ voice->FMC = 3;
+ voice->GVSel = 1;
+ voice->EC = 0;
+ voice->Alpha = 0;
+ voice->FMS = 0;
+ voice->Vol = mix->vol;
+ voice->RVol = mix->rvol;
+ voice->CVol = mix->cvol;
+ voice->Pan = mix->pan;
+ voice->Attribute = 0;
+#if 0
+ voice->Attribute = (1<<(30-16))|(2<<(26-16))|
+ (0<<(24-16))|(0x1f<<(19-16));
+#else
+ voice->Attribute = 0;
+#endif
+
+ snd_trident_write_voice_regs(trident, voice);
+
+ if (evoice != NULL) {
+ evoice->Delta = voice->Delta;
+ evoice->spurious_threshold = voice->spurious_threshold;
+ evoice->LBA = voice->LBA;
+ evoice->CSO = 0;
+ evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
+ evoice->CTRL = voice->CTRL;
+ evoice->FMC = 3;
+ evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+ evoice->EC = 0;
+ evoice->Alpha = 0;
+ evoice->FMS = 0;
+ evoice->Vol = 0x3ff; /* mute */
+ evoice->RVol = evoice->CVol = 0x7f; /* mute */
+ evoice->Pan = 0x7f; /* mute */
+#if 0
+ evoice->Attribute = (1<<(30-16))|(2<<(26-16))|
+ (0<<(24-16))|(0x1f<<(19-16));
+#else
+ evoice->Attribute = 0;
+#endif
+ snd_trident_write_voice_regs(trident, evoice);
+ evoice->isync2 = 1;
+ evoice->isync_mark = runtime->period_size;
+ evoice->ESO = (runtime->period_size * 2) - 1;
+ }
+
+ spin_unlock_irq(&trident->reg_lock);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_hw_params
+
+ Description: Set the hardware parameters for the capture device.
+
+ Parameters: substream - PCM substream class
+ hw_params - hardware parameters
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_trident_allocate_pcm_mem(substream, hw_params);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_prepare
+
+ Description: Prepare capture device for playback.
+
+ Parameters: substream - PCM substream class
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_prepare(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ unsigned int val, ESO_bytes;
+
+ spin_lock_irq(&trident->reg_lock);
+
+ // Initilize the channel and set channel Mode
+ outb(0, TRID_REG(trident, LEGACY_DMAR15));
+
+ // Set DMA channel operation mode register
+ outb(0x54, TRID_REG(trident, LEGACY_DMAR11));
+
+ // Set channel buffer Address, DMAR0 expects contiguous PCI memory area
+ voice->LBA = runtime->dma_addr;
+ outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0));
+ if (voice->memblk)
+ voice->LBA = voice->memblk->offset;
+
+ // set ESO
+ ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1;
+ outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6));
+ outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4));
+ ESO_bytes++;
+
+ // Set channel sample rate, 4.12 format
+ val = (((unsigned int) 48000L << 12) + (runtime->rate/2)) / runtime->rate;
+ outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));
+
+ // Set channel interrupt blk length
+ if (snd_pcm_format_width(runtime->format) == 16) {
+ val = (unsigned short) ((ESO_bytes >> 1) - 1);
+ } else {
+ val = (unsigned short) (ESO_bytes - 1);
+ }
+
+ outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL));
+
+ // Right now, set format and start to run captureing,
+ // continuous run loop enable.
+ trident->bDMAStart = 0x19; // 0001 1001b
+
+ if (snd_pcm_format_width(runtime->format) == 16)
+ trident->bDMAStart |= 0x80;
+ if (snd_pcm_format_signed(runtime->format))
+ trident->bDMAStart |= 0x20;
+ if (runtime->channels > 1)
+ trident->bDMAStart |= 0x40;
+
+ // Prepare capture intr channel
+
+ voice->Delta = snd_trident_convert_rate(runtime->rate);
+ voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+ voice->isync = 1;
+ voice->isync_mark = runtime->period_size;
+ voice->isync_max = runtime->buffer_size;
+
+ // Set voice parameters
+ voice->CSO = 0;
+ voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
+ voice->CTRL = snd_trident_control_mode(substream);
+ voice->FMC = 3;
+ voice->RVol = 0x7f;
+ voice->CVol = 0x7f;
+ voice->GVSel = 1;
+ voice->Pan = 0x7f; /* mute */
+ voice->Vol = 0x3ff; /* mute */
+ voice->EC = 0;
+ voice->Alpha = 0;
+ voice->FMS = 0;
+ voice->Attribute = 0;
+
+ snd_trident_write_voice_regs(trident, voice);
+
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_si7018_capture_hw_params
+
+ Description: Set the hardware parameters for the capture device.
+
+ Parameters: substream - PCM substream class
+ hw_params - hardware parameters
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ int err;
+
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+
+ return snd_trident_allocate_evoice(substream, hw_params);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_si7018_capture_hw_free
+
+ Description: Release the hardware resources for the capture device.
+
+ Parameters: substream - PCM substream class
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_hw_free(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice ? voice->extra : NULL;
+
+ snd_pcm_lib_free_pages(substream);
+ if (evoice != NULL) {
+ snd_trident_free_voice(trident, evoice);
+ voice->extra = NULL;
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_si7018_capture_prepare
+
+ Description: Prepare capture device for playback.
+
+ Parameters: substream - PCM substream class
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_prepare(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice->extra;
+
+ spin_lock_irq(&trident->reg_lock);
+
+ voice->LBA = runtime->dma_addr;
+ voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
+ voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+ // Set voice parameters
+ voice->CSO = 0;
+ voice->ESO = runtime->buffer_size - 1; /* in samples */
+ voice->CTRL = snd_trident_control_mode(substream);
+ voice->FMC = 0;
+ voice->RVol = 0;
+ voice->CVol = 0;
+ voice->GVSel = 1;
+ voice->Pan = T4D_DEFAULT_PCM_PAN;
+ voice->Vol = 0;
+ voice->EC = 0;
+ voice->Alpha = 0;
+ voice->FMS = 0;
+
+ voice->Attribute = (2 << (30-16)) |
+ (2 << (26-16)) |
+ (2 << (24-16)) |
+ (1 << (23-16));
+
+ snd_trident_write_voice_regs(trident, voice);
+
+ if (evoice != NULL) {
+ evoice->Delta = snd_trident_convert_rate(runtime->rate);
+ evoice->spurious_threshold = voice->spurious_threshold;
+ evoice->LBA = voice->LBA;
+ evoice->CSO = 0;
+ evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */
+ evoice->CTRL = voice->CTRL;
+ evoice->FMC = 3;
+ evoice->GVSel = 0;
+ evoice->EC = 0;
+ evoice->Alpha = 0;
+ evoice->FMS = 0;
+ evoice->Vol = 0x3ff; /* mute */
+ evoice->RVol = evoice->CVol = 0x7f; /* mute */
+ evoice->Pan = 0x7f; /* mute */
+ evoice->Attribute = 0;
+ snd_trident_write_voice_regs(trident, evoice);
+ evoice->isync2 = 1;
+ evoice->isync_mark = runtime->period_size;
+ evoice->ESO = (runtime->period_size * 2) - 1;
+ }
+
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_prepare
+
+ Description: Prepare foldback capture device for playback.
+
+ Parameters: substream - PCM substream class
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_prepare(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice->extra;
+
+ spin_lock_irq(&trident->reg_lock);
+
+ /* Set channel buffer Address */
+ if (voice->memblk)
+ voice->LBA = voice->memblk->offset;
+ else
+ voice->LBA = runtime->dma_addr;
+
+ /* set target ESO for channel */
+ voice->ESO = runtime->buffer_size - 1; /* in samples */
+
+ /* set sample rate */
+ voice->Delta = 0x1000;
+ voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
+
+ voice->CSO = 0;
+ voice->CTRL = snd_trident_control_mode(substream);
+ voice->FMC = 3;
+ voice->RVol = 0x7f;
+ voice->CVol = 0x7f;
+ voice->GVSel = 1;
+ voice->Pan = 0x7f; /* mute */
+ voice->Vol = 0x3ff; /* mute */
+ voice->EC = 0;
+ voice->Alpha = 0;
+ voice->FMS = 0;
+ voice->Attribute = 0;
+
+ /* set up capture channel */
+ outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan));
+
+ snd_trident_write_voice_regs(trident, voice);
+
+ if (evoice != NULL) {
+ evoice->Delta = voice->Delta;
+ evoice->spurious_threshold = voice->spurious_threshold;
+ evoice->LBA = voice->LBA;
+ evoice->CSO = 0;
+ evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
+ evoice->CTRL = voice->CTRL;
+ evoice->FMC = 3;
+ evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+ evoice->EC = 0;
+ evoice->Alpha = 0;
+ evoice->FMS = 0;
+ evoice->Vol = 0x3ff; /* mute */
+ evoice->RVol = evoice->CVol = 0x7f; /* mute */
+ evoice->Pan = 0x7f; /* mute */
+ evoice->Attribute = 0;
+ snd_trident_write_voice_regs(trident, evoice);
+ evoice->isync2 = 1;
+ evoice->isync_mark = runtime->period_size;
+ evoice->ESO = (runtime->period_size * 2) - 1;
+ }
+
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_hw_params
+
+ Description: Set the hardware parameters for the spdif device.
+
+ Parameters: substream - PCM substream class
+ hw_params - hardware parameters
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ unsigned int old_bits = 0, change = 0;
+ int err;
+
+ err = snd_trident_allocate_pcm_mem(substream, hw_params);
+ if (err < 0)
+ return err;
+
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ err = snd_trident_allocate_evoice(substream, hw_params);
+ if (err < 0)
+ return err;
+ }
+
+ /* prepare SPDIF channel */
+ spin_lock_irq(&trident->reg_lock);
+ old_bits = trident->spdif_pcm_bits;
+ if (old_bits & IEC958_AES0_PROFESSIONAL)
+ trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
+ else
+ trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
+ if (params_rate(hw_params) >= 48000) {
+ trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz
+ trident->spdif_pcm_bits |=
+ trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+ IEC958_AES0_PRO_FS_48000 :
+ (IEC958_AES3_CON_FS_48000 << 24);
+ }
+ else if (params_rate(hw_params) >= 44100) {
+ trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz
+ trident->spdif_pcm_bits |=
+ trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+ IEC958_AES0_PRO_FS_44100 :
+ (IEC958_AES3_CON_FS_44100 << 24);
+ }
+ else {
+ trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz
+ trident->spdif_pcm_bits |=
+ trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+ IEC958_AES0_PRO_FS_32000 :
+ (IEC958_AES3_CON_FS_32000 << 24);
+ }
+ change = old_bits != trident->spdif_pcm_bits;
+ spin_unlock_irq(&trident->reg_lock);
+
+ if (change)
+ snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_prepare
+
+ Description: Prepare SPDIF device for playback.
+
+ Parameters: substream - PCM substream class
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice->extra;
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number];
+ unsigned int RESO, LBAO;
+ unsigned int temp;
+
+ spin_lock_irq(&trident->reg_lock);
+
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+
+ /* set delta (rate) value */
+ voice->Delta = snd_trident_convert_rate(runtime->rate);
+ voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+ /* set Loop Back Address */
+ LBAO = runtime->dma_addr;
+ if (voice->memblk)
+ voice->LBA = voice->memblk->offset;
+ else
+ voice->LBA = LBAO;
+
+ voice->isync = 1;
+ voice->isync3 = 1;
+ voice->isync_mark = runtime->period_size;
+ voice->isync_max = runtime->buffer_size;
+
+ /* set target ESO for channel */
+ RESO = runtime->buffer_size - 1;
+ voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
+
+ /* set ctrl mode */
+ voice->CTRL = snd_trident_control_mode(substream);
+
+ voice->FMC = 3;
+ voice->RVol = 0x7f;
+ voice->CVol = 0x7f;
+ voice->GVSel = 1;
+ voice->Pan = 0x7f;
+ voice->Vol = 0x3ff;
+ voice->EC = 0;
+ voice->CSO = 0;
+ voice->Alpha = 0;
+ voice->FMS = 0;
+ voice->Attribute = 0;
+
+ /* prepare surrogate IRQ channel */
+ snd_trident_write_voice_regs(trident, voice);
+
+ outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO));
+ outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2));
+ outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA));
+ outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO));
+ outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2));
+
+ /* set SPDIF setting */
+ outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+ outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+
+ } else { /* SiS */
+
+ /* set delta (rate) value */
+ voice->Delta = 0x800;
+ voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
+
+ /* set Loop Begin Address */
+ if (voice->memblk)
+ voice->LBA = voice->memblk->offset;
+ else
+ voice->LBA = runtime->dma_addr;
+
+ voice->CSO = 0;
+ voice->ESO = runtime->buffer_size - 1; /* in samples */
+ voice->CTRL = snd_trident_control_mode(substream);
+ voice->FMC = 3;
+ voice->GVSel = 1;
+ voice->EC = 0;
+ voice->Alpha = 0;
+ voice->FMS = 0;
+ voice->Vol = mix->vol;
+ voice->RVol = mix->rvol;
+ voice->CVol = mix->cvol;
+ voice->Pan = mix->pan;
+ voice->Attribute = (1<<(30-16))|(7<<(26-16))|
+ (0<<(24-16))|(0<<(19-16));
+
+ snd_trident_write_voice_regs(trident, voice);
+
+ if (evoice != NULL) {
+ evoice->Delta = voice->Delta;
+ evoice->spurious_threshold = voice->spurious_threshold;
+ evoice->LBA = voice->LBA;
+ evoice->CSO = 0;
+ evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
+ evoice->CTRL = voice->CTRL;
+ evoice->FMC = 3;
+ evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+ evoice->EC = 0;
+ evoice->Alpha = 0;
+ evoice->FMS = 0;
+ evoice->Vol = 0x3ff; /* mute */
+ evoice->RVol = evoice->CVol = 0x7f; /* mute */
+ evoice->Pan = 0x7f; /* mute */
+ evoice->Attribute = 0;
+ snd_trident_write_voice_regs(trident, evoice);
+ evoice->isync2 = 1;
+ evoice->isync_mark = runtime->period_size;
+ evoice->ESO = (runtime->period_size * 2) - 1;
+ }
+
+ outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
+ temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+ temp &= ~(1<<19);
+ outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR));
+ temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ temp |= SPDIF_EN;
+ outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ }
+
+ spin_unlock_irq(&trident->reg_lock);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_trigger
+
+ Description: Start/stop devices
+
+ Parameters: substream - PCM substream class
+ cmd - trigger command (STOP, GO)
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+ unsigned int what, whati, capture_flag, spdif_flag;
+ snd_trident_voice_t *voice, *evoice;
+ unsigned int val, go;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ go = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ go = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ what = whati = capture_flag = spdif_flag = 0;
+ spin_lock(&trident->reg_lock);
+ val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if ((trident_t *) snd_pcm_substream_chip(s) == trident) {
+ voice = (snd_trident_voice_t *) s->runtime->private_data;
+ evoice = voice->extra;
+ what |= 1 << (voice->number & 0x1f);
+ if (evoice == NULL) {
+ whati |= 1 << (voice->number & 0x1f);
+ } else {
+ what |= 1 << (evoice->number & 0x1f);
+ whati |= 1 << (evoice->number & 0x1f);
+ if (go)
+ evoice->stimer = val;
+ }
+ if (go) {
+ voice->running = 1;
+ voice->stimer = val;
+ } else {
+ voice->running = 0;
+ }
+ snd_pcm_trigger_done(s, substream);
+ if (voice->capture)
+ capture_flag = 1;
+ if (voice->spdif)
+ spdif_flag = 1;
+ }
+ }
+ if (spdif_flag) {
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+ outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+ } else {
+ outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
+ val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
+ outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ }
+ }
+ if (!go)
+ outl(what, TRID_REG(trident, T4D_STOP_B));
+ val = inl(TRID_REG(trident, T4D_AINTEN_B));
+ if (go) {
+ val |= whati;
+ } else {
+ val &= ~whati;
+ }
+ outl(val, TRID_REG(trident, T4D_AINTEN_B));
+ if (go) {
+ outl(what, TRID_REG(trident, T4D_START_B));
+
+ if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
+ outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
+ } else {
+ if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
+ outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
+ }
+ spin_unlock(&trident->reg_lock);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_pointer
+
+ Description: This routine return the playback position
+
+ Parameters: substream - PCM substream class
+
+ Returns: position of buffer
+
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_playback_pointer(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ unsigned int cso;
+
+ if (!voice->running)
+ return 0;
+
+ spin_lock(&trident->reg_lock);
+
+ outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+
+ if (trident->device != TRIDENT_DEVICE_ID_NX) {
+ cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
+ } else { // ID_4DWAVE_NX
+ cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
+ }
+
+ spin_unlock(&trident->reg_lock);
+
+ if (cso >= runtime->buffer_size)
+ cso = 0;
+
+ return cso;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_pointer
+
+ Description: This routine return the capture position
+
+ Paramters: pcm1 - PCM device class
+
+ Returns: position of buffer
+
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_capture_pointer(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ unsigned int result;
+
+ if (!voice->running)
+ return 0;
+
+ result = inw(TRID_REG(trident, T4D_SBBL_SBCL));
+ if (runtime->channels > 1)
+ result >>= 1;
+ if (result > 0)
+ result = runtime->buffer_size - result;
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_pointer
+
+ Description: This routine return the SPDIF playback position
+
+ Parameters: substream - PCM substream class
+
+ Returns: position of buffer
+
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_spdif_pointer(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ unsigned int result;
+
+ if (!voice->running)
+ return 0;
+
+ result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
+
+ return result;
+}
+
+/*
+ * Playback support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (256*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (256*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/*
+ * Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .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,
+};
+
+/*
+ * Foldback capture support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_foldback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .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,
+};
+
+/*
+ * SPDIF playback support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_spdif =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .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,
+};
+
+static snd_pcm_hardware_t snd_trident_spdif_7018 =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .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,
+};
+
+static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ trident_t *trident;
+
+ if (voice) {
+ trident = voice->trident;
+ snd_trident_free_voice(trident, voice);
+ }
+}
+
+static int snd_trident_playback_open(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice;
+
+ voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+ if (voice == NULL)
+ return -EAGAIN;
+ snd_trident_pcm_mixer_build(trident, voice, substream);
+ voice->substream = substream;
+ runtime->private_data = voice;
+ runtime->private_free = snd_trident_pcm_free_substream;
+ runtime->hw = snd_trident_playback;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_close
+
+ Description: This routine will close the 4DWave playback device. For now
+ we will simply free the dma transfer buffer.
+
+ Parameters: substream - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+static int snd_trident_playback_close(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+
+ snd_trident_pcm_mixer_free(trident, voice, substream);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_open
+
+ Description: This routine will open the 4DWave SPDIF device.
+
+ Parameters: substream - PCM substream class
+
+ Returns: status - success or failure flag
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_open(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_trident_voice_t *voice;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+ if (voice == NULL)
+ return -EAGAIN;
+ voice->spdif = 1;
+ voice->substream = substream;
+ spin_lock_irq(&trident->reg_lock);
+ trident->spdif_pcm_bits = trident->spdif_bits;
+ spin_unlock_irq(&trident->reg_lock);
+
+ runtime->private_data = voice;
+ runtime->private_free = snd_trident_pcm_free_substream;
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ runtime->hw = snd_trident_spdif;
+ } else {
+ runtime->hw = snd_trident_spdif_7018;
+ }
+
+ trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
+
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+ return 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_close
+
+ Description: This routine will close the 4DWave SPDIF device.
+
+ Parameters: substream - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_close(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ unsigned int temp;
+
+ spin_lock_irq(&trident->reg_lock);
+ // restore default SPDIF setting
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+ outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+ } else {
+ outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+ temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ if (trident->spdif_ctrl) {
+ temp |= SPDIF_EN;
+ } else {
+ temp &= ~SPDIF_EN;
+ }
+ outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ }
+ spin_unlock_irq(&trident->reg_lock);
+ trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_open
+
+ Description: This routine will open the 4DWave capture device.
+
+ Parameters: substream - PCM substream class
+
+ Returns: status - success or failure flag
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_open(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_trident_voice_t *voice;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+ if (voice == NULL)
+ return -EAGAIN;
+ voice->capture = 1;
+ voice->substream = substream;
+ runtime->private_data = voice;
+ runtime->private_free = snd_trident_pcm_free_substream;
+ runtime->hw = snd_trident_capture;
+ snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_close
+
+ Description: This routine will close the 4DWave capture device. For now
+ we will simply free the dma transfer buffer.
+
+ Parameters: substream - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+static int snd_trident_capture_close(snd_pcm_substream_t * substream)
+{
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_open
+
+ Description: This routine will open the 4DWave foldback capture device.
+
+ Parameters: substream - PCM substream class
+
+ Returns: status - success or failure flag
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_open(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_trident_voice_t *voice;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+ if (voice == NULL)
+ return -EAGAIN;
+ voice->foldback_chan = substream->number;
+ voice->substream = substream;
+ runtime->private_data = voice;
+ runtime->private_free = snd_trident_pcm_free_substream;
+ runtime->hw = snd_trident_foldback;
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_close
+
+ Description: This routine will close the 4DWave foldback capture device.
+ For now we will simply free the dma transfer buffer.
+
+ Parameters: substream - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+static int snd_trident_foldback_close(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_trident_voice_t *voice;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ voice = (snd_trident_voice_t *) runtime->private_data;
+
+ /* stop capture channel */
+ spin_lock_irq(&trident->reg_lock);
+ outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ PCM operations
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_ops_t snd_trident_playback_ops = {
+ .open = snd_trident_playback_open,
+ .close = snd_trident_playback_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_hw_params,
+ .hw_free = snd_trident_hw_free,
+ .prepare = snd_trident_playback_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_nx_playback_ops = {
+ .open = snd_trident_playback_open,
+ .close = snd_trident_playback_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_hw_params,
+ .hw_free = snd_trident_hw_free,
+ .prepare = snd_trident_playback_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_playback_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static snd_pcm_ops_t snd_trident_capture_ops = {
+ .open = snd_trident_capture_open,
+ .close = snd_trident_capture_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_capture_hw_params,
+ .hw_free = snd_trident_hw_free,
+ .prepare = snd_trident_capture_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_capture_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_si7018_capture_ops = {
+ .open = snd_trident_capture_open,
+ .close = snd_trident_capture_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_si7018_capture_hw_params,
+ .hw_free = snd_trident_si7018_capture_hw_free,
+ .prepare = snd_trident_si7018_capture_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_foldback_ops = {
+ .open = snd_trident_foldback_open,
+ .close = snd_trident_foldback_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_hw_params,
+ .hw_free = snd_trident_hw_free,
+ .prepare = snd_trident_foldback_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_nx_foldback_ops = {
+ .open = snd_trident_foldback_open,
+ .close = snd_trident_foldback_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_hw_params,
+ .hw_free = snd_trident_hw_free,
+ .prepare = snd_trident_foldback_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_playback_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static snd_pcm_ops_t snd_trident_spdif_ops = {
+ .open = snd_trident_spdif_open,
+ .close = snd_trident_spdif_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_spdif_hw_params,
+ .hw_free = snd_trident_hw_free,
+ .prepare = snd_trident_spdif_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_spdif_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_spdif_7018_ops = {
+ .open = snd_trident_spdif_open,
+ .close = snd_trident_spdif_close,
+ .ioctl = snd_trident_ioctl,
+ .hw_params = snd_trident_spdif_hw_params,
+ .hw_free = snd_trident_hw_free,
+ .prepare = snd_trident_spdif_prepare,
+ .trigger = snd_trident_trigger,
+ .pointer = snd_trident_playback_pointer,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_free
+
+ Description: This routine release the 4DWave private data.
+
+ Paramters: private_data - pointer to 4DWave device info.
+
+ Returns: None
+
+ ---------------------------------------------------------------------------*/
+static void snd_trident_pcm_free(snd_pcm_t *pcm)
+{
+ trident_t *trident = pcm->private_data;
+ trident->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm)
+{
+ trident_t *trident = pcm->private_data;
+ trident->foldback = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm)
+{
+ trident_t *trident = pcm->private_data;
+ trident->spdif = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm
+
+ Description: This routine registers the 4DWave device for PCM support.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+
+ Returns: None
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0)
+ return err;
+
+ pcm->private_data = trident;
+ pcm->private_free = snd_trident_pcm_free;
+
+ if (trident->tlb.entries) {
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops);
+ } else {
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops);
+ }
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ trident->device != TRIDENT_DEVICE_ID_SI7018 ?
+ &snd_trident_capture_ops :
+ &snd_trident_si7018_capture_ops);
+
+ pcm->info_flags = 0;
+ pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+ strcpy(pcm->name, "Trident 4DWave");
+ trident->pcm = pcm;
+
+ if (trident->tlb.entries) {
+ snd_pcm_substream_t *substream;
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(trident->pci),
+ 64*1024, 128*1024);
+ snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+ 64*1024, 128*1024);
+ } else {
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+ }
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_pcm
+
+ Description: This routine registers the 4DWave device for foldback PCM support.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+
+ Returns: None
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *foldback;
+ int err;
+ int num_chan = 3;
+ snd_pcm_substream_t *substream;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if (trident->device == TRIDENT_DEVICE_ID_NX)
+ num_chan = 4;
+ if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0)
+ return err;
+
+ foldback->private_data = trident;
+ foldback->private_free = snd_trident_foldback_pcm_free;
+ if (trident->tlb.entries)
+ snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops);
+ else
+ snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops);
+ foldback->info_flags = 0;
+ strcpy(foldback->name, "Trident 4DWave");
+ substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ strcpy(substream->name, "Front Mixer");
+ substream = substream->next;
+ strcpy(substream->name, "Reverb Mixer");
+ substream = substream->next;
+ strcpy(substream->name, "Chorus Mixer");
+ if (num_chan == 4) {
+ substream = substream->next;
+ strcpy(substream->name, "Second AC'97 ADC");
+ }
+ trident->foldback = foldback;
+
+ if (trident->tlb.entries)
+ snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(trident->pci), 0, 128*1024);
+ else
+ snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = foldback;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif
+
+ Description: This routine registers the 4DWave-NX device for SPDIF support.
+
+ Paramters: trident - pointer to target device class for 4DWave-NX.
+
+ Returns: None
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *spdif;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0)
+ return err;
+
+ spdif->private_data = trident;
+ spdif->private_free = snd_trident_spdif_pcm_free;
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
+ } else {
+ snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops);
+ }
+ spdif->info_flags = 0;
+ strcpy(spdif->name, "Trident 4DWave IEC958");
+ trident->spdif = spdif;
+
+ snd_pcm_lib_preallocate_pages_for_all(spdif, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = spdif;
+ return 0;
+}
+
+/*
+ * Mixer part
+ */
+
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_control
+
+ Description: enable/disable S/PDIF out from ac97 mixer
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_trident_spdif_control_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+
+ spin_lock_irq(&trident->reg_lock);
+ val = trident->spdif_ctrl;
+ ucontrol->value.integer.value[0] = val == kcontrol->private_value;
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+static int snd_trident_spdif_control_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int change;
+
+ val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00;
+ spin_lock_irq(&trident->reg_lock);
+ /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */
+ change = trident->spdif_ctrl != val;
+ trident->spdif_ctrl = val;
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) {
+ outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+ outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+ }
+ } else {
+ if (trident->spdif == NULL) {
+ unsigned int temp;
+ outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+ temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN;
+ if (val)
+ temp |= SPDIF_EN;
+ outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ }
+ }
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+ .info = snd_trident_spdif_control_info,
+ .get = snd_trident_spdif_control_get,
+ .put = snd_trident_spdif_control_put,
+ .private_value = 0x28,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_default
+
+ Description: put/get the S/PDIF default settings
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_trident_spdif_default_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&trident->reg_lock);
+ ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff;
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+static int snd_trident_spdif_default_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = (ucontrol->value.iec958.status[0] << 0) |
+ (ucontrol->value.iec958.status[1] << 8) |
+ (ucontrol->value.iec958.status[2] << 16) |
+ (ucontrol->value.iec958.status[3] << 24);
+ spin_lock_irq(&trident->reg_lock);
+ change = trident->spdif_bits != val;
+ trident->spdif_bits = val;
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0)
+ outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+ } else {
+ if (trident->spdif == NULL)
+ outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+ }
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_default __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_trident_spdif_default_info,
+ .get = snd_trident_spdif_default_get,
+ .put = snd_trident_spdif_default_put
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_mask
+
+ Description: put/get the S/PDIF mask
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_trident_spdif_mask_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * 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 snd_kcontrol_new_t snd_trident_spdif_mask __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+ .info = snd_trident_spdif_mask_info,
+ .get = snd_trident_spdif_mask_get,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_stream
+
+ Description: put/get the S/PDIF stream settings
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_trident_spdif_stream_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&trident->reg_lock);
+ ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff;
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+static int snd_trident_spdif_stream_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = (ucontrol->value.iec958.status[0] << 0) |
+ (ucontrol->value.iec958.status[1] << 8) |
+ (ucontrol->value.iec958.status[2] << 16) |
+ (ucontrol->value.iec958.status[3] << 24);
+ spin_lock_irq(&trident->reg_lock);
+ change = trident->spdif_pcm_bits != val;
+ trident->spdif_pcm_bits = val;
+ if (trident->spdif != NULL) {
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+ } else {
+ outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+ }
+ }
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_stream __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_trident_spdif_stream_info,
+ .get = snd_trident_spdif_stream_get,
+ .put = snd_trident_spdif_stream_put
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_ac97_control
+
+ Description: enable/disable rear path for ac97
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_ac97_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_trident_ac97_control_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+
+ spin_lock_irq(&trident->reg_lock);
+ val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+ ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0;
+ spin_unlock_irq(&trident->reg_lock);
+ return 0;
+}
+
+static int snd_trident_ac97_control_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned char val;
+ int change = 0;
+
+ spin_lock_irq(&trident->reg_lock);
+ val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+ val &= ~(1 << kcontrol->private_value);
+ if (ucontrol->value.integer.value[0])
+ val |= 1 << kcontrol->private_value;
+ change = val != trident->ac97_ctrl;
+ trident->ac97_ctrl = val;
+ outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_ac97_rear_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Rear Path",
+ .info = snd_trident_ac97_control_info,
+ .get = snd_trident_ac97_control_get,
+ .put = snd_trident_ac97_control_put,
+ .private_value = 4,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_vol_control
+
+ Description: wave & music volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_trident_vol_control_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+
+ val = trident->musicvol_wavevol;
+ ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff);
+ ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff);
+ return 0;
+}
+
+static int snd_trident_vol_control_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ spin_lock_irq(&trident->reg_lock);
+ val = trident->musicvol_wavevol;
+ val &= ~(0xffff << kcontrol->private_value);
+ val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) |
+ ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value;
+ change = val != trident->musicvol_wavevol;
+ outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_vol_music_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Music Playback Volume",
+ .info = snd_trident_vol_control_info,
+ .get = snd_trident_vol_control_get,
+ .put = snd_trident_vol_control_put,
+ .private_value = 16,
+};
+
+static snd_kcontrol_new_t snd_trident_vol_wave_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Wave Playback Volume",
+ .info = snd_trident_vol_control_info,
+ .get = snd_trident_vol_control_get,
+ .put = snd_trident_vol_control_put,
+ .private_value = 0,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_vol_control
+
+ Description: PCM front volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+ uinfo->value.integer.max = 1023;
+ return 0;
+}
+
+static int snd_trident_pcm_vol_control_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ ucontrol->value.integer.value[0] = 1023 - mix->vol;
+ } else {
+ ucontrol->value.integer.value[0] = 255 - (mix->vol>>2);
+ }
+ return 0;
+}
+
+static int snd_trident_pcm_vol_control_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+ unsigned int val;
+ int change = 0;
+
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ val = 1023 - (ucontrol->value.integer.value[0] & 1023);
+ } else {
+ val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2;
+ }
+ spin_lock_irq(&trident->reg_lock);
+ change = val != mix->vol;
+ mix->vol = val;
+ if (mix->voice != NULL)
+ snd_trident_write_vol_reg(trident, mix->voice, val);
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_vol_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Front Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .count = 32,
+ .info = snd_trident_pcm_vol_control_info,
+ .get = snd_trident_pcm_vol_control_get,
+ .put = snd_trident_pcm_vol_control_put,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_pan_control
+
+ Description: PCM front pan control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_pan_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 127;
+ return 0;
+}
+
+static int snd_trident_pcm_pan_control_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+ ucontrol->value.integer.value[0] = mix->pan;
+ if (ucontrol->value.integer.value[0] & 0x40) {
+ ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f));
+ } else {
+ ucontrol->value.integer.value[0] |= 0x40;
+ }
+ return 0;
+}
+
+static int snd_trident_pcm_pan_control_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+ unsigned char val;
+ int change = 0;
+
+ if (ucontrol->value.integer.value[0] & 0x40)
+ val = ucontrol->value.integer.value[0] & 0x3f;
+ else
+ val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40;
+ spin_lock_irq(&trident->reg_lock);
+ change = val != mix->pan;
+ mix->pan = val;
+ if (mix->voice != NULL)
+ snd_trident_write_pan_reg(trident, mix->voice, val);
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_pan_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Pan Playback Control",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .count = 32,
+ .info = snd_trident_pcm_pan_control_info,
+ .get = snd_trident_pcm_pan_control_get,
+ .put = snd_trident_pcm_pan_control_put,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_rvol_control
+
+ Description: PCM reverb volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_rvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 127;
+ return 0;
+}
+
+static int snd_trident_pcm_rvol_control_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+ ucontrol->value.integer.value[0] = 127 - mix->rvol;
+ return 0;
+}
+
+static int snd_trident_pcm_rvol_control_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+ unsigned short val;
+ int change = 0;
+
+ val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
+ spin_lock_irq(&trident->reg_lock);
+ change = val != mix->rvol;
+ mix->rvol = val;
+ if (mix->voice != NULL)
+ snd_trident_write_rvol_reg(trident, mix->voice, val);
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_rvol_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Reverb Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .count = 32,
+ .info = snd_trident_pcm_rvol_control_info,
+ .get = snd_trident_pcm_rvol_control_get,
+ .put = snd_trident_pcm_rvol_control_put,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_cvol_control
+
+ Description: PCM chorus volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_cvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 127;
+ return 0;
+}
+
+static int snd_trident_pcm_cvol_control_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+ ucontrol->value.integer.value[0] = 127 - mix->cvol;
+ return 0;
+}
+
+static int snd_trident_pcm_cvol_control_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ trident_t *trident = snd_kcontrol_chip(kcontrol);
+ snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+ unsigned short val;
+ int change = 0;
+
+ val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
+ spin_lock_irq(&trident->reg_lock);
+ change = val != mix->cvol;
+ mix->cvol = val;
+ if (mix->voice != NULL)
+ snd_trident_write_cvol_reg(trident, mix->voice, val);
+ spin_unlock_irq(&trident->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_cvol_control __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Chorus Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .count = 32,
+ .info = snd_trident_pcm_cvol_control_info,
+ .get = snd_trident_pcm_cvol_control_get,
+ .put = snd_trident_pcm_cvol_control_put,
+};
+
+static void snd_trident_notify_pcm_change1(snd_card_t * card, snd_kcontrol_t *kctl, int num, int activate)
+{
+ snd_ctl_elem_id_t id;
+
+ snd_runtime_check(kctl != NULL, return);
+ if (activate)
+ kctl->vd[num].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ else
+ kctl->vd[num].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ snd_ctl_build_ioff(&id, kctl, num));
+}
+
+static void snd_trident_notify_pcm_change(trident_t *trident, snd_trident_pcm_mixer_t *tmix, int num, int activate)
+{
+ snd_trident_notify_pcm_change1(trident->card, trident->ctl_vol, num, activate);
+ snd_trident_notify_pcm_change1(trident->card, trident->ctl_pan, num, activate);
+ snd_trident_notify_pcm_change1(trident->card, trident->ctl_rvol, num, activate);
+ snd_trident_notify_pcm_change1(trident->card, trident->ctl_cvol, num, activate);
+}
+
+static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream)
+{
+ snd_trident_pcm_mixer_t *tmix;
+
+ snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL);
+ tmix = &trident->pcm_mixer[substream->number];
+ tmix->voice = voice;
+ tmix->vol = T4D_DEFAULT_PCM_VOL;
+ tmix->pan = T4D_DEFAULT_PCM_PAN;
+ tmix->rvol = T4D_DEFAULT_PCM_RVOL;
+ tmix->cvol = T4D_DEFAULT_PCM_CVOL;
+ snd_trident_notify_pcm_change(trident, tmix, substream->number, 1);
+ return 0;
+}
+
+static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream)
+{
+ snd_trident_pcm_mixer_t *tmix;
+
+ snd_assert(trident != NULL && substream != NULL, return -EINVAL);
+ tmix = &trident->pcm_mixer[substream->number];
+ tmix->voice = NULL;
+ snd_trident_notify_pcm_change(trident, tmix, substream->number, 0);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_mixer
+
+ Description: This routine registers the 4DWave device for mixer support.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+
+ Returns: None
+
+ ---------------------------------------------------------------------------*/
+
+static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device)
+{
+ ac97_template_t _ac97;
+ snd_card_t * card = trident->card;
+ snd_kcontrol_t *kctl;
+ snd_ctl_elem_value_t *uctl;
+ int idx, err, retries = 2;
+ static ac97_bus_ops_t ops = {
+ .write = snd_trident_codec_write,
+ .read = snd_trident_codec_read,
+ };
+
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return -ENOMEM;
+
+ if ((err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus)) < 0)
+ goto __out;
+
+ memset(&_ac97, 0, sizeof(_ac97));
+ _ac97.private_data = trident;
+ trident->ac97_detect = 1;
+
+ __again:
+ if ((err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97)) < 0) {
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ if ((err = snd_trident_sis_reset(trident)) < 0)
+ goto __out;
+ if (retries-- > 0)
+ goto __again;
+ err = -EIO;
+ }
+ goto __out;
+ }
+
+ /* secondary codec? */
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018 &&
+ (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) {
+ _ac97.num = 1;
+ err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec);
+ if (err < 0)
+ snd_printk("SI7018: the secondary codec - invalid access\n");
+#if 0 // only for my testing purpose --jk
+ {
+ ac97_t *mc97;
+ err = snd_ac97_modem(trident->card, &_ac97, &mc97);
+ if (err < 0)
+ snd_printk("snd_ac97_modem returned error %i\n", err);
+ }
+#endif
+ }
+
+ trident->ac97_detect = 0;
+
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0)
+ goto __out;
+ kctl->put(kctl, uctl);
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0)
+ goto __out;
+ kctl->put(kctl, uctl);
+ outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+ } else {
+ outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+ }
+
+ for (idx = 0; idx < 32; idx++) {
+ snd_trident_pcm_mixer_t *tmix;
+
+ tmix = &trident->pcm_mixer[idx];
+ tmix->voice = NULL;
+ }
+ if ((trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL)
+ goto __nomem;
+ if ((err = snd_ctl_add(card, trident->ctl_vol)))
+ goto __out;
+
+ if ((trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL)
+ goto __nomem;
+ if ((err = snd_ctl_add(card, trident->ctl_pan)))
+ goto __out;
+
+ if ((trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL)
+ goto __nomem;
+ if ((err = snd_ctl_add(card, trident->ctl_rvol)))
+ goto __out;
+
+ if ((trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL)
+ goto __nomem;
+ if ((err = snd_ctl_add(card, trident->ctl_cvol)))
+ goto __out;
+
+ if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0)
+ goto __out;
+ kctl->put(kctl, uctl);
+ }
+ if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
+
+ kctl = snd_ctl_new1(&snd_trident_spdif_control, trident);
+ if (kctl == NULL) {
+ err = -ENOMEM;
+ goto __out;
+ }
+ if (trident->ac97->ext_id & AC97_EI_SPDIF)
+ kctl->id.index++;
+ if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF))
+ kctl->id.index++;
+ idx = kctl->id.index;
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ goto __out;
+ kctl->put(kctl, uctl);
+
+ kctl = snd_ctl_new1(&snd_trident_spdif_default, trident);
+ if (kctl == NULL) {
+ err = -ENOMEM;
+ goto __out;
+ }
+ kctl->id.index = idx;
+ kctl->id.device = pcm_spdif_device;
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ goto __out;
+
+ kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident);
+ if (kctl == NULL) {
+ err = -ENOMEM;
+ goto __out;
+ }
+ kctl->id.index = idx;
+ kctl->id.device = pcm_spdif_device;
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ goto __out;
+
+ kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident);
+ if (kctl == NULL) {
+ err = -ENOMEM;
+ goto __out;
+ }
+ kctl->id.index = idx;
+ kctl->id.device = pcm_spdif_device;
+ if ((err = snd_ctl_add(card, kctl)) < 0)
+ goto __out;
+ trident->spdif_pcm_ctl = kctl;
+ }
+
+ err = 0;
+ goto __out;
+
+ __nomem:
+ err = -ENOMEM;
+
+ __out:
+ kfree(uctl);
+
+ return err;
+}
+
+/*
+ * gameport interface
+ */
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+static unsigned char snd_trident_gameport_read(struct gameport *gameport)
+{
+ trident_t *chip = gameport_get_port_data(gameport);
+
+ snd_assert(chip, return 0);
+ return inb(TRID_REG(chip, GAMEPORT_LEGACY));
+}
+
+static void snd_trident_gameport_trigger(struct gameport *gameport)
+{
+ trident_t *chip = gameport_get_port_data(gameport);
+
+ snd_assert(chip, return);
+ outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));
+}
+
+static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ trident_t *chip = gameport_get_port_data(gameport);
+ int i;
+
+ snd_assert(chip, return 0);
+
+ *buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf;
+
+ for (i = 0; i < 4; i++) {
+ axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2));
+ if (axes[i] == 0xffff) axes[i] = -1;
+ }
+
+ return 0;
+}
+
+static int snd_trident_gameport_open(struct gameport *gameport, int mode)
+{
+ trident_t *chip = gameport_get_port_data(gameport);
+
+ snd_assert(chip, return 0);
+
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR));
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 + 20 * HZ / 1000); /* 20msec */
+ return 0;
+ case GAMEPORT_MODE_RAW:
+ outb(0, TRID_REG(chip, GAMEPORT_GCR));
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+int __devinit snd_trident_create_gameport(trident_t *chip)
+{
+ struct gameport *gp;
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "trident: cannot allocate memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "Trident 4DWave");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+
+ gameport_set_port_data(gp, chip);
+ gp->fuzz = 64;
+ gp->read = snd_trident_gameport_read;
+ gp->trigger = snd_trident_gameport_trigger;
+ gp->cooked_read = snd_trident_gameport_cooked_read;
+ gp->open = snd_trident_gameport_open;
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static inline void snd_trident_free_gameport(trident_t *chip)
+{
+ if (chip->gameport) {
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+ }
+}
+#else
+int __devinit snd_trident_create_gameport(trident_t *chip) { return -ENOSYS; }
+static inline void snd_trident_free_gameport(trident_t *chip) { }
+#endif /* CONFIG_GAMEPORT */
+
+/*
+ * delay for 1 tick
+ */
+inline static void do_delay(trident_t *chip)
+{
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+}
+
+/*
+ * SiS reset routine
+ */
+
+static int snd_trident_sis_reset(trident_t *trident)
+{
+ unsigned long end_time;
+ unsigned int i;
+ int r;
+
+ r = trident->in_suspend ? 0 : 2; /* count of retries */
+ __si7018_retry:
+ pci_write_config_byte(trident->pci, 0x46, 0x04); /* SOFTWARE RESET */
+ udelay(100);
+ pci_write_config_byte(trident->pci, 0x46, 0x00);
+ udelay(100);
+ /* disable AC97 GPIO interrupt */
+ outb(0x00, TRID_REG(trident, SI_AC97_GPIO));
+ /* initialize serial interface, force cold reset */
+ i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET;
+ outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ udelay(1000);
+ /* remove cold reset */
+ i &= ~COLD_RESET;
+ outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ udelay(2000);
+ /* wait, until the codec is ready */
+ end_time = (jiffies + (HZ * 3) / 4) + 1;
+ do {
+ if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0)
+ goto __si7018_ok;
+ do_delay(trident);
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
+ if (r-- > 0) {
+ end_time = jiffies + HZ;
+ do {
+ do_delay(trident);
+ } while (time_after_eq(end_time, jiffies));
+ goto __si7018_retry;
+ }
+ __si7018_ok:
+ /* wait for the second codec */
+ do {
+ if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0)
+ break;
+ do_delay(trident);
+ } while (time_after_eq(end_time, jiffies));
+ /* enable 64 channel mode */
+ outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR));
+ return 0;
+}
+
+/*
+ * /proc interface
+ */
+
+static void snd_trident_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ trident_t *trident = entry->private_data;
+ char *s;
+
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_SI7018:
+ s = "SiS 7018 Audio";
+ break;
+ case TRIDENT_DEVICE_ID_DX:
+ s = "Trident 4DWave PCI DX";
+ break;
+ case TRIDENT_DEVICE_ID_NX:
+ s = "Trident 4DWave PCI NX";
+ break;
+ default:
+ s = "???";
+ }
+ snd_iprintf(buffer, "%s\n\n", s);
+ snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count);
+ snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta);
+ if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018)
+ snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off");
+ if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off");
+ if (trident->tlb.entries) {
+ snd_iprintf(buffer,"\nVirtual Memory\n");
+ snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size);
+ snd_iprintf(buffer, "Memory Used : %d\n", trident->tlb.memhdr->used);
+ snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
+ }
+ }
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+ snd_iprintf(buffer,"\nWavetable Synth\n");
+ snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size);
+ snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size);
+ snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size));
+#endif
+}
+
+static void __devinit snd_trident_proc_init(trident_t * trident)
+{
+ snd_info_entry_t *entry;
+ const char *s = "trident";
+
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+ s = "sis7018";
+ if (! snd_card_proc_new(trident->card, s, &entry))
+ snd_info_set_text_ops(entry, trident, 1024, snd_trident_proc_read);
+}
+
+static int snd_trident_dev_free(snd_device_t *device)
+{
+ trident_t *trident = device->device_data;
+ return snd_trident_free(trident);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_tlb_alloc
+
+ Description: Allocate and set up the TLB page table on 4D NX.
+ Each entry has 4 bytes (physical PCI address).
+
+ Paramters: trident - pointer to target device class for 4DWave.
+
+ Returns: 0 or negative error code
+
+ ---------------------------------------------------------------------------*/
+
+static int __devinit snd_trident_tlb_alloc(trident_t *trident)
+{
+ int i;
+
+ /* TLB array must be aligned to 16kB !!! so we allocate
+ 32kB region and correct offset when necessary */
+
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+ 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) {
+ snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n");
+ return -ENOMEM;
+ }
+ trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer.area + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1));
+ trident->tlb.entries_dmaaddr = (trident->tlb.buffer.addr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1);
+ /* allocate shadow TLB page table (virtual addresses) */
+ trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long));
+ if (trident->tlb.shadow_entries == NULL) {
+ snd_printk(KERN_ERR "trident: unable to allocate shadow TLB entries\n");
+ return -ENOMEM;
+ }
+ /* allocate and setup silent page and initialise TLB entries */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+ SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) {
+ snd_printk(KERN_ERR "trident: unable to allocate silent page\n");
+ return -ENOMEM;
+ }
+ memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE);
+ for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) {
+ trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1));
+ trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area;
+ }
+
+ /* use emu memory block manager code to manage tlb page allocation */
+ trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES);
+ if (trident->tlb.memhdr == NULL)
+ return -ENOMEM;
+
+ trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t);
+ return 0;
+}
+
+/*
+ * initialize 4D DX chip
+ */
+
+static void snd_trident_stop_all_voices(trident_t *trident)
+{
+ outl(0xffffffff, TRID_REG(trident, T4D_STOP_A));
+ outl(0xffffffff, TRID_REG(trident, T4D_STOP_B));
+ outl(0, TRID_REG(trident, T4D_AINTEN_A));
+ outl(0, TRID_REG(trident, T4D_AINTEN_B));
+}
+
+static int snd_trident_4d_dx_init(trident_t *trident)
+{
+ struct pci_dev *pci = trident->pci;
+ unsigned long end_time;
+
+ /* reset the legacy configuration and whole audio/wavetable block */
+ pci_write_config_dword(pci, 0x40, 0); /* DDMA */
+ pci_write_config_byte(pci, 0x44, 0); /* ports */
+ pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */
+ pci_write_config_byte(pci, 0x46, 4); /* reset */
+ udelay(100);
+ pci_write_config_byte(pci, 0x46, 0); /* release reset */
+ udelay(100);
+
+ /* warm reset of the AC'97 codec */
+ outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+ udelay(100);
+ outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+ /* DAC on, disable SB IRQ and try to force ADC valid signal */
+ trident->ac97_ctrl = 0x0000004a;
+ outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+ /* wait, until the codec is ready */
+ end_time = (jiffies + (HZ * 3) / 4) + 1;
+ do {
+ if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0)
+ goto __dx_ok;
+ do_delay(trident);
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk(KERN_ERR "AC'97 codec ready error\n");
+ return -EIO;
+
+ __dx_ok:
+ snd_trident_stop_all_voices(trident);
+
+ return 0;
+}
+
+/*
+ * initialize 4D NX chip
+ */
+static int snd_trident_4d_nx_init(trident_t *trident)
+{
+ struct pci_dev *pci = trident->pci;
+ unsigned long end_time;
+
+ /* reset the legacy configuration and whole audio/wavetable block */
+ pci_write_config_dword(pci, 0x40, 0); /* DDMA */
+ pci_write_config_byte(pci, 0x44, 0); /* ports */
+ pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */
+
+ pci_write_config_byte(pci, 0x46, 1); /* reset */
+ udelay(100);
+ pci_write_config_byte(pci, 0x46, 0); /* release reset */
+ udelay(100);
+
+ /* warm reset of the AC'97 codec */
+ outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+ udelay(100);
+ outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+ /* wait, until the codec is ready */
+ end_time = (jiffies + (HZ * 3) / 4) + 1;
+ do {
+ if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0)
+ goto __nx_ok;
+ do_delay(trident);
+ } while (time_after_eq(end_time, jiffies));
+ snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
+ return -EIO;
+
+ __nx_ok:
+ /* DAC on */
+ trident->ac97_ctrl = 0x00000002;
+ outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+ /* disable SB IRQ */
+ outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT));
+
+ snd_trident_stop_all_voices(trident);
+
+ if (trident->tlb.entries != NULL) {
+ unsigned int i;
+ /* enable virtual addressing via TLB */
+ i = trident->tlb.entries_dmaaddr;
+ i |= 0x00000001;
+ outl(i, TRID_REG(trident, NX_TLBC));
+ } else {
+ outl(0, TRID_REG(trident, NX_TLBC));
+ }
+ /* initialize S/PDIF */
+ outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+ outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+
+ return 0;
+}
+
+/*
+ * initialize sis7018 chip
+ */
+static int snd_trident_sis_init(trident_t *trident)
+{
+ int err;
+
+ if ((err = snd_trident_sis_reset(trident)) < 0)
+ return err;
+
+ snd_trident_stop_all_voices(trident);
+
+ /* initialize S/PDIF */
+ outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_create
+
+ Description: This routine will create the device specific class for
+ the 4DWave card. It will also perform basic initialization.
+
+ Paramters: card - which card to create
+ pci - interface to PCI bus resource info
+ dma1ptr - playback dma buffer
+ dma2ptr - capture dma buffer
+ irqptr - interrupt resource info
+
+ Returns: 4DWave device class private data
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_create(snd_card_t * card,
+ struct pci_dev *pci,
+ int pcm_streams,
+ int pcm_spdif_device,
+ int max_wavetable_size,
+ trident_t ** rtrident)
+{
+ trident_t *trident;
+ int i, err;
+ snd_trident_voice_t *voice;
+ snd_trident_pcm_mixer_t *tmix;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_trident_dev_free,
+ };
+
+ *rtrident = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ /* check, if we can restrict PCI DMA transfers to 30 bits */
+ if (pci_set_dma_mask(pci, 0x3fffffff) < 0 ||
+ pci_set_consistent_dma_mask(pci, 0x3fffffff) < 0) {
+ snd_printk("architecture does not support 30bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ trident = kcalloc(1, sizeof(*trident), GFP_KERNEL);
+ if (trident == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ trident->device = (pci->vendor << 16) | pci->device;
+ trident->card = card;
+ trident->pci = pci;
+ spin_lock_init(&trident->reg_lock);
+ spin_lock_init(&trident->event_lock);
+ spin_lock_init(&trident->voice_alloc);
+ if (pcm_streams < 1)
+ pcm_streams = 1;
+ if (pcm_streams > 32)
+ pcm_streams = 32;
+ trident->ChanPCM = pcm_streams;
+ if (max_wavetable_size < 0 )
+ max_wavetable_size = 0;
+ trident->synth.max_size = max_wavetable_size * 1024;
+ trident->irq = -1;
+
+ trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE);
+ pci_set_master(pci);
+
+ if ((err = pci_request_regions(pci, "Trident Audio")) < 0) {
+ kfree(trident);
+ pci_disable_device(pci);
+ return err;
+ }
+ trident->port = pci_resource_start(pci, 0);
+
+ if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, "Trident Audio", (void *) trident)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_trident_free(trident);
+ return -EBUSY;
+ }
+ trident->irq = pci->irq;
+
+ /* allocate 16k-aligned TLB for NX cards */
+ trident->tlb.entries = NULL;
+ trident->tlb.buffer.area = NULL;
+ if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ if ((err = snd_trident_tlb_alloc(trident)) < 0) {
+ snd_trident_free(trident);
+ return err;
+ }
+ }
+
+ trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+ /* initialize chip */
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX:
+ err = snd_trident_4d_dx_init(trident);
+ break;
+ case TRIDENT_DEVICE_ID_NX:
+ err = snd_trident_4d_nx_init(trident);
+ break;
+ case TRIDENT_DEVICE_ID_SI7018:
+ err = snd_trident_sis_init(trident);
+ break;
+ default:
+ snd_BUG();
+ break;
+ }
+ if (err < 0) {
+ snd_trident_free(trident);
+ return err;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) {
+ snd_trident_free(trident);
+ return err;
+ }
+
+ if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0)
+ return err;
+
+ /* initialise synth voices */
+ for (i = 0; i < 64; i++) {
+ voice = &trident->synth.voices[i];
+ voice->number = i;
+ voice->trident = trident;
+ }
+ /* initialize pcm mixer entries */
+ for (i = 0; i < 32; i++) {
+ tmix = &trident->pcm_mixer[i];
+ tmix->vol = T4D_DEFAULT_PCM_VOL;
+ tmix->pan = T4D_DEFAULT_PCM_PAN;
+ tmix->rvol = T4D_DEFAULT_PCM_RVOL;
+ tmix->cvol = T4D_DEFAULT_PCM_CVOL;
+ }
+
+ snd_trident_enable_eso(trident);
+
+
+ snd_card_set_pm_callback(card, snd_trident_suspend, snd_trident_resume, trident);
+ snd_trident_proc_init(trident);
+ snd_card_set_dev(card, &pci->dev);
+ *rtrident = trident;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_free
+
+ Description: This routine will free the device specific class for
+ the 4DWave card.
+
+ Paramters: trident - device specific private data for 4DWave card
+
+ Returns: None.
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_free(trident_t *trident)
+{
+ snd_trident_free_gameport(trident);
+ snd_trident_disable_eso(trident);
+ // Disable S/PDIF out
+ if (trident->device == TRIDENT_DEVICE_ID_NX)
+ outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+ else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+ }
+ if (trident->tlb.buffer.area) {
+ outl(0, TRID_REG(trident, NX_TLBC));
+ if (trident->tlb.memhdr)
+ snd_util_memhdr_free(trident->tlb.memhdr);
+ if (trident->tlb.silent_page.area)
+ snd_dma_free_pages(&trident->tlb.silent_page);
+ vfree(trident->tlb.shadow_entries);
+ snd_dma_free_pages(&trident->tlb.buffer);
+ }
+ if (trident->irq >= 0)
+ free_irq(trident->irq, (void *)trident);
+ pci_release_regions(trident->pci);
+ pci_disable_device(trident->pci);
+ kfree(trident);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_interrupt
+
+ Description: ISR for Trident 4DWave device
+
+ Paramters: trident - device specific private data for 4DWave card
+
+ Problems: It seems that Trident chips generates interrupts more than
+ one time in special cases. The spurious interrupts are
+ detected via sample timer (T4D_STIMER) and computing
+ corresponding delta value. The limits are detected with
+ the method try & fail so it is possible that it won't
+ work on all computers. [jaroslav]
+
+ Returns: None.
+
+ ---------------------------------------------------------------------------*/
+
+static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ trident_t *trident = dev_id;
+ unsigned int audio_int, chn_int, stimer, channel, mask, tmp;
+ int delta;
+ snd_trident_voice_t *voice;
+
+ audio_int = inl(TRID_REG(trident, T4D_MISCINT));
+ if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0)
+ return IRQ_NONE;
+ if (audio_int & ADDRESS_IRQ) {
+ // get interrupt status for all channels
+ spin_lock(&trident->reg_lock);
+ stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
+ chn_int = inl(TRID_REG(trident, T4D_AINT_A));
+ if (chn_int == 0)
+ goto __skip1;
+ outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */
+ __skip1:
+ chn_int = inl(TRID_REG(trident, T4D_AINT_B));
+ if (chn_int == 0)
+ goto __skip2;
+ for (channel = 63; channel >= 32; channel--) {
+ mask = 1 << (channel&0x1f);
+ if ((chn_int & mask) == 0)
+ continue;
+ voice = &trident->synth.voices[channel];
+ if (!voice->pcm || voice->substream == NULL) {
+ outl(mask, TRID_REG(trident, T4D_STOP_B));
+ continue;
+ }
+ delta = (int)stimer - (int)voice->stimer;
+ if (delta < 0)
+ delta = -delta;
+ if ((unsigned int)delta < voice->spurious_threshold) {
+ /* do some statistics here */
+ trident->spurious_irq_count++;
+ if (trident->spurious_irq_max_delta < (unsigned int)delta)
+ trident->spurious_irq_max_delta = delta;
+ continue;
+ }
+ voice->stimer = stimer;
+ if (voice->isync) {
+ if (!voice->isync3) {
+ tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL));
+ if (trident->bDMAStart & 0x40)
+ tmp >>= 1;
+ if (tmp > 0)
+ tmp = voice->isync_max - tmp;
+ } else {
+ tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
+ }
+ if (tmp < voice->isync_mark) {
+ if (tmp > 0x10)
+ tmp = voice->isync_ESO - 7;
+ else
+ tmp = voice->isync_ESO + 2;
+ /* update ESO for IRQ voice to preserve sync */
+ snd_trident_stop_voice(trident, voice->number);
+ snd_trident_write_eso_reg(trident, voice, tmp);
+ snd_trident_start_voice(trident, voice->number);
+ }
+ } else if (voice->isync2) {
+ voice->isync2 = 0;
+ /* write original ESO and update CSO for IRQ voice to preserve sync */
+ snd_trident_stop_voice(trident, voice->number);
+ snd_trident_write_cso_reg(trident, voice, voice->isync_mark);
+ snd_trident_write_eso_reg(trident, voice, voice->ESO);
+ snd_trident_start_voice(trident, voice->number);
+ }
+#if 0
+ if (voice->extra) {
+ /* update CSO for extra voice to preserve sync */
+ snd_trident_stop_voice(trident, voice->extra->number);
+ snd_trident_write_cso_reg(trident, voice->extra, 0);
+ snd_trident_start_voice(trident, voice->extra->number);
+ }
+#endif
+ spin_unlock(&trident->reg_lock);
+ snd_pcm_period_elapsed(voice->substream);
+ spin_lock(&trident->reg_lock);
+ }
+ outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */
+ __skip2:
+ spin_unlock(&trident->reg_lock);
+ }
+ if (audio_int & MPU401_IRQ) {
+ if (trident->rmidi) {
+ snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data, regs);
+ } else {
+ inb(TRID_REG(trident, T4D_MPUR0));
+ }
+ }
+ // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT));
+ return IRQ_HANDLED;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_attach_synthesizer
+
+ Description: Attach synthesizer hooks
+
+ Paramters: trident - device specific private data for 4DWave card
+
+ Returns: None.
+
+ ---------------------------------------------------------------------------*/
+int snd_trident_attach_synthesizer(trident_t *trident)
+{
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+ if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT,
+ sizeof(trident_t*), &trident->seq_dev) >= 0) {
+ strcpy(trident->seq_dev->name, "4DWave");
+ *(trident_t**)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident;
+ }
+#endif
+ return 0;
+}
+
+snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port)
+{
+ snd_trident_voice_t *pvoice;
+ unsigned long flags;
+ int idx;
+
+ spin_lock_irqsave(&trident->voice_alloc, flags);
+ if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) {
+ idx = snd_trident_allocate_pcm_channel(trident);
+ if(idx < 0) {
+ spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ return NULL;
+ }
+ pvoice = &trident->synth.voices[idx];
+ pvoice->use = 1;
+ pvoice->pcm = 1;
+ pvoice->capture = 0;
+ pvoice->spdif = 0;
+ pvoice->memblk = NULL;
+ pvoice->substream = NULL;
+ spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ return pvoice;
+ }
+ if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) {
+ idx = snd_trident_allocate_synth_channel(trident);
+ if(idx < 0) {
+ spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ return NULL;
+ }
+ pvoice = &trident->synth.voices[idx];
+ pvoice->use = 1;
+ pvoice->synth = 1;
+ pvoice->client = client;
+ pvoice->port = port;
+ pvoice->memblk = NULL;
+ spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ return pvoice;
+ }
+ if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) {
+ }
+ spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ return NULL;
+}
+
+void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice)
+{
+ unsigned long flags;
+ void (*private_free)(snd_trident_voice_t *);
+ void *private_data;
+
+ if (voice == NULL || !voice->use)
+ return;
+ snd_trident_clear_voices(trident, voice->number, voice->number);
+ spin_lock_irqsave(&trident->voice_alloc, flags);
+ private_free = voice->private_free;
+ private_data = voice->private_data;
+ voice->private_free = NULL;
+ voice->private_data = NULL;
+ if (voice->pcm)
+ snd_trident_free_pcm_channel(trident, voice->number);
+ if (voice->synth)
+ snd_trident_free_synth_channel(trident, voice->number);
+ voice->use = voice->pcm = voice->synth = voice->midi = 0;
+ voice->capture = voice->spdif = 0;
+ voice->sample_ops = NULL;
+ voice->substream = NULL;
+ voice->extra = NULL;
+ spin_unlock_irqrestore(&trident->voice_alloc, flags);
+ if (private_free)
+ private_free(voice);
+}
+
+static void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max)
+{
+ unsigned int i, val, mask[2] = { 0, 0 };
+
+ snd_assert(v_min <= 63, return);
+ snd_assert(v_max <= 63, return);
+ for (i = v_min; i <= v_max; i++)
+ mask[i >> 5] |= 1 << (i & 0x1f);
+ if (mask[0]) {
+ outl(mask[0], TRID_REG(trident, T4D_STOP_A));
+ val = inl(TRID_REG(trident, T4D_AINTEN_A));
+ outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A));
+ }
+ if (mask[1]) {
+ outl(mask[1], TRID_REG(trident, T4D_STOP_B));
+ val = inl(TRID_REG(trident, T4D_AINTEN_B));
+ outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B));
+ }
+}
+
+#ifdef CONFIG_PM
+static int snd_trident_suspend(snd_card_t *card, pm_message_t state)
+{
+ trident_t *trident = card->pm_private_data;
+
+ trident->in_suspend = 1;
+ snd_pcm_suspend_all(trident->pcm);
+ if (trident->foldback)
+ snd_pcm_suspend_all(trident->foldback);
+ if (trident->spdif)
+ snd_pcm_suspend_all(trident->spdif);
+
+ snd_ac97_suspend(trident->ac97);
+ if (trident->ac97_sec)
+ snd_ac97_suspend(trident->ac97_sec);
+
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX:
+ case TRIDENT_DEVICE_ID_NX:
+ break; /* TODO */
+ case TRIDENT_DEVICE_ID_SI7018:
+ break;
+ }
+ pci_disable_device(trident->pci);
+ return 0;
+}
+
+static int snd_trident_resume(snd_card_t *card)
+{
+ trident_t *trident = card->pm_private_data;
+
+ pci_enable_device(trident->pci);
+ if (pci_set_dma_mask(trident->pci, 0x3fffffff) < 0 ||
+ pci_set_consistent_dma_mask(trident->pci, 0x3fffffff) < 0)
+ snd_printk(KERN_WARNING "trident: can't set the proper DMA mask\n");
+ pci_set_master(trident->pci); /* to be sure */
+
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX:
+ snd_trident_4d_dx_init(trident);
+ break;
+ case TRIDENT_DEVICE_ID_NX:
+ snd_trident_4d_nx_init(trident);
+ break;
+ case TRIDENT_DEVICE_ID_SI7018:
+ snd_trident_sis_init(trident);
+ break;
+ }
+
+ snd_ac97_resume(trident->ac97);
+ if (trident->ac97_sec)
+ snd_ac97_resume(trident->ac97_sec);
+
+ /* restore some registers */
+ outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+
+ snd_trident_enable_eso(trident);
+
+ trident->in_suspend = 0;
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+EXPORT_SYMBOL(snd_trident_alloc_voice);
+EXPORT_SYMBOL(snd_trident_free_voice);
+EXPORT_SYMBOL(snd_trident_start_voice);
+EXPORT_SYMBOL(snd_trident_stop_voice);
+EXPORT_SYMBOL(snd_trident_write_voice_regs);
+/* trident_memory.c symbols */
+EXPORT_SYMBOL(snd_trident_synth_alloc);
+EXPORT_SYMBOL(snd_trident_synth_free);
+EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
new file mode 100644
index 00000000000..6cc282681e0
--- /dev/null
+++ b/sound/pci/trident/trident_memory.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ * Copyright (c) by Scott McNab <sdm@fractalgraphics.com.au>
+ *
+ * Trident 4DWave-NX memory page allocation (TLB area)
+ * Trident chip can handle only 16MByte of the memory at the same time.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/trident.h>
+
+/* page arguments of these two macros are Trident page (4096 bytes), not like
+ * aligned pages in others
+ */
+#define __set_tlb_bus(trident,page,ptr,addr) \
+ do { (trident)->tlb.entries[page] = cpu_to_le32((addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); \
+ (trident)->tlb.shadow_entries[page] = (ptr); } while (0)
+#define __tlb_to_ptr(trident,page) \
+ (void*)((trident)->tlb.shadow_entries[page])
+#define __tlb_to_addr(trident,page) \
+ (dma_addr_t)le32_to_cpu((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1))
+
+#if PAGE_SIZE == 4096
+/* page size == SNDRV_TRIDENT_PAGE_SIZE */
+#define ALIGN_PAGE_SIZE PAGE_SIZE /* minimum page size for allocation */
+#define MAX_ALIGN_PAGES SNDRV_TRIDENT_MAX_PAGES /* maxmium aligned pages */
+/* fill TLB entrie(s) corresponding to page with ptr */
+#define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr)
+/* fill TLB entrie(s) corresponding to page with silence pointer */
+#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr)
+/* get aligned page from offset address */
+#define get_aligned_page(offset) ((offset) >> 12)
+/* get offset address from aligned page */
+#define aligned_page_offset(page) ((page) << 12)
+/* get buffer address from aligned page */
+#define page_to_ptr(trident,page) __tlb_to_ptr(trident, page)
+/* get PCI physical address from aligned page */
+#define page_to_addr(trident,page) __tlb_to_addr(trident, page)
+
+#elif PAGE_SIZE == 8192
+/* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/
+#define ALIGN_PAGE_SIZE PAGE_SIZE
+#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / 2)
+#define get_aligned_page(offset) ((offset) >> 13)
+#define aligned_page_offset(page) ((page) << 13)
+#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) << 1)
+#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) << 1)
+
+/* fill TLB entries -- we need to fill two entries */
+static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr)
+{
+ page <<= 1;
+ __set_tlb_bus(trident, page, ptr, addr);
+ __set_tlb_bus(trident, page+1, ptr + SNDRV_TRIDENT_PAGE_SIZE, addr + SNDRV_TRIDENT_PAGE_SIZE);
+}
+static inline void set_silent_tlb(trident_t *trident, int page)
+{
+ page <<= 1;
+ __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+ __set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+}
+
+#else
+/* arbitrary size */
+#define UNIT_PAGES (PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE)
+#define ALIGN_PAGE_SIZE (SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES)
+#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES)
+/* Note: if alignment doesn't match to the maximum size, the last few blocks
+ * become unusable. To use such blocks, you'll need to check the validity
+ * of accessing page in set_tlb_bus and set_silent_tlb. search_empty()
+ * should also check it, too.
+ */
+#define get_aligned_page(offset) ((offset) / ALIGN_PAGE_SIZE)
+#define aligned_page_offset(page) ((page) * ALIGN_PAGE_SIZE)
+#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) * UNIT_PAGES)
+#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) * UNIT_PAGES)
+
+/* fill TLB entries -- UNIT_PAGES entries must be filled */
+static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr)
+{
+ int i;
+ page *= UNIT_PAGES;
+ for (i = 0; i < UNIT_PAGES; i++, page++) {
+ __set_tlb_bus(trident, page, ptr, addr);
+ ptr += SNDRV_TRIDENT_PAGE_SIZE;
+ addr += SNDRV_TRIDENT_PAGE_SIZE;
+ }
+}
+static inline void set_silent_tlb(trident_t *trident, int page)
+{
+ int i;
+ page *= UNIT_PAGES;
+ for (i = 0; i < UNIT_PAGES; i++, page++)
+ __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+}
+
+#endif /* PAGE_SIZE */
+
+/* calculate buffer pointer from offset address */
+inline static void *offset_ptr(trident_t *trident, int offset)
+{
+ char *ptr;
+ ptr = page_to_ptr(trident, get_aligned_page(offset));
+ ptr += offset % ALIGN_PAGE_SIZE;
+ return (void*)ptr;
+}
+
+/* first and last (aligned) pages of memory block */
+#define firstpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->first_page)
+#define lastpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->last_page)
+
+/*
+ * search empty pages which may contain given size
+ */
+static snd_util_memblk_t *
+search_empty(snd_util_memhdr_t *hdr, int size)
+{
+ snd_util_memblk_t *blk, *prev;
+ int page, psize;
+ struct list_head *p;
+
+ psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1);
+ prev = NULL;
+ page = 0;
+ list_for_each(p, &hdr->block) {
+ blk = list_entry(p, snd_util_memblk_t, list);
+ if (page + psize <= firstpg(blk))
+ goto __found_pages;
+ page = lastpg(blk) + 1;
+ }
+ if (page + psize > MAX_ALIGN_PAGES)
+ return NULL;
+
+__found_pages:
+ /* create a new memory block */
+ blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev);
+ if (blk == NULL)
+ return NULL;
+ blk->offset = aligned_page_offset(page); /* set aligned offset */
+ firstpg(blk) = page;
+ lastpg(blk) = page + psize - 1;
+ return blk;
+}
+
+
+/*
+ * check if the given pointer is valid for pages
+ */
+static int is_valid_page(unsigned long ptr)
+{
+ if (ptr & ~0x3fffffffUL) {
+ snd_printk("max memory size is 1GB!!\n");
+ return 0;
+ }
+ if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) {
+ snd_printk("page is not aligned\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * page allocation for DMA (Scatter-Gather version)
+ */
+static snd_util_memblk_t *
+snd_trident_alloc_sg_pages(trident_t *trident, snd_pcm_substream_t *substream)
+{
+ snd_util_memhdr_t *hdr;
+ snd_util_memblk_t *blk;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int idx, page;
+ struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+
+ snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+ hdr = trident->tlb.memhdr;
+ snd_assert(hdr != NULL, return NULL);
+
+
+
+ down(&hdr->block_mutex);
+ blk = search_empty(hdr, runtime->dma_bytes);
+ if (blk == NULL) {
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) {
+ snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk));
+ __snd_util_mem_free(hdr, blk);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+
+ /* set TLB entries */
+ idx = 0;
+ for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) {
+ dma_addr_t addr = sgbuf->table[idx].addr;
+ unsigned long ptr = (unsigned long)sgbuf->table[idx].buf;
+ if (! is_valid_page(addr)) {
+ __snd_util_mem_free(hdr, blk);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ set_tlb_bus(trident, page, ptr, addr);
+ }
+ up(&hdr->block_mutex);
+ return blk;
+}
+
+/*
+ * page allocation for DMA (contiguous version)
+ */
+static snd_util_memblk_t *
+snd_trident_alloc_cont_pages(trident_t *trident, snd_pcm_substream_t *substream)
+{
+ snd_util_memhdr_t *hdr;
+ snd_util_memblk_t *blk;
+ int page;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ dma_addr_t addr;
+ unsigned long ptr;
+
+ snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+ hdr = trident->tlb.memhdr;
+ snd_assert(hdr != NULL, return NULL);
+
+ down(&hdr->block_mutex);
+ blk = search_empty(hdr, runtime->dma_bytes);
+ if (blk == NULL) {
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+
+ /* set TLB entries */
+ addr = runtime->dma_addr;
+ ptr = (unsigned long)runtime->dma_area;
+ for (page = firstpg(blk); page <= lastpg(blk); page++,
+ ptr += SNDRV_TRIDENT_PAGE_SIZE, addr += SNDRV_TRIDENT_PAGE_SIZE) {
+ if (! is_valid_page(addr)) {
+ __snd_util_mem_free(hdr, blk);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ set_tlb_bus(trident, page, ptr, addr);
+ }
+ up(&hdr->block_mutex);
+ return blk;
+}
+
+/*
+ * page allocation for DMA
+ */
+snd_util_memblk_t *
+snd_trident_alloc_pages(trident_t *trident, snd_pcm_substream_t *substream)
+{
+ snd_assert(trident != NULL, return NULL);
+ snd_assert(substream != NULL, return NULL);
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG)
+ return snd_trident_alloc_sg_pages(trident, substream);
+ else
+ return snd_trident_alloc_cont_pages(trident, substream);
+}
+
+
+/*
+ * release DMA buffer from page table
+ */
+int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk)
+{
+ snd_util_memhdr_t *hdr;
+ int page;
+
+ snd_assert(trident != NULL, return -EINVAL);
+ snd_assert(blk != NULL, return -EINVAL);
+
+ hdr = trident->tlb.memhdr;
+ down(&hdr->block_mutex);
+ /* reset TLB entries */
+ for (page = firstpg(blk); page <= lastpg(blk); page++)
+ set_silent_tlb(trident, page);
+ /* free memory block */
+ __snd_util_mem_free(hdr, blk);
+ up(&hdr->block_mutex);
+ return 0;
+}
+
+
+/*----------------------------------------------------------------
+ * memory allocation using multiple pages (for synth)
+ *----------------------------------------------------------------
+ * Unlike the DMA allocation above, non-contiguous pages are
+ * assigned to TLB.
+ *----------------------------------------------------------------*/
+
+/*
+ */
+static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk);
+static int synth_free_pages(trident_t *hw, snd_util_memblk_t *blk);
+
+/*
+ * allocate a synth sample area
+ */
+snd_util_memblk_t *
+snd_trident_synth_alloc(trident_t *hw, unsigned int size)
+{
+ snd_util_memblk_t *blk;
+ snd_util_memhdr_t *hdr = hw->tlb.memhdr;
+
+ down(&hdr->block_mutex);
+ blk = __snd_util_mem_alloc(hdr, size);
+ if (blk == NULL) {
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ if (synth_alloc_pages(hw, blk)) {
+ __snd_util_mem_free(hdr, blk);
+ up(&hdr->block_mutex);
+ return NULL;
+ }
+ up(&hdr->block_mutex);
+ return blk;
+}
+
+
+/*
+ * free a synth sample area
+ */
+int
+snd_trident_synth_free(trident_t *hw, snd_util_memblk_t *blk)
+{
+ snd_util_memhdr_t *hdr = hw->tlb.memhdr;
+
+ down(&hdr->block_mutex);
+ synth_free_pages(hw, blk);
+ __snd_util_mem_free(hdr, blk);
+ up(&hdr->block_mutex);
+ return 0;
+}
+
+
+/*
+ * reset TLB entry and free kernel page
+ */
+static void clear_tlb(trident_t *trident, int page)
+{
+ void *ptr = page_to_ptr(trident, page);
+ dma_addr_t addr = page_to_addr(trident, page);
+ set_silent_tlb(trident, page);
+ if (ptr) {
+ struct snd_dma_buffer dmab;
+ dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+ dmab.dev.dev = snd_dma_pci_data(trident->pci);
+ dmab.area = ptr;
+ dmab.addr = addr;
+ dmab.bytes = ALIGN_PAGE_SIZE;
+ snd_dma_free_pages(&dmab);
+ }
+}
+
+/* check new allocation range */
+static void get_single_page_range(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk, int *first_page_ret, int *last_page_ret)
+{
+ struct list_head *p;
+ snd_util_memblk_t *q;
+ int first_page, last_page;
+ first_page = firstpg(blk);
+ if ((p = blk->list.prev) != &hdr->block) {
+ q = list_entry(p, snd_util_memblk_t, list);
+ if (lastpg(q) == first_page)
+ first_page++; /* first page was already allocated */
+ }
+ last_page = lastpg(blk);
+ if ((p = blk->list.next) != &hdr->block) {
+ q = list_entry(p, snd_util_memblk_t, list);
+ if (firstpg(q) == last_page)
+ last_page--; /* last page was already allocated */
+ }
+ *first_page_ret = first_page;
+ *last_page_ret = last_page;
+}
+
+/*
+ * allocate kernel pages and assign them to TLB
+ */
+static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk)
+{
+ int page, first_page, last_page;
+ struct snd_dma_buffer dmab;
+
+ firstpg(blk) = get_aligned_page(blk->offset);
+ lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1);
+ get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page);
+
+ /* allocate a kernel page for each Trident page -
+ * fortunately Trident page size and kernel PAGE_SIZE is identical!
+ */
+ for (page = first_page; page <= last_page; page++) {
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(hw->pci),
+ ALIGN_PAGE_SIZE, &dmab) < 0)
+ goto __fail;
+ if (! is_valid_page(dmab.addr)) {
+ snd_dma_free_pages(&dmab);
+ goto __fail;
+ }
+ set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr);
+ }
+ return 0;
+
+__fail:
+ /* release allocated pages */
+ last_page = page - 1;
+ for (page = first_page; page <= last_page; page++)
+ clear_tlb(hw, page);
+
+ return -ENOMEM;
+}
+
+/*
+ * free pages
+ */
+static int synth_free_pages(trident_t *trident, snd_util_memblk_t *blk)
+{
+ int page, first_page, last_page;
+
+ get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page);
+ for (page = first_page; page <= last_page; page++)
+ clear_tlb(trident, page);
+
+ return 0;
+}
+
+/*
+ * copy_from_user(blk + offset, data, size)
+ */
+int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char __user *data, int size)
+{
+ int page, nextofs, end_offset, temp, temp1;
+
+ offset += blk->offset;
+ end_offset = offset + size;
+ page = get_aligned_page(offset) + 1;
+ do {
+ nextofs = aligned_page_offset(page);
+ temp = nextofs - offset;
+ temp1 = end_offset - offset;
+ if (temp1 < temp)
+ temp = temp1;
+ if (copy_from_user(offset_ptr(trident, offset), data, temp))
+ return -EFAULT;
+ offset = nextofs;
+ data += temp;
+ page++;
+ } while (offset < end_offset);
+ return 0;
+}
+
diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c
new file mode 100644
index 00000000000..5d5a719b058
--- /dev/null
+++ b/sound/pci/trident/trident_synth.c
@@ -0,0 +1,1031 @@
+/*
+ * Routines for Trident 4DWave NX/DX soundcards - Synthesizer
+ * Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/trident.h>
+#include <sound/seq_device.h>
+
+MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>");
+MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
+MODULE_LICENSE("GPL");
+
+/* linear to log pan conversion table (4.2 channel attenuation format) */
+static unsigned int pan_table[63] = {
+ 7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507,
+ 6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168,
+ 5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105,
+ 3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261,
+ 3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590,
+ 2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057,
+ 1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634,
+ 1588, 1543, 1499, 1456, 1415, 1375, 1336
+};
+
+#define LOG_TABLE_SIZE 386
+
+/* Linear half-attenuation to log conversion table in the format:
+ * {linear volume, logarithmic attenuation equivalent}, ...
+ *
+ * Provides conversion from a linear half-volume value in the range
+ * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
+ * Halving the linear volume is equivalent to an additional 6dB of
+ * logarithmic attenuation. The algorithm used in log_from_linear()
+ * therefore uses this table as follows:
+ *
+ * - loop and for every time the volume is less than half the maximum
+ * volume (16384), add another 6dB and halve the maximum value used
+ * for this comparison.
+ * - when the volume is greater than half the maximum volume, take
+ * the difference of the volume to half volume (in the range [0,8192])
+ * and look up the log_table[] to find the nearest entry.
+ * - take the logarithic component of this entry and add it to the
+ * resulting attenuation.
+ *
+ * Thus this routine provides a linear->log conversion for a range of
+ * [0,16384] using only 386 table entries
+ *
+ * Note: although this table stores log attenuation in 8.8 format, values
+ * were only calculated for 6 bits fractional precision, since that is
+ * the most precision offered by the trident hardware.
+ */
+
+static unsigned short log_table[LOG_TABLE_SIZE*2] =
+{
+ 4, 0x0604, 19, 0x0600, 34, 0x05fc,
+ 49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8,
+ 123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4,
+ 198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0,
+ 274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac,
+ 350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598,
+ 428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584,
+ 506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570,
+ 584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c,
+ 663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548,
+ 743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534,
+ 824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520,
+ 906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c,
+ 988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8,
+ 1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4,
+ 1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0,
+ 1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc,
+ 1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8,
+ 1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494,
+ 1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480,
+ 1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c,
+ 1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458,
+ 1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444,
+ 1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430,
+ 1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c,
+ 2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408,
+ 2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4,
+ 2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0,
+ 2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc,
+ 2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8,
+ 2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4,
+ 2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390,
+ 2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c,
+ 2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368,
+ 2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354,
+ 2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340,
+ 3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c,
+ 3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318,
+ 3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304,
+ 3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0,
+ 3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc,
+ 3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8,
+ 3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4,
+ 3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0,
+ 3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c,
+ 4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278,
+ 4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264,
+ 4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250,
+ 4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c,
+ 4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228,
+ 4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214,
+ 4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200,
+ 4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec,
+ 4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8,
+ 5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4,
+ 5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0,
+ 5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c,
+ 5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188,
+ 5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174,
+ 5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160,
+ 5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c,
+ 5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138,
+ 6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124,
+ 6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110,
+ 6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc,
+ 6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8,
+ 6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4,
+ 6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0,
+ 6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac,
+ 6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098,
+ 7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084,
+ 7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070,
+ 7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c,
+ 7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048,
+ 7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034,
+ 7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020,
+ 7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c,
+ 8133, 0x0008, 8162, 0x0004, 8192, 0x0000
+};
+
+static unsigned short lookup_volume_table( unsigned short value )
+{
+ /* This code is an optimised version of:
+ * int i = 0;
+ * while( volume_table[i*2] < value )
+ * i++;
+ * return volume_table[i*2+1];
+ */
+ unsigned short *ptr = log_table;
+ while( *ptr < value )
+ ptr += 2;
+ return *(ptr+1);
+}
+
+/* this function calculates a 8.8 fixed point logarithmic attenuation
+ * value from a linear volume value in the range 0 to 16384 */
+static unsigned short log_from_linear( unsigned short value )
+{
+ if (value >= 16384)
+ return 0x0000;
+ if (value) {
+ unsigned short result = 0;
+ int v, c;
+ for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
+ if( value >= v ) {
+ result += lookup_volume_table( (value - v) << c );
+ return result;
+ }
+ result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */
+ }
+ }
+ return 0xffff;
+}
+
+/*
+ * Sample handling operations
+ */
+
+static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position);
+static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode);
+static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq);
+static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume);
+static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop);
+static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position);
+static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data);
+
+static snd_trident_sample_ops_t sample_ops =
+{
+ sample_start,
+ sample_stop,
+ sample_freq,
+ sample_volume,
+ sample_loop,
+ sample_pos,
+ sample_private1
+};
+
+static void snd_trident_simple_init(snd_trident_voice_t * voice)
+{
+ //voice->handler_wave = interrupt_wave;
+ //voice->handler_volume = interrupt_volume;
+ //voice->handler_effect = interrupt_effect;
+ //voice->volume_change = NULL;
+ voice->sample_ops = &sample_ops;
+}
+
+static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position)
+{
+ simple_instrument_t *simple;
+ snd_seq_kinstr_t *instr;
+ unsigned long flags;
+ unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
+ unsigned int value;
+ unsigned int shift = 0;
+
+ instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+ if (instr == NULL)
+ return;
+ voice->instr = instr->instr; /* copy ID to speedup aliases */
+ simple = KINSTR_DATA(instr);
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+ voice->GVSel = 1; /* route to Wave volume */
+
+ voice->CTRL = 0;
+ voice->Alpha = 0;
+ voice->FMS = 0;
+
+ loop_start = simple->loop_start >> 4;
+ loop_end = simple->loop_end >> 4;
+ sample_start = (simple->start + position) >> 4;
+ if( sample_start >= simple->size )
+ sample_start = simple->start >> 4;
+ sample_end = simple->size;
+ start_offset = position >> 4;
+
+ if (simple->format & SIMPLE_WAVE_16BIT) {
+ voice->CTRL |= 8;
+ shift++;
+ }
+ if (simple->format & SIMPLE_WAVE_STEREO) {
+ voice->CTRL |= 4;
+ shift++;
+ }
+ if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
+ voice->CTRL |= 2;
+
+ voice->LBA = simple->address.memory;
+
+ if (simple->format & SIMPLE_WAVE_LOOP) {
+ voice->CTRL |= 1;
+ voice->LBA += loop_start << shift;
+ if( start_offset >= loop_start ) {
+ voice->CSO = start_offset - loop_start;
+ voice->negCSO = 0;
+ } else {
+ voice->CSO = loop_start - start_offset;
+ voice->negCSO = 1;
+ }
+ voice->ESO = loop_end - loop_start - 1;
+ } else {
+ voice->LBA += start_offset << shift;
+ voice->CSO = sample_start;
+ voice->ESO = sample_end - 1;
+ voice->negCSO = 0;
+ }
+
+ if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
+ snd_trident_stop_voice(trident, voice->number);
+ voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+ }
+
+ /* set CSO sign */
+ value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+ if( voice->negCSO ) {
+ value |= 1 << (voice->number&31);
+ } else {
+ value &= ~(1 << (voice->number&31));
+ }
+ outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+
+ voice->Attribute = 0;
+ snd_trident_write_voice_regs(trident, voice);
+ snd_trident_start_voice(trident, voice->number);
+ voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode)
+{
+ unsigned long flags;
+
+ if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
+ return;
+
+ switch (mode) {
+ default:
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ snd_trident_stop_voice(trident, voice->number);
+ voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ break;
+ case SAMPLE_STOP_LOOP: /* disable loop only */
+ voice->CTRL &= ~1;
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ break;
+ }
+}
+
+static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq)
+{
+ unsigned long flags;
+ freq >>= 4;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ if (freq == 44100)
+ voice->Delta = 0xeb3;
+ else if (freq == 8000)
+ voice->Delta = 0x2ab;
+ else if (freq == 48000)
+ voice->Delta = 0x1000;
+ else
+ voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
+
+ outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
+ outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
+ } else {
+ outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
+ }
+
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume)
+{
+ unsigned long flags;
+ unsigned short value;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ voice->GVSel = 0; /* use global music volume */
+ voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */
+ if (volume->volume >= 0) {
+ volume->volume &= 0x3fff;
+ /* linear volume -> logarithmic attenuation conversion
+ * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
+ * Vol register used when additional attenuation is required */
+ voice->RVol = 0;
+ voice->CVol = 0;
+ value = log_from_linear( volume->volume );
+ voice->Vol = 0;
+ voice->EC = (value & 0x3fff) >> 2;
+ if (value > 0x3fff) {
+ voice->EC |= 0xfc0;
+ if (value < 0x5f00 )
+ voice->Vol = ((value >> 8) - 0x3f) << 5;
+ else {
+ voice->Vol = 0x3ff;
+ voice->EC = 0xfff;
+ }
+ }
+ }
+ if (volume->lr >= 0) {
+ volume->lr &= 0x3fff;
+ /* approximate linear pan by attenuating channels */
+ if (volume->lr >= 0x2000) { /* attenuate left (pan right) */
+ value = 0x3fff - volume->lr;
+ for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
+ if (value >= pan_table[voice->Pan] )
+ break;
+ } else { /* attenuate right (pan left) */
+ for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
+ if ((unsigned int)volume->lr >= pan_table[voice->Pan] )
+ break;
+ voice->Pan |= 0x40;
+ }
+ }
+ outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
+ ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
+ (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+ value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
+ outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop)
+{
+ unsigned long flags;
+ simple_instrument_t *simple;
+ snd_seq_kinstr_t *instr;
+ unsigned int loop_start, loop_end;
+
+ instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+ if (instr == NULL)
+ return;
+ voice->instr = instr->instr; /* copy ID to speedup aliases */
+ simple = KINSTR_DATA(instr);
+
+ loop_start = loop->start >> 4;
+ loop_end = loop->end >> 4;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+
+ voice->LBA = simple->address.memory + loop_start;
+ voice->CSO = 0;
+ voice->ESO = loop_end - loop_start - 1;
+
+ outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
+ outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
+ if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
+ outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
+ outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+ outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+ } else {
+ outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
+ outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
+ }
+
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position)
+{
+ unsigned long flags;
+ simple_instrument_t *simple;
+ snd_seq_kinstr_t *instr;
+ unsigned int value;
+
+ instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+ if (instr == NULL)
+ return;
+ voice->instr = instr->instr; /* copy ID to speedup aliases */
+ simple = KINSTR_DATA(instr);
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+
+ if (simple->format & SIMPLE_WAVE_LOOP) {
+ if( position >= simple->loop_start ) {
+ voice->CSO = (position - simple->loop_start) >> 4;
+ voice->negCSO = 0;
+ } else {
+ voice->CSO = (simple->loop_start - position) >> 4;
+ voice->negCSO = 1;
+ }
+ } else {
+ voice->CSO = position >> 4;
+ voice->negCSO = 0;
+ }
+
+ /* set CSO sign */
+ value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+ if( voice->negCSO ) {
+ value |= 1 << (voice->number&31);
+ } else {
+ value &= ~(1 << (voice->number&31));
+ }
+ outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+
+
+ outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+ if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+ outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+ } else {
+ outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
+ }
+
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data)
+{
+}
+
+/*
+ * Memory management / sample loading
+ */
+
+static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr,
+ char __user *data, long len, int atomic)
+{
+ trident_t *trident = private_data;
+ int size = instr->size;
+ int shift = 0;
+
+ if (instr->format & SIMPLE_WAVE_BACKWARD ||
+ instr->format & SIMPLE_WAVE_BIDIR ||
+ instr->format & SIMPLE_WAVE_ULAW)
+ return -EINVAL; /* not supported */
+
+ if (instr->format & SIMPLE_WAVE_16BIT)
+ shift++;
+ if (instr->format & SIMPLE_WAVE_STEREO)
+ shift++;
+ size <<= shift;
+
+ if (trident->synth.current_size + size > trident->synth.max_size)
+ return -ENOMEM;
+
+ if (!access_ok(VERIFY_READ, data, size))
+ return -EFAULT;
+
+ if (trident->tlb.entries) {
+ snd_util_memblk_t *memblk;
+ memblk = snd_trident_synth_alloc(trident, size);
+ if (memblk == NULL)
+ return -ENOMEM;
+ if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
+ snd_trident_synth_free(trident, memblk);
+ return -EFAULT;
+ }
+ instr->address.ptr = (unsigned char*)memblk;
+ instr->address.memory = memblk->offset;
+ } else {
+ struct snd_dma_buffer dmab;
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+ size, &dmab) < 0)
+ return -ENOMEM;
+
+ if (copy_from_user(dmab.area, data, size)) {
+ snd_dma_free_pages(&dmab);
+ return -EFAULT;
+ }
+ instr->address.ptr = dmab.area;
+ instr->address.memory = dmab.addr;
+ }
+
+ trident->synth.current_size += size;
+ return 0;
+}
+
+static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr,
+ char __user *data, long len, int atomic)
+{
+ //trident_t *trident = private_data;
+ int size = instr->size;
+ int shift = 0;
+
+ if (instr->format & SIMPLE_WAVE_16BIT)
+ shift++;
+ if (instr->format & SIMPLE_WAVE_STEREO)
+ shift++;
+ size <<= shift;
+
+ if (!access_ok(VERIFY_WRITE, data, size))
+ return -EFAULT;
+
+ /* FIXME: not implemented yet */
+
+ return -EBUSY;
+}
+
+static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr,
+ int atomic)
+{
+ trident_t *trident = private_data;
+ int size = instr->size;
+
+ if (instr->format & SIMPLE_WAVE_16BIT)
+ size <<= 1;
+ if (instr->format & SIMPLE_WAVE_STEREO)
+ size <<= 1;
+
+ if (trident->tlb.entries) {
+ snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr;
+ if (memblk)
+ snd_trident_synth_free(trident, memblk);
+ else
+ return -EFAULT;
+ } else {
+ struct snd_dma_buffer dmab;
+ dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+ dmab.dev.dev = snd_dma_pci_data(trident->pci);
+ dmab.area = instr->address.ptr;
+ dmab.addr = instr->address.memory;
+ dmab.bytes = size;
+ snd_dma_free_pages(&dmab);
+ }
+
+ trident->synth.current_size -= size;
+ if (trident->synth.current_size < 0) /* shouldn't need this check... */
+ trident->synth.current_size = 0;
+
+ return 0;
+}
+
+static void select_instrument(trident_t * trident, snd_trident_voice_t * v)
+{
+ snd_seq_kinstr_t *instr;
+ instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
+ if (instr != NULL) {
+ if (instr->ops) {
+ if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
+ snd_trident_simple_init(v);
+ }
+ snd_seq_instr_free_use(trident->synth.ilist, instr);
+ }
+}
+
+/*
+
+ */
+
+static void event_sample(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_stop)
+ v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+ v->instr.std = ev->data.sample.param.sample.std;
+ if (v->instr.std & 0xff000000) { /* private instrument */
+ v->instr.std &= 0x00ffffff;
+ v->instr.std |= (unsigned int)ev->source.client << 24;
+ }
+ v->instr.bank = ev->data.sample.param.sample.bank;
+ v->instr.prg = ev->data.sample.param.sample.prg;
+ select_instrument(p->trident, v);
+}
+
+static void event_cluster(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_stop)
+ v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+ v->instr.cluster = ev->data.sample.param.cluster.cluster;
+ select_instrument(p->trident, v);
+}
+
+static void event_start(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_start)
+ v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
+}
+
+static void event_stop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_stop)
+ v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
+}
+
+static void event_freq(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_freq)
+ v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
+}
+
+static void event_volume(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_volume)
+ v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
+}
+
+static void event_loop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_loop)
+ v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
+}
+
+static void event_position(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_pos)
+ v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
+}
+
+static void event_private1(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+ if (v->sample_ops && v->sample_ops->sample_private1)
+ v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
+}
+
+typedef void (trident_sample_event_handler_t) (snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v);
+
+static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
+{
+ event_sample,
+ event_cluster,
+ event_start,
+ event_stop,
+ event_freq,
+ event_volume,
+ event_loop,
+ event_position,
+ event_private1
+};
+
+static void snd_trident_sample_event(snd_seq_event_t * ev, snd_trident_port_t * p)
+{
+ int idx, voice;
+ trident_t *trident = p->trident;
+ snd_trident_voice_t *v;
+ unsigned long flags;
+
+ idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
+ if (idx < 0 || idx > 8)
+ return;
+ for (voice = 0; voice < 64; voice++) {
+ v = &trident->synth.voices[voice];
+ if (v->use && v->client == ev->source.client &&
+ v->port == ev->source.port &&
+ v->index == ev->data.sample.channel) {
+ spin_lock_irqsave(&trident->event_lock, flags);
+ trident_sample_event_handlers[idx] (ev, p, v);
+ spin_unlock_irqrestore(&trident->event_lock, flags);
+ return;
+ }
+ }
+}
+
+/*
+
+ */
+
+static void snd_trident_synth_free_voices(trident_t * trident, int client, int port)
+{
+ int idx;
+ snd_trident_voice_t *voice;
+
+ for (idx = 0; idx < 32; idx++) {
+ voice = &trident->synth.voices[idx];
+ if (voice->use && voice->client == client && voice->port == port)
+ snd_trident_free_voice(trident, voice);
+ }
+}
+
+static int snd_trident_synth_use(void *private_data, snd_seq_port_subscribe_t * info)
+{
+ snd_trident_port_t *port = (snd_trident_port_t *) private_data;
+ trident_t *trident = port->trident;
+ snd_trident_voice_t *voice;
+ unsigned int idx;
+ unsigned long flags;
+
+ if (info->voices > 32)
+ return -EINVAL;
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ for (idx = 0; idx < info->voices; idx++) {
+ voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
+ if (voice == NULL) {
+ snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return -EBUSY;
+ }
+ voice->index = idx;
+ voice->Vol = 0x3ff;
+ voice->EC = 0x0fff;
+ }
+#if 0
+ for (idx = 0; idx < info->midi_voices; idx++) {
+ port->midi_has_voices = 1;
+ voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
+ if (voice == NULL) {
+ snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return -EBUSY;
+ }
+ voice->Vol = 0x3ff;
+ voice->EC = 0x0fff;
+ }
+#endif
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return 0;
+}
+
+static int snd_trident_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info)
+{
+ snd_trident_port_t *port = (snd_trident_port_t *) private_data;
+ trident_t *trident = port->trident;
+ unsigned long flags;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return 0;
+}
+
+/*
+
+ */
+
+static void snd_trident_synth_free_private_instruments(snd_trident_port_t * p, int client)
+{
+ snd_seq_instr_header_t ifree;
+
+ memset(&ifree, 0, sizeof(ifree));
+ ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
+ snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
+}
+
+static int snd_trident_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
+{
+ snd_trident_port_t *p = (snd_trident_port_t *) private_data;
+
+ if (p == NULL)
+ return -EINVAL;
+ if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
+ ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
+ snd_trident_sample_event(ev, p);
+ return 0;
+ }
+ if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
+ ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
+ if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
+ snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
+ return 0;
+ }
+ }
+ if (direct) {
+ if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
+ snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
+ p->trident->synth.ilist, ev,
+ p->trident->synth.seq_client, atomic, hop);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static void snd_trident_synth_instr_notify(void *private_data,
+ snd_seq_kinstr_t * instr,
+ int what)
+{
+ int idx;
+ trident_t *trident = private_data;
+ snd_trident_voice_t *pvoice;
+ unsigned long flags;
+
+ spin_lock_irqsave(&trident->event_lock, flags);
+ for (idx = 0; idx < 64; idx++) {
+ pvoice = &trident->synth.voices[idx];
+ if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
+ if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
+ pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
+ } else {
+ snd_trident_stop_voice(trident, pvoice->number);
+ pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&trident->event_lock, flags);
+}
+
+/*
+
+ */
+
+static void snd_trident_synth_free_port(void *private_data)
+{
+ snd_trident_port_t *p = (snd_trident_port_t *) private_data;
+
+ if (p)
+ snd_midi_channel_free_set(p->chset);
+}
+
+static int snd_trident_synth_create_port(trident_t * trident, int idx)
+{
+ snd_trident_port_t *p;
+ snd_seq_port_callback_t callbacks;
+ char name[32];
+ char *str;
+ int result;
+
+ p = &trident->synth.seq_ports[idx];
+ p->chset = snd_midi_channel_alloc_set(16);
+ if (p->chset == NULL)
+ return -ENOMEM;
+ p->chset->private_data = p;
+ p->trident = trident;
+ p->client = trident->synth.seq_client;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.owner = THIS_MODULE;
+ callbacks.use = snd_trident_synth_use;
+ callbacks.unuse = snd_trident_synth_unuse;
+ callbacks.event_input = snd_trident_synth_event_input;
+ callbacks.private_free = snd_trident_synth_free_port;
+ callbacks.private_data = p;
+
+ str = "???";
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break;
+ case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break;
+ case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break;
+ }
+ sprintf(name, "%s port %i", str, idx);
+ p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
+ &callbacks,
+ SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+ SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+ SNDRV_SEQ_PORT_TYPE_SYNTH,
+ 16, 0,
+ name);
+ if (p->chset->port < 0) {
+ result = p->chset->port;
+ snd_trident_synth_free_port(p);
+ return result;
+ }
+ p->port = p->chset->port;
+ return 0;
+}
+
+/*
+
+ */
+
+static int snd_trident_synth_new_device(snd_seq_device_t *dev)
+{
+ trident_t *trident;
+ int client, i;
+ snd_seq_client_callback_t callbacks;
+ snd_seq_client_info_t cinfo;
+ snd_seq_port_subscribe_t sub;
+ snd_simple_ops_t *simpleops;
+ char *str;
+
+ trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+ if (trident == NULL)
+ return -EINVAL;
+
+ trident->synth.seq_client = -1;
+
+ /* allocate new client */
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.private_data = trident;
+ callbacks.allow_output = callbacks.allow_input = 1;
+ client = trident->synth.seq_client =
+ snd_seq_create_kernel_client(trident->card, 1, &callbacks);
+ if (client < 0)
+ return client;
+
+ /* change name of client */
+ memset(&cinfo, 0, sizeof(cinfo));
+ cinfo.client = client;
+ cinfo.type = KERNEL_CLIENT;
+ str = "???";
+ switch (trident->device) {
+ case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break;
+ case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break;
+ case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break;
+ }
+ sprintf(cinfo.name, str);
+ snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+ for (i = 0; i < 4; i++)
+ snd_trident_synth_create_port(trident, i);
+
+ trident->synth.ilist = snd_seq_instr_list_new();
+ if (trident->synth.ilist == NULL) {
+ snd_seq_delete_kernel_client(client);
+ trident->synth.seq_client = -1;
+ return -ENOMEM;
+ }
+ trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+
+ simpleops = &trident->synth.simple_ops;
+ snd_seq_simple_init(simpleops, trident, NULL);
+ simpleops->put_sample = snd_trident_simple_put_sample;
+ simpleops->get_sample = snd_trident_simple_get_sample;
+ simpleops->remove_sample = snd_trident_simple_remove_sample;
+ simpleops->notify = snd_trident_synth_instr_notify;
+
+ memset(&sub, 0, sizeof(sub));
+ sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+ sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+ sub.dest.client = client;
+ sub.dest.port = 0;
+ snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
+
+ return 0;
+}
+
+static int snd_trident_synth_delete_device(snd_seq_device_t *dev)
+{
+ trident_t *trident;
+
+ trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+ if (trident == NULL)
+ return -EINVAL;
+
+ if (trident->synth.seq_client >= 0) {
+ snd_seq_delete_kernel_client(trident->synth.seq_client);
+ trident->synth.seq_client = -1;
+ }
+ if (trident->synth.ilist)
+ snd_seq_instr_list_free(&trident->synth.ilist);
+ return 0;
+}
+
+static int __init alsa_trident_synth_init(void)
+{
+ static snd_seq_dev_ops_t ops =
+ {
+ snd_trident_synth_new_device,
+ snd_trident_synth_delete_device
+ };
+
+ return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
+ sizeof(trident_t*));
+}
+
+static void __exit alsa_trident_synth_exit(void)
+{
+ snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
+}
+
+module_init(alsa_trident_synth_init)
+module_exit(alsa_trident_synth_exit)
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
new file mode 100644
index 00000000000..f1ce808501d
--- /dev/null
+++ b/sound/pci/via82xx.c
@@ -0,0 +1,2346 @@
+/*
+ * ALSA driver for VIA VT82xx (South Bridge)
+ *
+ * VT82C686A/B/C, VT8233A/C, VT8235
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ * Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
+ * 2002 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Changes:
+ *
+ * Dec. 19, 2002 Takashi Iwai <tiwai@suse.de>
+ * - use the DSX channels for the first pcm playback.
+ * (on VIA8233, 8233C and 8235 only)
+ * this will allow you play simultaneously up to 4 streams.
+ * multi-channel playback is assigned to the second device
+ * on these chips.
+ * - support the secondary capture (on VIA8233/C,8235)
+ * - SPDIF support
+ * the DSX3 channel can be used for SPDIF output.
+ * on VIA8233A, this channel is assigned to the second pcm
+ * playback.
+ * the card config of alsa-lib will assign the correct
+ * device for applications.
+ * - clean up the code, separate low-level initialization
+ * routines for each chipset.
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#if 0
+#define POINTER_DEBUG
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA VT82xx audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static long mpu_port[SNDRV_CARDS];
+#ifdef SUPPORT_JOYSTICK
+static int joystick[SNDRV_CARDS];
+#endif
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+static char *ac97_quirk[SNDRV_CARDS];
+static int dxs_support[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable audio part of VIA 82xx bridge.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port. (VT82C686x only)");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable joystick. (VT82C686x only)");
+#endif
+module_param_array(ac97_clock, int, NULL, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
+module_param_array(ac97_quirk, charp, NULL, 0444);
+MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
+module_param_array(dxs_support, int, NULL, 0444);
+MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA)");
+
+
+/* pci ids */
+#ifndef PCI_DEVICE_ID_VIA_82C686_5
+#define PCI_DEVICE_ID_VIA_82C686_5 0x3058
+#endif
+#ifndef PCI_DEVICE_ID_VIA_8233_5
+#define PCI_DEVICE_ID_VIA_8233_5 0x3059
+#endif
+
+/* revision numbers for via686 */
+#define VIA_REV_686_A 0x10
+#define VIA_REV_686_B 0x11
+#define VIA_REV_686_C 0x12
+#define VIA_REV_686_D 0x13
+#define VIA_REV_686_E 0x14
+#define VIA_REV_686_H 0x20
+
+/* revision numbers for via8233 */
+#define VIA_REV_PRE_8233 0x10 /* not in market */
+#define VIA_REV_8233C 0x20 /* 2 rec, 4 pb, 1 multi-pb */
+#define VIA_REV_8233 0x30 /* 2 rec, 4 pb, 1 multi-pb, spdif */
+#define VIA_REV_8233A 0x40 /* 1 rec, 1 multi-pb, spdf */
+#define VIA_REV_8235 0x50 /* 2 rec, 4 pb, 1 multi-pb, spdif */
+#define VIA_REV_8237 0x60
+
+/*
+ * Direct registers
+ */
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x)
+
+/* common offsets */
+#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */
+#define VIA_REG_STAT_ACTIVE 0x80 /* RO */
+#define VIA_REG_STAT_PAUSED 0x40 /* RO */
+#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */
+#define VIA_REG_STAT_STOPPED 0x04 /* RWC */
+#define VIA_REG_STAT_EOL 0x02 /* RWC */
+#define VIA_REG_STAT_FLAG 0x01 /* RWC */
+#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */
+#define VIA_REG_CTRL_START 0x80 /* WO */
+#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */
+#define VIA_REG_CTRL_AUTOSTART 0x20
+#define VIA_REG_CTRL_PAUSE 0x08 /* RW */
+#define VIA_REG_CTRL_INT_STOP 0x04
+#define VIA_REG_CTRL_INT_EOL 0x02
+#define VIA_REG_CTRL_INT_FLAG 0x01
+#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */
+#define VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
+#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type (686 only) */
+#define VIA_REG_TYPE_AUTOSTART 0x80 /* RW - autostart at EOL */
+#define VIA_REG_TYPE_16BIT 0x20 /* RW */
+#define VIA_REG_TYPE_STEREO 0x10 /* RW */
+#define VIA_REG_TYPE_INT_LLINE 0x00
+#define VIA_REG_TYPE_INT_LSAMPLE 0x04
+#define VIA_REG_TYPE_INT_LESSONE 0x08
+#define VIA_REG_TYPE_INT_MASK 0x0c
+#define VIA_REG_TYPE_INT_EOL 0x02
+#define VIA_REG_TYPE_INT_FLAG 0x01
+#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */
+#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */
+#define VIA_REG_OFFSET_STOP_IDX 0x08 /* dword - stop index, channel type, sample rate */
+#define VIA8233_REG_TYPE_16BIT 0x00200000 /* RW */
+#define VIA8233_REG_TYPE_STEREO 0x00100000 /* RW */
+#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */
+#define VIA_REG_OFFSET_CURR_INDEX 0x0f /* byte - channel current index (for via8233 only) */
+
+#define DEFINE_VIA_REGSET(name,val) \
+enum {\
+ VIA_REG_##name##_STATUS = (val),\
+ VIA_REG_##name##_CONTROL = (val) + 0x01,\
+ VIA_REG_##name##_TYPE = (val) + 0x02,\
+ VIA_REG_##name##_TABLE_PTR = (val) + 0x04,\
+ VIA_REG_##name##_CURR_PTR = (val) + 0x04,\
+ VIA_REG_##name##_STOP_IDX = (val) + 0x08,\
+ VIA_REG_##name##_CURR_COUNT = (val) + 0x0c,\
+}
+
+/* playback block */
+DEFINE_VIA_REGSET(PLAYBACK, 0x00);
+DEFINE_VIA_REGSET(CAPTURE, 0x10);
+DEFINE_VIA_REGSET(FM, 0x20);
+
+/* AC'97 */
+#define VIA_REG_AC97 0x80 /* dword */
+#define VIA_REG_AC97_CODEC_ID_MASK (3<<30)
+#define VIA_REG_AC97_CODEC_ID_SHIFT 30
+#define VIA_REG_AC97_CODEC_ID_PRIMARY 0x00
+#define VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
+#define VIA_REG_AC97_SECONDARY_VALID (1<<27)
+#define VIA_REG_AC97_PRIMARY_VALID (1<<25)
+#define VIA_REG_AC97_BUSY (1<<24)
+#define VIA_REG_AC97_READ (1<<23)
+#define VIA_REG_AC97_CMD_SHIFT 16
+#define VIA_REG_AC97_CMD_MASK 0x7e
+#define VIA_REG_AC97_DATA_SHIFT 0
+#define VIA_REG_AC97_DATA_MASK 0xffff
+
+#define VIA_REG_SGD_SHADOW 0x84 /* dword */
+/* via686 */
+#define VIA_REG_SGD_STAT_PB_FLAG (1<<0)
+#define VIA_REG_SGD_STAT_CP_FLAG (1<<1)
+#define VIA_REG_SGD_STAT_FM_FLAG (1<<2)
+#define VIA_REG_SGD_STAT_PB_EOL (1<<4)
+#define VIA_REG_SGD_STAT_CP_EOL (1<<5)
+#define VIA_REG_SGD_STAT_FM_EOL (1<<6)
+#define VIA_REG_SGD_STAT_PB_STOP (1<<8)
+#define VIA_REG_SGD_STAT_CP_STOP (1<<9)
+#define VIA_REG_SGD_STAT_FM_STOP (1<<10)
+#define VIA_REG_SGD_STAT_PB_ACTIVE (1<<12)
+#define VIA_REG_SGD_STAT_CP_ACTIVE (1<<13)
+#define VIA_REG_SGD_STAT_FM_ACTIVE (1<<14)
+/* via8233 */
+#define VIA8233_REG_SGD_STAT_FLAG (1<<0)
+#define VIA8233_REG_SGD_STAT_EOL (1<<1)
+#define VIA8233_REG_SGD_STAT_STOP (1<<2)
+#define VIA8233_REG_SGD_STAT_ACTIVE (1<<3)
+#define VIA8233_INTR_MASK(chan) ((VIA8233_REG_SGD_STAT_FLAG|VIA8233_REG_SGD_STAT_EOL) << ((chan) * 4))
+#define VIA8233_REG_SGD_CHAN_SDX 0
+#define VIA8233_REG_SGD_CHAN_MULTI 4
+#define VIA8233_REG_SGD_CHAN_REC 6
+#define VIA8233_REG_SGD_CHAN_REC1 7
+
+#define VIA_REG_GPI_STATUS 0x88
+#define VIA_REG_GPI_INTR 0x8c
+
+/* multi-channel and capture registers for via8233 */
+DEFINE_VIA_REGSET(MULTPLAY, 0x40);
+DEFINE_VIA_REGSET(CAPTURE_8233, 0x60);
+
+/* via8233-specific registers */
+#define VIA_REG_OFS_PLAYBACK_VOLUME_L 0x02 /* byte */
+#define VIA_REG_OFS_PLAYBACK_VOLUME_R 0x03 /* byte */
+#define VIA_REG_OFS_MULTPLAY_FORMAT 0x02 /* byte - format and channels */
+#define VIA_REG_MULTPLAY_FMT_8BIT 0x00
+#define VIA_REG_MULTPLAY_FMT_16BIT 0x80
+#define VIA_REG_MULTPLAY_FMT_CH_MASK 0x70 /* # channels << 4 (valid = 1,2,4,6) */
+#define VIA_REG_OFS_CAPTURE_FIFO 0x02 /* byte - bit 6 = fifo enable */
+#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40
+
+#define VIA_DXS_MAX_VOLUME 31 /* max. volume (attenuation) of reg 0x32/33 */
+
+#define VIA_REG_CAPTURE_CHANNEL 0x63 /* byte - input select */
+#define VIA_REG_CAPTURE_CHANNEL_MIC 0x4
+#define VIA_REG_CAPTURE_CHANNEL_LINE 0
+#define VIA_REG_CAPTURE_SELECT_CODEC 0x03 /* recording source codec (0 = primary) */
+
+#define VIA_TBL_BIT_FLAG 0x40000000
+#define VIA_TBL_BIT_EOL 0x80000000
+
+/* pci space */
+#define VIA_ACLINK_STAT 0x40
+#define VIA_ACLINK_C11_READY 0x20
+#define VIA_ACLINK_C10_READY 0x10
+#define VIA_ACLINK_C01_READY 0x04 /* secondary codec ready */
+#define VIA_ACLINK_LOWPOWER 0x02 /* low-power state */
+#define VIA_ACLINK_C00_READY 0x01 /* primary codec ready */
+#define VIA_ACLINK_CTRL 0x41
+#define VIA_ACLINK_CTRL_ENABLE 0x80 /* 0: disable, 1: enable */
+#define VIA_ACLINK_CTRL_RESET 0x40 /* 0: assert, 1: de-assert */
+#define VIA_ACLINK_CTRL_SYNC 0x20 /* 0: release SYNC, 1: force SYNC hi */
+#define VIA_ACLINK_CTRL_SDO 0x10 /* 0: release SDO, 1: force SDO hi */
+#define VIA_ACLINK_CTRL_VRA 0x08 /* 0: disable VRA, 1: enable VRA */
+#define VIA_ACLINK_CTRL_PCM 0x04 /* 0: disable PCM, 1: enable PCM */
+#define VIA_ACLINK_CTRL_FM 0x02 /* via686 only */
+#define VIA_ACLINK_CTRL_SB 0x01 /* via686 only */
+#define VIA_ACLINK_CTRL_INIT (VIA_ACLINK_CTRL_ENABLE|\
+ VIA_ACLINK_CTRL_RESET|\
+ VIA_ACLINK_CTRL_PCM|\
+ VIA_ACLINK_CTRL_VRA)
+#define VIA_FUNC_ENABLE 0x42
+#define VIA_FUNC_MIDI_PNP 0x80 /* FIXME: it's 0x40 in the datasheet! */
+#define VIA_FUNC_MIDI_IRQMASK 0x40 /* FIXME: not documented! */
+#define VIA_FUNC_RX2C_WRITE 0x20
+#define VIA_FUNC_SB_FIFO_EMPTY 0x10
+#define VIA_FUNC_ENABLE_GAME 0x08
+#define VIA_FUNC_ENABLE_FM 0x04
+#define VIA_FUNC_ENABLE_MIDI 0x02
+#define VIA_FUNC_ENABLE_SB 0x01
+#define VIA_PNP_CONTROL 0x43
+#define VIA_FM_NMI_CTRL 0x48
+#define VIA8233_VOLCHG_CTRL 0x48
+#define VIA8233_SPDIF_CTRL 0x49
+#define VIA8233_SPDIF_DX3 0x08
+#define VIA8233_SPDIF_SLOT_MASK 0x03
+#define VIA8233_SPDIF_SLOT_1011 0x00
+#define VIA8233_SPDIF_SLOT_34 0x01
+#define VIA8233_SPDIF_SLOT_78 0x02
+#define VIA8233_SPDIF_SLOT_69 0x03
+
+/*
+ */
+
+#define VIA_DXS_AUTO 0
+#define VIA_DXS_ENABLE 1
+#define VIA_DXS_DISABLE 2
+#define VIA_DXS_48K 3
+#define VIA_DXS_NO_VRA 4
+
+
+/*
+ */
+
+typedef struct _snd_via82xx via82xx_t;
+typedef struct via_dev viadev_t;
+
+/*
+ * pcm stream
+ */
+
+struct snd_via_sg_table {
+ unsigned int offset;
+ unsigned int size;
+} ;
+
+#define VIA_TABLE_SIZE 255
+
+struct via_dev {
+ unsigned int reg_offset;
+ unsigned long port;
+ int direction; /* playback = 0, capture = 1 */
+ snd_pcm_substream_t *substream;
+ int running;
+ unsigned int tbl_entries; /* # descriptors */
+ struct snd_dma_buffer table;
+ struct snd_via_sg_table *idx_table;
+ /* for recovery from the unexpected pointer */
+ unsigned int lastpos;
+ unsigned int fragsize;
+ unsigned int bufsize;
+ unsigned int bufsize2;
+};
+
+
+enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 };
+enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A };
+
+#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */
+
+struct via_rate_lock {
+ spinlock_t lock;
+ int rate;
+ int used;
+};
+
+struct _snd_via82xx {
+ int irq;
+
+ unsigned long port;
+ struct resource *mpu_res;
+ int chip_type;
+ unsigned char revision;
+
+ unsigned char old_legacy;
+ unsigned char old_legacy_cfg;
+#ifdef CONFIG_PM
+ unsigned char legacy_saved;
+ unsigned char legacy_cfg_saved;
+ unsigned char spdif_ctrl_saved;
+ unsigned char capture_src_saved[2];
+ unsigned int mpu_port_saved;
+#endif
+
+ unsigned char playback_volume[2]; /* for VIA8233/C/8235; default = 0 */
+
+ unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+
+ unsigned int num_devs;
+ unsigned int playback_devno, multi_devno, capture_devno;
+ viadev_t devs[VIA_MAX_DEVS];
+ struct via_rate_lock rates[2]; /* playback and capture */
+ unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */
+ unsigned int no_vra: 1; /* no need to set VRA on DXS channels */
+ unsigned int spdif_on: 1; /* only spdif rates work to external DACs */
+
+ snd_pcm_t *pcms[2];
+ snd_rawmidi_t *rmidi;
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97;
+ unsigned int ac97_clock;
+ unsigned int ac97_secondary; /* secondary AC'97 codec is present */
+
+ spinlock_t reg_lock;
+ snd_info_entry_t *proc_entry;
+
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+};
+
+static struct pci_device_id snd_via82xx_ids[] = {
+ { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */
+ { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via82xx_ids);
+
+/*
+ */
+
+/*
+ * allocate and initialize the descriptor buffers
+ * periods = number of periods
+ * fragsize = period size in bytes
+ */
+static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+ struct pci_dev *pci,
+ unsigned int periods, unsigned int fragsize)
+{
+ unsigned int i, idx, ofs, rest;
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+
+ if (dev->table.area == NULL) {
+ /* the start of each lists must be aligned to 8 bytes,
+ * but the kernel pages are much bigger, so we don't care
+ */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
+ &dev->table) < 0)
+ return -ENOMEM;
+ }
+ if (! dev->idx_table) {
+ dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
+ if (! dev->idx_table)
+ return -ENOMEM;
+ }
+
+ /* fill the entries */
+ idx = 0;
+ ofs = 0;
+ for (i = 0; i < periods; i++) {
+ rest = fragsize;
+ /* fill descriptors for a period.
+ * a period can be split to several descriptors if it's
+ * over page boundary.
+ */
+ do {
+ unsigned int r;
+ unsigned int flag;
+
+ if (idx >= VIA_TABLE_SIZE) {
+ snd_printk(KERN_ERR "via82xx: too much table size!\n");
+ return -EINVAL;
+ }
+ ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
+ r = PAGE_SIZE - (ofs % PAGE_SIZE);
+ if (rest < r)
+ r = rest;
+ rest -= r;
+ if (! rest) {
+ if (i == periods - 1)
+ flag = VIA_TBL_BIT_EOL; /* buffer boundary */
+ else
+ flag = VIA_TBL_BIT_FLAG; /* period boundary */
+ } else
+ flag = 0; /* period continues to the next */
+ // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest);
+ ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
+ dev->idx_table[idx].offset = ofs;
+ dev->idx_table[idx].size = r;
+ ofs += r;
+ idx++;
+ } while (rest > 0);
+ }
+ dev->tbl_entries = idx;
+ dev->bufsize = periods * fragsize;
+ dev->bufsize2 = dev->bufsize / 2;
+ dev->fragsize = fragsize;
+ return 0;
+}
+
+
+static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+ struct pci_dev *pci)
+{
+ if (dev->table.area) {
+ snd_dma_free_pages(&dev->table);
+ dev->table.area = NULL;
+ }
+ if (dev->idx_table) {
+ kfree(dev->idx_table);
+ dev->idx_table = NULL;
+ }
+ return 0;
+}
+
+/*
+ * Basic I/O
+ */
+
+static inline unsigned int snd_via82xx_codec_xread(via82xx_t *chip)
+{
+ return inl(VIAREG(chip, AC97));
+}
+
+static inline void snd_via82xx_codec_xwrite(via82xx_t *chip, unsigned int val)
+{
+ outl(val, VIAREG(chip, AC97));
+}
+
+static int snd_via82xx_codec_ready(via82xx_t *chip, int secondary)
+{
+ unsigned int timeout = 1000; /* 1ms */
+ unsigned int val;
+
+ while (timeout-- > 0) {
+ udelay(1);
+ if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+ return val & 0xffff;
+ }
+ snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip));
+ return -EIO;
+}
+
+static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary)
+{
+ unsigned int timeout = 1000; /* 1ms */
+ unsigned int val, val1;
+ unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
+ VIA_REG_AC97_SECONDARY_VALID;
+
+ while (timeout-- > 0) {
+ val = snd_via82xx_codec_xread(chip);
+ val1 = val & (VIA_REG_AC97_BUSY | stat);
+ if (val1 == stat)
+ return val & 0xffff;
+ udelay(1);
+ }
+ return -EIO;
+}
+
+static void snd_via82xx_codec_wait(ac97_t *ac97)
+{
+ via82xx_t *chip = ac97->private_data;
+ int err;
+ err = snd_via82xx_codec_ready(chip, ac97->num);
+ /* here we need to wait fairly for long time.. */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+}
+
+static void snd_via82xx_codec_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ via82xx_t *chip = ac97->private_data;
+ unsigned int xval;
+
+ xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
+ xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
+ xval |= reg << VIA_REG_AC97_CMD_SHIFT;
+ xval |= val << VIA_REG_AC97_DATA_SHIFT;
+ snd_via82xx_codec_xwrite(chip, xval);
+ snd_via82xx_codec_ready(chip, ac97->num);
+}
+
+static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg)
+{
+ via82xx_t *chip = ac97->private_data;
+ unsigned int xval, val = 0xffff;
+ int again = 0;
+
+ xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT;
+ xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
+ xval |= VIA_REG_AC97_READ;
+ xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
+ while (1) {
+ if (again++ > 3) {
+ snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n", ac97->num, snd_via82xx_codec_xread(chip));
+ return 0xffff;
+ }
+ snd_via82xx_codec_xwrite(chip, xval);
+ udelay (20);
+ if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) {
+ udelay(25);
+ val = snd_via82xx_codec_xread(chip);
+ break;
+ }
+ }
+ return val & 0xffff;
+}
+
+static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev)
+{
+ outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET,
+ VIADEV_REG(viadev, OFFSET_CONTROL));
+ inb(VIADEV_REG(viadev, OFFSET_CONTROL));
+ udelay(50);
+ /* disable interrupts */
+ outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL));
+ /* clear interrupts */
+ outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS));
+ outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */
+ // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR));
+ viadev->lastpos = 0;
+}
+
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ via82xx_t *chip = dev_id;
+ unsigned int status;
+ unsigned int i;
+
+ status = inl(VIAREG(chip, SGD_SHADOW));
+ if (! (status & chip->intr_mask)) {
+ if (chip->rmidi)
+ /* check mpu401 interrupt */
+ return snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+ return IRQ_NONE;
+ }
+
+ /* check status for each stream */
+ spin_lock(&chip->reg_lock);
+ for (i = 0; i < chip->num_devs; i++) {
+ viadev_t *viadev = &chip->devs[i];
+ unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
+ c_status &= (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED);
+ if (! c_status)
+ continue;
+ if (viadev->substream && viadev->running) {
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(viadev->substream);
+ spin_lock(&chip->reg_lock);
+ }
+ outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
+ }
+ spin_unlock(&chip->reg_lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * PCM callbacks
+ */
+
+/*
+ * trigger callback
+ */
+static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ unsigned char val;
+
+ if (chip->chip_type != TYPE_VIA686)
+ val = VIA_REG_CTRL_INT;
+ else
+ val = 0;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ val |= VIA_REG_CTRL_START;
+ viadev->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ val = VIA_REG_CTRL_TERMINATE;
+ viadev->running = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val |= VIA_REG_CTRL_PAUSE;
+ viadev->running = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ viadev->running = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ outb(val, VIADEV_REG(viadev, OFFSET_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_STOP)
+ snd_via82xx_channel_reset(chip, viadev);
+ return 0;
+}
+
+
+/*
+ * pointer callbacks
+ */
+
+/*
+ * calculate the linear position at the given sg-buffer index and the rest count
+ */
+
+#define check_invalid_pos(viadev,pos) \
+ ((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))
+
+static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, unsigned int count)
+{
+ unsigned int size, base, res;
+
+ size = viadev->idx_table[idx].size;
+ base = viadev->idx_table[idx].offset;
+ res = base + size - count;
+
+ /* check the validity of the calculated position */
+ if (size < count) {
+ snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n", (int)size, (int)count);
+ res = viadev->lastpos;
+ } else {
+ if (! count) {
+ /* Some mobos report count = 0 on the DMA boundary,
+ * i.e. count = size indeed.
+ * Let's check whether this step is above the expected size.
+ */
+ int delta = res - viadev->lastpos;
+ if (delta < 0)
+ delta += viadev->bufsize;
+ if ((unsigned int)delta > viadev->fragsize)
+ res = base;
+ }
+ if (check_invalid_pos(viadev, res)) {
+#ifdef POINTER_DEBUG
+ printk(KERN_DEBUG "fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count);
+#endif
+ /* count register returns full size when end of buffer is reached */
+ res = base + size;
+ if (check_invalid_pos(viadev, res)) {
+ snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), using last valid pointer\n");
+ res = viadev->lastpos;
+ }
+ }
+ }
+ viadev->lastpos = res; /* remember the last position */
+ if (res >= viadev->bufsize)
+ res -= viadev->bufsize;
+ return res;
+}
+
+/*
+ * get the current pointer on via686
+ */
+static snd_pcm_uframes_t snd_via686_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ unsigned int idx, ptr, count, res;
+
+ snd_assert(viadev->tbl_entries, return 0);
+ if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
+ return 0;
+
+ spin_lock(&chip->reg_lock);
+ count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff;
+ /* The via686a does not have the current index register,
+ * so we need to calculate the index from CURR_PTR.
+ */
+ ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR));
+ if (ptr <= (unsigned int)viadev->table.addr)
+ idx = 0;
+ else /* CURR_PTR holds the address + 8 */
+ idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;
+ res = calc_linear_pos(viadev, idx, count);
+ spin_unlock(&chip->reg_lock);
+
+ return bytes_to_frames(substream->runtime, res);
+}
+
+/*
+ * get the current pointer on via823x
+ */
+static snd_pcm_uframes_t snd_via8233_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ unsigned int idx, count, res;
+ int timeout = 5000;
+
+ snd_assert(viadev->tbl_entries, return 0);
+ if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
+ return 0;
+ spin_lock(&chip->reg_lock);
+ do {
+ count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT));
+ /* some mobos read 0 count */
+ if ((count & 0xffffff) || ! viadev->running)
+ break;
+ } while (--timeout);
+ if (! timeout)
+ snd_printd(KERN_ERR "zero position is read\n");
+ idx = count >> 24;
+ if (idx >= viadev->tbl_entries) {
+#ifdef POINTER_DEBUG
+ printk("fail: invalid idx = %i/%i\n", idx, viadev->tbl_entries);
+#endif
+ res = viadev->lastpos;
+ } else {
+ count &= 0xffffff;
+ res = calc_linear_pos(viadev, idx, count);
+ }
+ spin_unlock(&chip->reg_lock);
+
+ return bytes_to_frames(substream->runtime, res);
+}
+
+
+/*
+ * hw_params callback:
+ * allocate the buffer and build up the buffer description table
+ */
+static int snd_via82xx_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ int err;
+
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+ err = build_via_table(viadev, substream, chip->pci,
+ params_periods(hw_params),
+ params_period_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * hw_free callback:
+ * clean up the buffer description table and release the buffer
+ */
+static int snd_via82xx_hw_free(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+
+ clean_via_table(viadev, substream, chip->pci);
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+
+/*
+ * set up the table pointer
+ */
+static void snd_via82xx_set_table_ptr(via82xx_t *chip, viadev_t *viadev)
+{
+ snd_via82xx_codec_ready(chip, 0);
+ outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR));
+ udelay(20);
+ snd_via82xx_codec_ready(chip, 0);
+}
+
+/*
+ * prepare callback for playback and capture on via686
+ */
+static void via686_setup_format(via82xx_t *chip, viadev_t *viadev, snd_pcm_runtime_t *runtime)
+{
+ snd_via82xx_channel_reset(chip, viadev);
+ /* this must be set after channel_reset */
+ snd_via82xx_set_table_ptr(chip, viadev);
+ outb(VIA_REG_TYPE_AUTOSTART |
+ (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
+ (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
+ ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) |
+ VIA_REG_TYPE_INT_EOL |
+ VIA_REG_TYPE_INT_FLAG, VIADEV_REG(viadev, OFFSET_TYPE));
+}
+
+static int snd_via686_playback_prepare(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+ snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+ via686_setup_format(chip, viadev, runtime);
+ return 0;
+}
+
+static int snd_via686_capture_prepare(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+ via686_setup_format(chip, viadev, runtime);
+ return 0;
+}
+
+/*
+ * lock the current rate
+ */
+static int via_lock_rate(struct via_rate_lock *rec, int rate)
+{
+ int changed = 0;
+
+ spin_lock_irq(&rec->lock);
+ if (rec->rate != rate) {
+ if (rec->rate && rec->used > 1) /* already set */
+ changed = -EINVAL;
+ else {
+ rec->rate = rate;
+ changed = 1;
+ }
+ }
+ spin_unlock_irq(&rec->lock);
+ return changed;
+}
+
+/*
+ * prepare callback for DSX playback on via823x
+ */
+static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int rate_changed;
+ u32 rbits;
+
+ if ((rate_changed = via_lock_rate(&chip->rates[0], runtime->rate)) < 0)
+ return rate_changed;
+ if (rate_changed) {
+ snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
+ chip->no_vra ? 48000 : runtime->rate);
+ snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+ }
+ if (runtime->rate == 48000)
+ rbits = 0xfffff;
+ else
+ rbits = (0x100000 / 48000) * runtime->rate + ((0x100000 % 48000) * runtime->rate) / 48000;
+ snd_assert((rbits & ~0xfffff) == 0, return -EINVAL);
+ snd_via82xx_channel_reset(chip, viadev);
+ snd_via82xx_set_table_ptr(chip, viadev);
+ outb(chip->playback_volume[0], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_L));
+ outb(chip->playback_volume[1], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_R));
+ outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | /* format */
+ (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | /* stereo */
+ rbits | /* rate */
+ 0xff000000, /* STOP index is never reached */
+ VIADEV_REG(viadev, OFFSET_STOP_IDX));
+ udelay(20);
+ snd_via82xx_codec_ready(chip, 0);
+ return 0;
+}
+
+/*
+ * prepare callback for multi-channel playback on via823x
+ */
+static int snd_via8233_multi_prepare(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int slots;
+ int fmt;
+
+ if (via_lock_rate(&chip->rates[0], runtime->rate) < 0)
+ return -EINVAL;
+ snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+ snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+ snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate);
+ snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+ snd_via82xx_channel_reset(chip, viadev);
+ snd_via82xx_set_table_ptr(chip, viadev);
+
+ fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT;
+ fmt |= runtime->channels << 4;
+ outb(fmt, VIADEV_REG(viadev, OFS_MULTPLAY_FORMAT));
+#if 0
+ if (chip->revision == VIA_REV_8233A)
+ slots = 0;
+ else
+#endif
+ {
+ /* set sample number to slot 3, 4, 7, 8, 6, 9 (for VIA8233/C,8235) */
+ /* corresponding to FL, FR, RL, RR, C, LFE ?? */
+ switch (runtime->channels) {
+ case 1: slots = (1<<0) | (1<<4); break;
+ case 2: slots = (1<<0) | (2<<4); break;
+ case 3: slots = (1<<0) | (2<<4) | (5<<8); break;
+ case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break;
+ case 5: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16); break;
+ case 6: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16) | (6<<20); break;
+ default: slots = 0; break;
+ }
+ }
+ /* STOP index is never reached */
+ outl(0xff000000 | slots, VIADEV_REG(viadev, OFFSET_STOP_IDX));
+ udelay(20);
+ snd_via82xx_codec_ready(chip, 0);
+ return 0;
+}
+
+/*
+ * prepare callback for capture on via823x
+ */
+static int snd_via8233_capture_prepare(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (via_lock_rate(&chip->rates[1], runtime->rate) < 0)
+ return -EINVAL;
+ snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+ snd_via82xx_channel_reset(chip, viadev);
+ snd_via82xx_set_table_ptr(chip, viadev);
+ outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIADEV_REG(viadev, OFS_CAPTURE_FIFO));
+ outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) |
+ (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) |
+ 0xff000000, /* STOP index is never reached */
+ VIADEV_REG(viadev, OFFSET_STOP_IDX));
+ udelay(20);
+ snd_via82xx_codec_ready(chip, 0);
+ return 0;
+}
+
+
+/*
+ * pcm hardware definition, identical for both playback and capture
+ */
+static snd_pcm_hardware_t snd_via82xx_hw =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = VIA_TABLE_SIZE / 2,
+ .fifo_size = 0,
+};
+
+
+/*
+ * open callback skeleton
+ */
+static int snd_via82xx_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+ struct via_rate_lock *ratep;
+
+ runtime->hw = snd_via82xx_hw;
+
+ /* set the hw rate condition */
+ ratep = &chip->rates[viadev->direction];
+ spin_lock_irq(&ratep->lock);
+ ratep->used++;
+ if (chip->spdif_on && viadev->reg_offset == 0x30) {
+ /* DXS#3 and spdif is on */
+ runtime->hw.rates = chip->ac97->rates[AC97_RATES_SPDIF];
+ snd_pcm_limit_hw_rates(runtime);
+ } else if (chip->dxs_fixed && viadev->reg_offset < 0x40) {
+ /* fixed DXS playback rate */
+ runtime->hw.rates = SNDRV_PCM_RATE_48000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+ } else if (! ratep->rate) {
+ int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC;
+ runtime->hw.rates = chip->ac97->rates[idx];
+ snd_pcm_limit_hw_rates(runtime);
+ } else {
+ /* a fixed rate */
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate;
+ }
+ spin_unlock_irq(&ratep->lock);
+
+ /* we may remove following constaint when we modify table entries
+ in interrupt */
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+
+ runtime->private_data = viadev;
+ viadev->substream = substream;
+
+ return 0;
+}
+
+
+/*
+ * open callback for playback on via686 and via823x DSX
+ */
+static int snd_via82xx_playback_open(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = &chip->devs[chip->playback_devno + substream->number];
+ int err;
+
+ if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * open callback for playback on via823x multi-channel
+ */
+static int snd_via8233_multi_open(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = &chip->devs[chip->multi_devno];
+ int err;
+ /* channels constraint for VIA8233A
+ * 3 and 5 channels are not supported
+ */
+ static unsigned int channels[] = {
+ 1, 2, 4, 6
+ };
+ static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+ return err;
+ substream->runtime->hw.channels_max = 6;
+ if (chip->revision == VIA_REV_8233A)
+ snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
+ return 0;
+}
+
+/*
+ * open callback for capture on via686 and via823x
+ */
+static int snd_via82xx_capture_open(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = &chip->devs[chip->capture_devno + substream->pcm->device];
+
+ return snd_via82xx_pcm_open(chip, viadev, substream);
+}
+
+/*
+ * close callback
+ */
+static int snd_via82xx_pcm_close(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ struct via_rate_lock *ratep;
+
+ /* release the rate lock */
+ ratep = &chip->rates[viadev->direction];
+ spin_lock_irq(&ratep->lock);
+ ratep->used--;
+ if (! ratep->used)
+ ratep->rate = 0;
+ spin_unlock_irq(&ratep->lock);
+
+ viadev->substream = NULL;
+ return 0;
+}
+
+
+/* via686 playback callbacks */
+static snd_pcm_ops_t snd_via686_playback_ops = {
+ .open = snd_via82xx_playback_open,
+ .close = snd_via82xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_via82xx_hw_params,
+ .hw_free = snd_via82xx_hw_free,
+ .prepare = snd_via686_playback_prepare,
+ .trigger = snd_via82xx_pcm_trigger,
+ .pointer = snd_via686_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+/* via686 capture callbacks */
+static snd_pcm_ops_t snd_via686_capture_ops = {
+ .open = snd_via82xx_capture_open,
+ .close = snd_via82xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_via82xx_hw_params,
+ .hw_free = snd_via82xx_hw_free,
+ .prepare = snd_via686_capture_prepare,
+ .trigger = snd_via82xx_pcm_trigger,
+ .pointer = snd_via686_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+/* via823x DSX playback callbacks */
+static snd_pcm_ops_t snd_via8233_playback_ops = {
+ .open = snd_via82xx_playback_open,
+ .close = snd_via82xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_via82xx_hw_params,
+ .hw_free = snd_via82xx_hw_free,
+ .prepare = snd_via8233_playback_prepare,
+ .trigger = snd_via82xx_pcm_trigger,
+ .pointer = snd_via8233_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+/* via823x multi-channel playback callbacks */
+static snd_pcm_ops_t snd_via8233_multi_ops = {
+ .open = snd_via8233_multi_open,
+ .close = snd_via82xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_via82xx_hw_params,
+ .hw_free = snd_via82xx_hw_free,
+ .prepare = snd_via8233_multi_prepare,
+ .trigger = snd_via82xx_pcm_trigger,
+ .pointer = snd_via8233_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+/* via823x capture callbacks */
+static snd_pcm_ops_t snd_via8233_capture_ops = {
+ .open = snd_via82xx_capture_open,
+ .close = snd_via82xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_via82xx_hw_params,
+ .hw_free = snd_via82xx_hw_free,
+ .prepare = snd_via8233_capture_prepare,
+ .trigger = snd_via82xx_pcm_trigger,
+ .pointer = snd_via8233_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+
+static void init_viadev(via82xx_t *chip, int idx, unsigned int reg_offset, int direction)
+{
+ chip->devs[idx].reg_offset = reg_offset;
+ chip->devs[idx].direction = direction;
+ chip->devs[idx].port = chip->port + reg_offset;
+}
+
+/*
+ * create pcm instances for VIA8233, 8233C and 8235 (not 8233A)
+ */
+static int __devinit snd_via8233_pcm_new(via82xx_t *chip)
+{
+ snd_pcm_t *pcm;
+ int i, err;
+
+ chip->playback_devno = 0; /* x 4 */
+ chip->multi_devno = 4; /* x 1 */
+ chip->capture_devno = 5; /* x 2 */
+ chip->num_devs = 7;
+ chip->intr_mask = 0x33033333; /* FLAG|EOL for rec0-1, mc, sdx0-3 */
+
+ /* PCM #0: 4 DSX playbacks and 1 capture */
+ err = snd_pcm_new(chip->card, chip->card->shortname, 0, 4, 1, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcms[0] = pcm;
+ /* set up playbacks */
+ for (i = 0; i < 4; i++)
+ init_viadev(chip, i, 0x10 * i, 0);
+ /* capture */
+ init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1);
+
+ if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
+ return err;
+
+ /* PCM #1: multi-channel playback and 2nd capture */
+ err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcms[1] = pcm;
+ /* set up playback */
+ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0);
+ /* set up capture */
+ init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 1);
+
+ if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * create pcm instances for VIA8233A
+ */
+static int __devinit snd_via8233a_pcm_new(via82xx_t *chip)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ chip->multi_devno = 0;
+ chip->playback_devno = 1;
+ chip->capture_devno = 2;
+ chip->num_devs = 3;
+ chip->intr_mask = 0x03033000; /* FLAG|EOL for rec0, mc, sdx3 */
+
+ /* PCM #0: multi-channel playback and capture */
+ err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcms[0] = pcm;
+ /* set up playback */
+ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0);
+ /* capture */
+ init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1);
+
+ if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
+ return err;
+
+ /* SPDIF supported? */
+ if (! ac97_can_spdif(chip->ac97))
+ return 0;
+
+ /* PCM #1: DXS3 playback (for spdif) */
+ err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 0, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcms[1] = pcm;
+ /* set up playback */
+ init_viadev(chip, chip->playback_devno, 0x30, 0);
+
+ if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * create a pcm instance for via686a/b
+ */
+static int __devinit snd_via686_pcm_new(via82xx_t *chip)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ chip->playback_devno = 0;
+ chip->capture_devno = 1;
+ chip->num_devs = 2;
+ chip->intr_mask = 0x77; /* FLAG | EOL for PB, CP, FM */
+
+ err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcms[0] = pcm;
+ init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0);
+ init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 1);
+
+ if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
+ return err;
+
+ return 0;
+}
+
+
+/*
+ * Mixer part
+ */
+
+static int snd_via8233_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ /* formerly they were "Line" and "Mic", but it looks like that they
+ * have nothing to do with the actual physical connections...
+ */
+ static char *texts[2] = {
+ "Input1", "Input2"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= 2)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_via8233_capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL);
+ ucontrol->value.enumerated.item[0] = inb(port) & VIA_REG_CAPTURE_CHANNEL_MIC ? 1 : 0;
+ return 0;
+}
+
+static int snd_via8233_capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL);
+ u8 val, oval;
+
+ spin_lock_irq(&chip->reg_lock);
+ oval = inb(port);
+ val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC;
+ if (ucontrol->value.enumerated.item[0])
+ val |= VIA_REG_CAPTURE_CHANNEL_MIC;
+ if (val != oval)
+ outb(val, port);
+ spin_unlock_irq(&chip->reg_lock);
+ return val != oval;
+}
+
+static snd_kcontrol_new_t snd_via8233_capture_source __devinitdata = {
+ .name = "Input Source Select",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_via8233_capture_source_info,
+ .get = snd_via8233_capture_source_get,
+ .put = snd_via8233_capture_source_put,
+};
+
+static int snd_via8233_dxs3_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_via8233_dxs3_spdif_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+ u8 val;
+
+ pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val);
+ ucontrol->value.integer.value[0] = (val & VIA8233_SPDIF_DX3) ? 1 : 0;
+ return 0;
+}
+
+static int snd_via8233_dxs3_spdif_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+ u8 val, oval;
+
+ pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &oval);
+ val = oval & ~VIA8233_SPDIF_DX3;
+ if (ucontrol->value.integer.value[0])
+ val |= VIA8233_SPDIF_DX3;
+ /* save the spdif flag for rate filtering */
+ chip->spdif_on = ucontrol->value.integer.value[0] ? 1 : 0;
+ if (val != oval) {
+ pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val);
+ return 1;
+ }
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_via8233_dxs3_spdif_control __devinitdata = {
+ .name = "IEC958 Output Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_via8233_dxs3_spdif_info,
+ .get = snd_via8233_dxs3_spdif_get,
+ .put = snd_via8233_dxs3_spdif_put,
+};
+
+static int snd_via8233_dxs_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = VIA_DXS_MAX_VOLUME;
+ return 0;
+}
+
+static int snd_via8233_dxs_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume[0];
+ ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume[1];
+ return 0;
+}
+
+static int snd_via8233_dxs_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int idx;
+ unsigned char val;
+ int i, change = 0;
+
+ for (i = 0; i < 2; i++) {
+ val = ucontrol->value.integer.value[i];
+ if (val > VIA_DXS_MAX_VOLUME)
+ val = VIA_DXS_MAX_VOLUME;
+ val = VIA_DXS_MAX_VOLUME - val;
+ if (val != chip->playback_volume[i]) {
+ change = 1;
+ chip->playback_volume[i] = val;
+ for (idx = 0; idx < 4; idx++) {
+ unsigned long port = chip->port + 0x10 * idx;
+ outb(val, port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+ }
+ }
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_via8233_dxs_volume_control __devinitdata = {
+ .name = "PCM Playback Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_via8233_dxs_volume_info,
+ .get = snd_via8233_dxs_volume_get,
+ .put = snd_via8233_dxs_volume_put,
+};
+
+/*
+ */
+
+static void snd_via82xx_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ via82xx_t *chip = bus->private_data;
+ chip->ac97_bus = NULL;
+}
+
+static void snd_via82xx_mixer_free_ac97(ac97_t *ac97)
+{
+ via82xx_t *chip = ac97->private_data;
+ chip->ac97 = NULL;
+}
+
+static struct ac97_quirk ac97_quirks[] = {
+ {
+ .vendor = 0x1106,
+ .device = 0x4161,
+ .codec_id = 0x56494161, /* VT1612A */
+ .name = "Soltek SL-75DRV5",
+ .type = AC97_TUNE_NONE
+ },
+ { /* FIXME: which codec? */
+ .vendor = 0x1106,
+ .device = 0x4161,
+ .name = "ASRock K7VT2",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1019,
+ .device = 0x0a81,
+ .name = "ECS K7VTA3",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1019,
+ .device = 0x0a85,
+ .name = "ECS L7VMM2",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .vendor = 0x1849,
+ .device = 0x3059,
+ .name = "ASRock K7VM2",
+ .type = AC97_TUNE_HP_ONLY /* VT1616 */
+ },
+ {
+ .vendor = 0x14cd,
+ .device = 0x7002,
+ .name = "Unknown",
+ .type = AC97_TUNE_ALC_JACK
+ },
+ {
+ .vendor = 0x1071,
+ .device = 0x8590,
+ .name = "Mitac Mobo",
+ .type = AC97_TUNE_ALC_JACK
+ },
+ {
+ .vendor = 0x161f,
+ .device = 0x202b,
+ .name = "Arima Notebook",
+ .type = AC97_TUNE_HP_ONLY,
+ },
+ { } /* terminator */
+};
+
+static int __devinit snd_via82xx_mixer_new(via82xx_t *chip, const char *quirk_override)
+{
+ ac97_template_t ac97;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_via82xx_codec_write,
+ .read = snd_via82xx_codec_read,
+ .wait = snd_via82xx_codec_wait,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ return err;
+ chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus;
+ chip->ac97_bus->clock = chip->ac97_clock;
+ chip->ac97_bus->shared_type = AC97_SHARED_TYPE_VIA;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_via82xx_mixer_free_ac97;
+ ac97.pci = chip->pci;
+ if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ return err;
+
+ snd_ac97_tune_hardware(chip->ac97, ac97_quirks, quirk_override);
+
+ if (chip->chip_type != TYPE_VIA686) {
+ /* use slot 10/11 */
+ snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4);
+ }
+
+ return 0;
+}
+
+#ifdef SUPPORT_JOYSTICK
+#define JOYSTICK_ADDR 0x200
+static int __devinit snd_via686_create_gameport(via82xx_t *chip, int dev, unsigned char *legacy)
+{
+ struct gameport *gp;
+ struct resource *r;
+
+ if (!joystick[dev])
+ return -ENODEV;
+
+ r = request_region(JOYSTICK_ADDR, 8, "VIA686 gameport");
+ if (!r) {
+ printk(KERN_WARNING "via82xx: cannot reserve joystick port 0x%#x\n", JOYSTICK_ADDR);
+ return -EBUSY;
+ }
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "via82xx: cannot allocate memory for gameport\n");
+ release_resource(r);
+ kfree_nocheck(r);
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "VIA686 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gp->io = JOYSTICK_ADDR;
+ gameport_set_port_data(gp, r);
+
+ /* Enable legacy joystick port */
+ *legacy |= VIA_FUNC_ENABLE_GAME;
+ pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, *legacy);
+
+ gameport_register_port(chip->gameport);
+
+ return 0;
+}
+
+static void snd_via686_free_gameport(via82xx_t *chip)
+{
+ if (chip->gameport) {
+ struct resource *r = gameport_get_port_data(chip->gameport);
+
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+ release_resource(r);
+ kfree_nocheck(r);
+ }
+}
+#else
+static inline int snd_via686_create_gameport(via82xx_t *chip, int dev, unsigned char *legacy)
+{
+ return -ENOSYS;
+}
+static inline void snd_via686_free_gameport(via82xx_t *chip) { }
+#endif
+
+
+/*
+ *
+ */
+
+static int __devinit snd_via8233_init_misc(via82xx_t *chip, int dev)
+{
+ int i, err, caps;
+ unsigned char val;
+
+ caps = chip->chip_type == TYPE_VIA8233A ? 1 : 2;
+ for (i = 0; i < caps; i++) {
+ snd_via8233_capture_source.index = i;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_capture_source, chip));
+ if (err < 0)
+ return err;
+ }
+ if (ac97_can_spdif(chip->ac97)) {
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip));
+ if (err < 0)
+ return err;
+ }
+ if (chip->chip_type != TYPE_VIA8233A) {
+ /* when no h/w PCM volume control is found, use DXS volume control
+ * as the PCM vol control
+ */
+ snd_ctl_elem_id_t sid;
+ memset(&sid, 0, sizeof(sid));
+ strcpy(sid.name, "PCM Playback Volume");
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ if (! snd_ctl_find_id(chip->card, &sid)) {
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* select spdif data slot 10/11 */
+ pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val);
+ val = (val & ~VIA8233_SPDIF_SLOT_MASK) | VIA8233_SPDIF_SLOT_1011;
+ val &= ~VIA8233_SPDIF_DX3; /* SPDIF off as default */
+ pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val);
+
+ return 0;
+}
+
+static int __devinit snd_via686_init_misc(via82xx_t *chip, int dev)
+{
+ unsigned char legacy, legacy_cfg;
+ int rev_h = 0;
+
+ legacy = chip->old_legacy;
+ legacy_cfg = chip->old_legacy_cfg;
+ legacy |= VIA_FUNC_MIDI_IRQMASK; /* FIXME: correct? (disable MIDI) */
+ legacy &= ~VIA_FUNC_ENABLE_GAME; /* disable joystick */
+ if (chip->revision >= VIA_REV_686_H) {
+ rev_h = 1;
+ if (mpu_port[dev] >= 0x200) { /* force MIDI */
+ mpu_port[dev] &= 0xfffc;
+ pci_write_config_dword(chip->pci, 0x18, mpu_port[dev] | 0x01);
+#ifdef CONFIG_PM
+ chip->mpu_port_saved = mpu_port[dev];
+#endif
+ } else {
+ mpu_port[dev] = pci_resource_start(chip->pci, 2);
+ }
+ } else {
+ switch (mpu_port[dev]) { /* force MIDI */
+ case 0x300:
+ case 0x310:
+ case 0x320:
+ case 0x330:
+ legacy_cfg &= ~(3 << 2);
+ legacy_cfg |= (mpu_port[dev] & 0x0030) >> 2;
+ break;
+ default: /* no, use BIOS settings */
+ if (legacy & VIA_FUNC_ENABLE_MIDI)
+ mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2);
+ break;
+ }
+ }
+ if (mpu_port[dev] >= 0x200 &&
+ (chip->mpu_res = request_region(mpu_port[dev], 2, "VIA82xx MPU401")) != NULL) {
+ if (rev_h)
+ legacy |= VIA_FUNC_MIDI_PNP; /* enable PCI I/O 2 */
+ legacy |= VIA_FUNC_ENABLE_MIDI;
+ } else {
+ if (rev_h)
+ legacy &= ~VIA_FUNC_MIDI_PNP; /* disable PCI I/O 2 */
+ legacy &= ~VIA_FUNC_ENABLE_MIDI;
+ mpu_port[dev] = 0;
+ }
+
+ pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy);
+ pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg);
+ if (chip->mpu_res) {
+ if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A,
+ mpu_port[dev], 1,
+ chip->irq, 0, &chip->rmidi) < 0) {
+ printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", mpu_port[dev]);
+ legacy &= ~VIA_FUNC_ENABLE_MIDI;
+ } else {
+ legacy &= ~VIA_FUNC_MIDI_IRQMASK; /* enable MIDI interrupt */
+ }
+ pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy);
+ }
+
+ snd_via686_create_gameport(chip, dev, &legacy);
+
+#ifdef CONFIG_PM
+ chip->legacy_saved = legacy;
+ chip->legacy_cfg_saved = legacy_cfg;
+#endif
+
+ return 0;
+}
+
+
+/*
+ * proc interface
+ */
+static void snd_via82xx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ via82xx_t *chip = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "%s\n\n", chip->card->longname);
+ for (i = 0; i < 0xa0; i += 4) {
+ snd_iprintf(buffer, "%02x: %08x\n", i, inl(chip->port + i));
+ }
+}
+
+static void __devinit snd_via82xx_proc_init(via82xx_t *chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(chip->card, "via82xx", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read);
+}
+
+/*
+ *
+ */
+
+static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
+{
+ unsigned int val;
+ int max_count;
+ unsigned char pval;
+
+#if 0 /* broken on K7M? */
+ if (chip->chip_type == TYPE_VIA686)
+ /* disable all legacy ports */
+ pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, 0);
+#endif
+ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+ if (! (pval & VIA_ACLINK_C00_READY)) { /* codec not ready? */
+ /* deassert ACLink reset, force SYNC */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+ VIA_ACLINK_CTRL_ENABLE |
+ VIA_ACLINK_CTRL_RESET |
+ VIA_ACLINK_CTRL_SYNC);
+ udelay(100);
+#if 1 /* FIXME: should we do full reset here for all chip models? */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, 0x00);
+ udelay(100);
+#else
+ /* deassert ACLink reset, force SYNC (warm AC'97 reset) */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+ VIA_ACLINK_CTRL_RESET|VIA_ACLINK_CTRL_SYNC);
+ udelay(2);
+#endif
+ /* ACLink on, deassert ACLink reset, VSR, SGD data out */
+ /* note - FM data out has trouble with non VRA codecs !! */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+ udelay(100);
+ }
+
+ /* Make sure VRA is enabled, in case we didn't do a
+ * complete codec reset, above */
+ pci_read_config_byte(chip->pci, VIA_ACLINK_CTRL, &pval);
+ if ((pval & VIA_ACLINK_CTRL_INIT) != VIA_ACLINK_CTRL_INIT) {
+ /* ACLink on, deassert ACLink reset, VSR, SGD data out */
+ /* note - FM data out has trouble with non VRA codecs !! */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+ udelay(100);
+ }
+
+ /* wait until codec ready */
+ max_count = ((3 * HZ) / 4) + 1;
+ do {
+ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+ if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
+ break;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (--max_count > 0);
+
+ if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+ snd_printk("AC'97 codec is not ready [0x%x]\n", val);
+
+#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */
+ snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+ VIA_REG_AC97_SECONDARY_VALID |
+ (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+ max_count = ((3 * HZ) / 4) + 1;
+ snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+ VIA_REG_AC97_SECONDARY_VALID |
+ (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+ do {
+ if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+ chip->ac97_secondary = 1;
+ goto __ac97_ok2;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (--max_count > 0);
+ /* This is ok, the most of motherboards have only one codec */
+
+ __ac97_ok2:
+#endif
+
+ if (chip->chip_type == TYPE_VIA686) {
+ /* route FM trap to IRQ, disable FM trap */
+ pci_write_config_byte(chip->pci, VIA_FM_NMI_CTRL, 0);
+ /* disable all GPI interrupts */
+ outl(0, VIAREG(chip, GPI_INTR));
+ }
+
+ if (chip->chip_type != TYPE_VIA686) {
+ /* Workaround for Award BIOS bug:
+ * DXS channels don't work properly with VRA if MC97 is disabled.
+ */
+ struct pci_dev *pci;
+ pci = pci_find_device(0x1106, 0x3068, NULL); /* MC97 */
+ if (pci) {
+ unsigned char data;
+ pci_read_config_byte(pci, 0x44, &data);
+ pci_write_config_byte(pci, 0x44, data | 0x40);
+ }
+ }
+
+ if (chip->chip_type != TYPE_VIA8233A) {
+ int i, idx;
+ for (idx = 0; idx < 4; idx++) {
+ unsigned long port = chip->port + 0x10 * idx;
+ for (i = 0; i < 2; i++)
+ outb(chip->playback_volume[i], port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int snd_via82xx_suspend(snd_card_t *card, pm_message_t state)
+{
+ via82xx_t *chip = card->pm_private_data;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (chip->pcms[i])
+ snd_pcm_suspend_all(chip->pcms[i]);
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+ synchronize_irq(chip->irq);
+ snd_ac97_suspend(chip->ac97);
+
+ /* save misc values */
+ if (chip->chip_type != TYPE_VIA686) {
+ pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &chip->spdif_ctrl_saved);
+ chip->capture_src_saved[0] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL);
+ chip->capture_src_saved[1] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10);
+ }
+
+ pci_set_power_state(chip->pci, 3);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int snd_via82xx_resume(snd_card_t *card)
+{
+ via82xx_t *chip = card->pm_private_data;
+ int i;
+
+ pci_enable_device(chip->pci);
+ pci_set_power_state(chip->pci, 0);
+
+ snd_via82xx_chip_init(chip);
+
+ if (chip->chip_type == TYPE_VIA686) {
+ if (chip->mpu_port_saved)
+ pci_write_config_dword(chip->pci, 0x18, chip->mpu_port_saved | 0x01);
+ pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->legacy_saved);
+ pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->legacy_cfg_saved);
+ } else {
+ pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, chip->spdif_ctrl_saved);
+ outb(chip->capture_src_saved[0], chip->port + VIA_REG_CAPTURE_CHANNEL);
+ outb(chip->capture_src_saved[1], chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10);
+ }
+
+ snd_ac97_resume(chip->ac97);
+
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_via82xx_free(via82xx_t *chip)
+{
+ unsigned int i;
+
+ if (chip->irq < 0)
+ goto __end_hw;
+ /* disable interrupts */
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+ synchronize_irq(chip->irq);
+ __end_hw:
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->mpu_res) {
+ release_resource(chip->mpu_res);
+ kfree_nocheck(chip->mpu_res);
+ }
+ pci_release_regions(chip->pci);
+
+ if (chip->chip_type == TYPE_VIA686) {
+ snd_via686_free_gameport(chip);
+ pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->old_legacy);
+ pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->old_legacy_cfg);
+ }
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_via82xx_dev_free(snd_device_t *device)
+{
+ via82xx_t *chip = device->device_data;
+ return snd_via82xx_free(chip);
+}
+
+static int __devinit snd_via82xx_create(snd_card_t * card,
+ struct pci_dev *pci,
+ int chip_type,
+ int revision,
+ unsigned int ac97_clock,
+ via82xx_t ** r_via)
+{
+ via82xx_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_via82xx_dev_free,
+ };
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ if ((chip = kcalloc(1, sizeof(*chip), GFP_KERNEL)) == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ chip->chip_type = chip_type;
+ chip->revision = revision;
+
+ spin_lock_init(&chip->reg_lock);
+ spin_lock_init(&chip->rates[0].lock);
+ spin_lock_init(&chip->rates[1].lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ pci_read_config_byte(pci, VIA_FUNC_ENABLE, &chip->old_legacy);
+ pci_read_config_byte(pci, VIA_PNP_CONTROL, &chip->old_legacy_cfg);
+ pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE,
+ chip->old_legacy & ~(VIA_FUNC_ENABLE_SB|VIA_FUNC_ENABLE_FM));
+
+ if ((err = pci_request_regions(pci, card->driver)) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->port = pci_resource_start(pci, 0);
+ if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ card->driver, (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_via82xx_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ if (ac97_clock >= 8000 && ac97_clock <= 48000)
+ chip->ac97_clock = ac97_clock;
+ synchronize_irq(chip->irq);
+
+ if ((err = snd_via82xx_chip_init(chip)) < 0) {
+ snd_via82xx_free(chip);
+ return err;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_via82xx_free(chip);
+ return err;
+ }
+
+ /* The 8233 ac97 controller does not implement the master bit
+ * in the pci command register. IMHO this is a violation of the PCI spec.
+ * We call pci_set_master here because it does not hurt. */
+ pci_set_master(pci);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_via = chip;
+ return 0;
+}
+
+struct via823x_info {
+ int revision;
+ char *name;
+ int type;
+};
+static struct via823x_info via823x_cards[] __devinitdata = {
+ { VIA_REV_PRE_8233, "VIA 8233-Pre", TYPE_VIA8233 },
+ { VIA_REV_8233C, "VIA 8233C", TYPE_VIA8233 },
+ { VIA_REV_8233, "VIA 8233", TYPE_VIA8233 },
+ { VIA_REV_8233A, "VIA 8233A", TYPE_VIA8233A },
+ { VIA_REV_8235, "VIA 8235", TYPE_VIA8233 },
+ { VIA_REV_8237, "VIA 8237", TYPE_VIA8233 },
+};
+
+/*
+ * auto detection of DXS channel supports.
+ */
+struct dxs_whitelist {
+ unsigned short vendor;
+ unsigned short device;
+ unsigned short mask;
+ short action; /* new dxs_support value */
+};
+
+static int __devinit check_dxs_list(struct pci_dev *pci)
+{
+ static struct dxs_whitelist whitelist[] = {
+ { .vendor = 0x1005, .device = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */
+ { .vendor = 0x1019, .device = 0x0996, .action = VIA_DXS_48K },
+ { .vendor = 0x1019, .device = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */
+ { .vendor = 0x1019, .device = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */
+ { .vendor = 0x1025, .device = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */
+ { .vendor = 0x1043, .device = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/
+ { .vendor = 0x1043, .device = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */
+ { .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/
+ { .vendor = 0x1071, .device = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */
+ { .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */
+ { .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
+ { .vendor = 0x1106, .device = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */
+ { .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */
+ { .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
+ { .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
+ { .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
+ { .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
+ { .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
+ { .vendor = 0x1462, .device = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */
+ { .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
+ { .vendor = 0x147b, .device = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */
+ { .vendor = 0x147b, .device = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */
+ { .vendor = 0x147b, .device = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */
+ { .vendor = 0x147b, .device = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */
+ { .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
+ { .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
+ { .vendor = 0x1584, .device = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */
+ { .vendor = 0x161f, .device = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */
+ { .vendor = 0x161f, .device = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */
+ { .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
+ { .vendor = 0x1695, .device = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
+ { .vendor = 0x1849, .device = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
+ { } /* terminator */
+ };
+ struct dxs_whitelist *w;
+ unsigned short subsystem_vendor;
+ unsigned short subsystem_device;
+
+ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+
+ for (w = whitelist; w->vendor; w++) {
+ if (w->vendor != subsystem_vendor)
+ continue;
+ if (w->mask) {
+ if ((w->mask & subsystem_device) == w->device)
+ return w->action;
+ } else {
+ if (subsystem_device == w->device)
+ return w->action;
+ }
+ }
+
+ /*
+ * not detected, try 48k rate only to be sure.
+ */
+ printk(KERN_INFO "via82xx: Assuming DXS channels with 48k fixed sample rate.\n");
+ printk(KERN_INFO " Please try dxs_support=1 or dxs_support=4 option\n");
+ printk(KERN_INFO " and report if it works on your machine.\n");
+ return VIA_DXS_48K;
+};
+
+static int __devinit snd_via82xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ via82xx_t *chip;
+ unsigned char revision;
+ int chip_type = 0, card_type;
+ unsigned int i;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ card_type = pci_id->driver_data;
+ pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+ switch (card_type) {
+ case TYPE_CARD_VIA686:
+ strcpy(card->driver, "VIA686A");
+ sprintf(card->shortname, "VIA 82C686A/B rev%x", revision);
+ chip_type = TYPE_VIA686;
+ break;
+ case TYPE_CARD_VIA8233:
+ chip_type = TYPE_VIA8233;
+ sprintf(card->shortname, "VIA 823x rev%x", revision);
+ for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) {
+ if (revision == via823x_cards[i].revision) {
+ chip_type = via823x_cards[i].type;
+ strcpy(card->shortname, via823x_cards[i].name);
+ break;
+ }
+ }
+ if (chip_type != TYPE_VIA8233A) {
+ if (dxs_support[dev] == VIA_DXS_AUTO)
+ dxs_support[dev] = check_dxs_list(pci);
+ /* force to use VIA8233 or 8233A model according to
+ * dxs_support module option
+ */
+ if (dxs_support[dev] == VIA_DXS_DISABLE)
+ chip_type = TYPE_VIA8233A;
+ else
+ chip_type = TYPE_VIA8233;
+ }
+ if (chip_type == TYPE_VIA8233A)
+ strcpy(card->driver, "VIA8233A");
+ else if (revision >= VIA_REV_8237)
+ strcpy(card->driver, "VIA8237"); /* no slog assignment */
+ else
+ strcpy(card->driver, "VIA8233");
+ break;
+ default:
+ snd_printk(KERN_ERR "invalid card type %d\n", card_type);
+ err = -EINVAL;
+ goto __error;
+ }
+
+ if ((err = snd_via82xx_create(card, pci, chip_type, revision, ac97_clock[dev], &chip)) < 0)
+ goto __error;
+ if ((err = snd_via82xx_mixer_new(chip, ac97_quirk[dev])) < 0)
+ goto __error;
+
+ if (chip_type == TYPE_VIA686) {
+ if ((err = snd_via686_pcm_new(chip)) < 0 ||
+ (err = snd_via686_init_misc(chip, dev)) < 0)
+ goto __error;
+ } else {
+ if (chip_type == TYPE_VIA8233A) {
+ if ((err = snd_via8233a_pcm_new(chip)) < 0)
+ goto __error;
+ // chip->dxs_fixed = 1; /* FIXME: use 48k for DXS #3? */
+ } else {
+ if ((err = snd_via8233_pcm_new(chip)) < 0)
+ goto __error;
+ if (dxs_support[dev] == VIA_DXS_48K)
+ chip->dxs_fixed = 1;
+ else if (dxs_support[dev] == VIA_DXS_NO_VRA)
+ chip->no_vra = 1;
+ }
+ if ((err = snd_via8233_init_misc(chip, dev)) < 0)
+ goto __error;
+ }
+
+ snd_card_set_pm_callback(card, snd_via82xx_suspend, snd_via82xx_resume, chip);
+
+ /* disable interrupts */
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+ snprintf(card->longname, sizeof(card->longname),
+ "%s with %s at %#lx, irq %d", card->shortname,
+ snd_ac97_get_short_name(chip->ac97), chip->port, chip->irq);
+
+ snd_via82xx_proc_init(chip);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+
+ __error:
+ snd_card_free(card);
+ return err;
+}
+
+static void __devexit snd_via82xx_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "VIA 82xx Audio",
+ .id_table = snd_via82xx_ids,
+ .probe = snd_via82xx_probe,
+ .remove = __devexit_p(snd_via82xx_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_via82xx_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_via82xx_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via82xx_init)
+module_exit(alsa_card_via82xx_exit)
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
new file mode 100644
index 00000000000..ea5c6f64015
--- /dev/null
+++ b/sound/pci/via82xx_modem.c
@@ -0,0 +1,1245 @@
+/*
+ * ALSA modem driver for VIA VT82xx (South Bridge)
+ *
+ * VT82C686A/B/C, VT8233A/C, VT8235
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ * Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
+ * 2002 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Changes:
+ *
+ * Sep. 2, 2004 Sasha Khapyorsky <sashak@smlink.com>
+ * Modified from original audio driver 'via82xx.c' to support AC97
+ * modems.
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#if 0
+#define POINTER_DEBUG
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA VT82xx modem");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C modem,pci}}");
+
+static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable modem part of VIA 82xx bridge.");
+module_param_array(ac97_clock, int, NULL, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
+
+
+/*
+ * Direct registers
+ */
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x)
+
+/* common offsets */
+#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */
+#define VIA_REG_STAT_ACTIVE 0x80 /* RO */
+#define VIA_REG_STAT_PAUSED 0x40 /* RO */
+#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */
+#define VIA_REG_STAT_STOPPED 0x04 /* RWC */
+#define VIA_REG_STAT_EOL 0x02 /* RWC */
+#define VIA_REG_STAT_FLAG 0x01 /* RWC */
+#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */
+#define VIA_REG_CTRL_START 0x80 /* WO */
+#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */
+#define VIA_REG_CTRL_AUTOSTART 0x20
+#define VIA_REG_CTRL_PAUSE 0x08 /* RW */
+#define VIA_REG_CTRL_INT_STOP 0x04
+#define VIA_REG_CTRL_INT_EOL 0x02
+#define VIA_REG_CTRL_INT_FLAG 0x01
+#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */
+#define VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
+#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type (686 only) */
+#define VIA_REG_TYPE_AUTOSTART 0x80 /* RW - autostart at EOL */
+#define VIA_REG_TYPE_16BIT 0x20 /* RW */
+#define VIA_REG_TYPE_STEREO 0x10 /* RW */
+#define VIA_REG_TYPE_INT_LLINE 0x00
+#define VIA_REG_TYPE_INT_LSAMPLE 0x04
+#define VIA_REG_TYPE_INT_LESSONE 0x08
+#define VIA_REG_TYPE_INT_MASK 0x0c
+#define VIA_REG_TYPE_INT_EOL 0x02
+#define VIA_REG_TYPE_INT_FLAG 0x01
+#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */
+#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */
+#define VIA_REG_OFFSET_STOP_IDX 0x08 /* dword - stop index, channel type, sample rate */
+#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */
+#define VIA_REG_OFFSET_CURR_INDEX 0x0f /* byte - channel current index (for via8233 only) */
+
+#define DEFINE_VIA_REGSET(name,val) \
+enum {\
+ VIA_REG_##name##_STATUS = (val),\
+ VIA_REG_##name##_CONTROL = (val) + 0x01,\
+ VIA_REG_##name##_TYPE = (val) + 0x02,\
+ VIA_REG_##name##_TABLE_PTR = (val) + 0x04,\
+ VIA_REG_##name##_CURR_PTR = (val) + 0x04,\
+ VIA_REG_##name##_STOP_IDX = (val) + 0x08,\
+ VIA_REG_##name##_CURR_COUNT = (val) + 0x0c,\
+}
+
+/* modem block */
+DEFINE_VIA_REGSET(MO, 0x40);
+DEFINE_VIA_REGSET(MI, 0x50);
+
+/* AC'97 */
+#define VIA_REG_AC97 0x80 /* dword */
+#define VIA_REG_AC97_CODEC_ID_MASK (3<<30)
+#define VIA_REG_AC97_CODEC_ID_SHIFT 30
+#define VIA_REG_AC97_CODEC_ID_PRIMARY 0x00
+#define VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
+#define VIA_REG_AC97_SECONDARY_VALID (1<<27)
+#define VIA_REG_AC97_PRIMARY_VALID (1<<25)
+#define VIA_REG_AC97_BUSY (1<<24)
+#define VIA_REG_AC97_READ (1<<23)
+#define VIA_REG_AC97_CMD_SHIFT 16
+#define VIA_REG_AC97_CMD_MASK 0x7e
+#define VIA_REG_AC97_DATA_SHIFT 0
+#define VIA_REG_AC97_DATA_MASK 0xffff
+
+#define VIA_REG_SGD_SHADOW 0x84 /* dword */
+#define VIA_REG_SGD_STAT_PB_FLAG (1<<0)
+#define VIA_REG_SGD_STAT_CP_FLAG (1<<1)
+#define VIA_REG_SGD_STAT_FM_FLAG (1<<2)
+#define VIA_REG_SGD_STAT_PB_EOL (1<<4)
+#define VIA_REG_SGD_STAT_CP_EOL (1<<5)
+#define VIA_REG_SGD_STAT_FM_EOL (1<<6)
+#define VIA_REG_SGD_STAT_PB_STOP (1<<8)
+#define VIA_REG_SGD_STAT_CP_STOP (1<<9)
+#define VIA_REG_SGD_STAT_FM_STOP (1<<10)
+#define VIA_REG_SGD_STAT_PB_ACTIVE (1<<12)
+#define VIA_REG_SGD_STAT_CP_ACTIVE (1<<13)
+#define VIA_REG_SGD_STAT_FM_ACTIVE (1<<14)
+#define VIA_REG_SGD_STAT_MR_FLAG (1<<16)
+#define VIA_REG_SGD_STAT_MW_FLAG (1<<17)
+#define VIA_REG_SGD_STAT_MR_EOL (1<<20)
+#define VIA_REG_SGD_STAT_MW_EOL (1<<21)
+#define VIA_REG_SGD_STAT_MR_STOP (1<<24)
+#define VIA_REG_SGD_STAT_MW_STOP (1<<25)
+#define VIA_REG_SGD_STAT_MR_ACTIVE (1<<28)
+#define VIA_REG_SGD_STAT_MW_ACTIVE (1<<29)
+
+#define VIA_REG_GPI_STATUS 0x88
+#define VIA_REG_GPI_INTR 0x8c
+
+#define VIA_TBL_BIT_FLAG 0x40000000
+#define VIA_TBL_BIT_EOL 0x80000000
+
+/* pci space */
+#define VIA_ACLINK_STAT 0x40
+#define VIA_ACLINK_C11_READY 0x20
+#define VIA_ACLINK_C10_READY 0x10
+#define VIA_ACLINK_C01_READY 0x04 /* secondary codec ready */
+#define VIA_ACLINK_LOWPOWER 0x02 /* low-power state */
+#define VIA_ACLINK_C00_READY 0x01 /* primary codec ready */
+#define VIA_ACLINK_CTRL 0x41
+#define VIA_ACLINK_CTRL_ENABLE 0x80 /* 0: disable, 1: enable */
+#define VIA_ACLINK_CTRL_RESET 0x40 /* 0: assert, 1: de-assert */
+#define VIA_ACLINK_CTRL_SYNC 0x20 /* 0: release SYNC, 1: force SYNC hi */
+#define VIA_ACLINK_CTRL_SDO 0x10 /* 0: release SDO, 1: force SDO hi */
+#define VIA_ACLINK_CTRL_VRA 0x08 /* 0: disable VRA, 1: enable VRA */
+#define VIA_ACLINK_CTRL_PCM 0x04 /* 0: disable PCM, 1: enable PCM */
+#define VIA_ACLINK_CTRL_FM 0x02 /* via686 only */
+#define VIA_ACLINK_CTRL_SB 0x01 /* via686 only */
+#define VIA_ACLINK_CTRL_INIT (VIA_ACLINK_CTRL_ENABLE|\
+ VIA_ACLINK_CTRL_RESET|\
+ VIA_ACLINK_CTRL_PCM)
+#define VIA_FUNC_ENABLE 0x42
+#define VIA_FUNC_MIDI_PNP 0x80 /* FIXME: it's 0x40 in the datasheet! */
+#define VIA_FUNC_MIDI_IRQMASK 0x40 /* FIXME: not documented! */
+#define VIA_FUNC_RX2C_WRITE 0x20
+#define VIA_FUNC_SB_FIFO_EMPTY 0x10
+#define VIA_FUNC_ENABLE_GAME 0x08
+#define VIA_FUNC_ENABLE_FM 0x04
+#define VIA_FUNC_ENABLE_MIDI 0x02
+#define VIA_FUNC_ENABLE_SB 0x01
+#define VIA_PNP_CONTROL 0x43
+#define VIA_MC97_CTRL 0x44
+#define VIA_MC97_CTRL_ENABLE 0x80
+#define VIA_MC97_CTRL_SECONDARY 0x40
+#define VIA_MC97_CTRL_INIT (VIA_MC97_CTRL_ENABLE|\
+ VIA_MC97_CTRL_SECONDARY)
+
+
+typedef struct _snd_via82xx_modem via82xx_t;
+typedef struct via_dev viadev_t;
+
+/*
+ * pcm stream
+ */
+
+struct snd_via_sg_table {
+ unsigned int offset;
+ unsigned int size;
+} ;
+
+#define VIA_TABLE_SIZE 255
+
+struct via_dev {
+ unsigned int reg_offset;
+ unsigned long port;
+ int direction; /* playback = 0, capture = 1 */
+ snd_pcm_substream_t *substream;
+ int running;
+ unsigned int tbl_entries; /* # descriptors */
+ struct snd_dma_buffer table;
+ struct snd_via_sg_table *idx_table;
+ /* for recovery from the unexpected pointer */
+ unsigned int lastpos;
+ unsigned int bufsize;
+ unsigned int bufsize2;
+};
+
+enum { TYPE_CARD_VIA82XX_MODEM = 1 };
+
+#define VIA_MAX_MODEM_DEVS 2
+
+struct _snd_via82xx_modem {
+ int irq;
+
+ unsigned long port;
+
+ unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */
+
+ struct pci_dev *pci;
+ snd_card_t *card;
+
+ unsigned int num_devs;
+ unsigned int playback_devno, capture_devno;
+ viadev_t devs[VIA_MAX_MODEM_DEVS];
+
+ snd_pcm_t *pcms[2];
+
+ ac97_bus_t *ac97_bus;
+ ac97_t *ac97;
+ unsigned int ac97_clock;
+ unsigned int ac97_secondary; /* secondary AC'97 codec is present */
+
+ spinlock_t reg_lock;
+ snd_info_entry_t *proc_entry;
+};
+
+static struct pci_device_id snd_via82xx_modem_ids[] = {
+ { 0x1106, 0x3068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA82XX_MODEM, },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via82xx_modem_ids);
+
+/*
+ */
+
+/*
+ * allocate and initialize the descriptor buffers
+ * periods = number of periods
+ * fragsize = period size in bytes
+ */
+static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+ struct pci_dev *pci,
+ unsigned int periods, unsigned int fragsize)
+{
+ unsigned int i, idx, ofs, rest;
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+
+ if (dev->table.area == NULL) {
+ /* the start of each lists must be aligned to 8 bytes,
+ * but the kernel pages are much bigger, so we don't care
+ */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
+ &dev->table) < 0)
+ return -ENOMEM;
+ }
+ if (! dev->idx_table) {
+ dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
+ if (! dev->idx_table)
+ return -ENOMEM;
+ }
+
+ /* fill the entries */
+ idx = 0;
+ ofs = 0;
+ for (i = 0; i < periods; i++) {
+ rest = fragsize;
+ /* fill descriptors for a period.
+ * a period can be split to several descriptors if it's
+ * over page boundary.
+ */
+ do {
+ unsigned int r;
+ unsigned int flag;
+
+ if (idx >= VIA_TABLE_SIZE) {
+ snd_printk(KERN_ERR "via82xx: too much table size!\n");
+ return -EINVAL;
+ }
+ ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
+ r = PAGE_SIZE - (ofs % PAGE_SIZE);
+ if (rest < r)
+ r = rest;
+ rest -= r;
+ if (! rest) {
+ if (i == periods - 1)
+ flag = VIA_TBL_BIT_EOL; /* buffer boundary */
+ else
+ flag = VIA_TBL_BIT_FLAG; /* period boundary */
+ } else
+ flag = 0; /* period continues to the next */
+ // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest);
+ ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
+ dev->idx_table[idx].offset = ofs;
+ dev->idx_table[idx].size = r;
+ ofs += r;
+ idx++;
+ } while (rest > 0);
+ }
+ dev->tbl_entries = idx;
+ dev->bufsize = periods * fragsize;
+ dev->bufsize2 = dev->bufsize / 2;
+ return 0;
+}
+
+
+static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+ struct pci_dev *pci)
+{
+ if (dev->table.area) {
+ snd_dma_free_pages(&dev->table);
+ dev->table.area = NULL;
+ }
+ if (dev->idx_table) {
+ kfree(dev->idx_table);
+ dev->idx_table = NULL;
+ }
+ return 0;
+}
+
+/*
+ * Basic I/O
+ */
+
+static inline unsigned int snd_via82xx_codec_xread(via82xx_t *chip)
+{
+ return inl(VIAREG(chip, AC97));
+}
+
+static inline void snd_via82xx_codec_xwrite(via82xx_t *chip, unsigned int val)
+{
+ outl(val, VIAREG(chip, AC97));
+}
+
+static int snd_via82xx_codec_ready(via82xx_t *chip, int secondary)
+{
+ unsigned int timeout = 1000; /* 1ms */
+ unsigned int val;
+
+ while (timeout-- > 0) {
+ udelay(1);
+ if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+ return val & 0xffff;
+ }
+ snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip));
+ return -EIO;
+}
+
+static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary)
+{
+ unsigned int timeout = 1000; /* 1ms */
+ unsigned int val, val1;
+ unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
+ VIA_REG_AC97_SECONDARY_VALID;
+
+ while (timeout-- > 0) {
+ val = snd_via82xx_codec_xread(chip);
+ val1 = val & (VIA_REG_AC97_BUSY | stat);
+ if (val1 == stat)
+ return val & 0xffff;
+ udelay(1);
+ }
+ return -EIO;
+}
+
+static void snd_via82xx_codec_wait(ac97_t *ac97)
+{
+ via82xx_t *chip = ac97->private_data;
+ int err;
+ err = snd_via82xx_codec_ready(chip, ac97->num);
+ /* here we need to wait fairly for long time.. */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+}
+
+static void snd_via82xx_codec_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ via82xx_t *chip = ac97->private_data;
+ unsigned int xval;
+
+ xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
+ xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
+ xval |= reg << VIA_REG_AC97_CMD_SHIFT;
+ xval |= val << VIA_REG_AC97_DATA_SHIFT;
+ snd_via82xx_codec_xwrite(chip, xval);
+ snd_via82xx_codec_ready(chip, ac97->num);
+}
+
+static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg)
+{
+ via82xx_t *chip = ac97->private_data;
+ unsigned int xval, val = 0xffff;
+ int again = 0;
+
+ xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT;
+ xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
+ xval |= VIA_REG_AC97_READ;
+ xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
+ while (1) {
+ if (again++ > 3) {
+ snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n", ac97->num, snd_via82xx_codec_xread(chip));
+ return 0xffff;
+ }
+ snd_via82xx_codec_xwrite(chip, xval);
+ udelay (20);
+ if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) {
+ udelay(25);
+ val = snd_via82xx_codec_xread(chip);
+ break;
+ }
+ }
+ return val & 0xffff;
+}
+
+static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev)
+{
+ outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET,
+ VIADEV_REG(viadev, OFFSET_CONTROL));
+ inb(VIADEV_REG(viadev, OFFSET_CONTROL));
+ udelay(50);
+ /* disable interrupts */
+ outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL));
+ /* clear interrupts */
+ outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS));
+ outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */
+ // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR));
+ viadev->lastpos = 0;
+}
+
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ via82xx_t *chip = dev_id;
+ unsigned int status;
+ unsigned int i;
+
+ status = inl(VIAREG(chip, SGD_SHADOW));
+ if (! (status & chip->intr_mask)) {
+ return IRQ_NONE;
+ }
+// _skip_sgd:
+
+ /* check status for each stream */
+ spin_lock(&chip->reg_lock);
+ for (i = 0; i < chip->num_devs; i++) {
+ viadev_t *viadev = &chip->devs[i];
+ unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
+ c_status &= (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED);
+ if (! c_status)
+ continue;
+ if (viadev->substream && viadev->running) {
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(viadev->substream);
+ spin_lock(&chip->reg_lock);
+ }
+ outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
+ }
+ spin_unlock(&chip->reg_lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * PCM callbacks
+ */
+
+/*
+ * trigger callback
+ */
+static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ unsigned char val = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ val |= VIA_REG_CTRL_START;
+ viadev->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ val = VIA_REG_CTRL_TERMINATE;
+ viadev->running = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val |= VIA_REG_CTRL_PAUSE;
+ viadev->running = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ viadev->running = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ outb(val, VIADEV_REG(viadev, OFFSET_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_STOP)
+ snd_via82xx_channel_reset(chip, viadev);
+ return 0;
+}
+
+static int snd_via82xx_modem_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int val = 0;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS);
+ outl(val|AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS);
+ outl(val&~AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS));
+ break;
+ default:
+ break;
+ }
+ return snd_via82xx_pcm_trigger(substream, cmd);
+}
+
+/*
+ * pointer callbacks
+ */
+
+/*
+ * calculate the linear position at the given sg-buffer index and the rest count
+ */
+
+#define check_invalid_pos(viadev,pos) \
+ ((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))
+
+static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, unsigned int count)
+{
+ unsigned int size, res;
+
+ size = viadev->idx_table[idx].size;
+ res = viadev->idx_table[idx].offset + size - count;
+
+ /* check the validity of the calculated position */
+ if (size < count) {
+ snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n", (int)size, (int)count);
+ res = viadev->lastpos;
+ } else if (check_invalid_pos(viadev, res)) {
+#ifdef POINTER_DEBUG
+ printk("fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count);
+#endif
+ if (count && size < count) {
+ snd_printd(KERN_ERR "invalid via82xx_cur_ptr, using last valid pointer\n");
+ res = viadev->lastpos;
+ } else {
+ if (! count)
+ /* bogus count 0 on the DMA boundary? */
+ res = viadev->idx_table[idx].offset;
+ else
+ /* count register returns full size when end of buffer is reached */
+ res = viadev->idx_table[idx].offset + size;
+ if (check_invalid_pos(viadev, res)) {
+ snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), using last valid pointer\n");
+ res = viadev->lastpos;
+ }
+ }
+ }
+ viadev->lastpos = res; /* remember the last position */
+ if (res >= viadev->bufsize)
+ res -= viadev->bufsize;
+ return res;
+}
+
+/*
+ * get the current pointer on via686
+ */
+static snd_pcm_uframes_t snd_via686_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ unsigned int idx, ptr, count, res;
+
+ snd_assert(viadev->tbl_entries, return 0);
+ if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
+ return 0;
+
+ spin_lock(&chip->reg_lock);
+ count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff;
+ /* The via686a does not have the current index register,
+ * so we need to calculate the index from CURR_PTR.
+ */
+ ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR));
+ if (ptr <= (unsigned int)viadev->table.addr)
+ idx = 0;
+ else /* CURR_PTR holds the address + 8 */
+ idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;
+ res = calc_linear_pos(viadev, idx, count);
+ spin_unlock(&chip->reg_lock);
+
+ return bytes_to_frames(substream->runtime, res);
+}
+
+/*
+ * hw_params callback:
+ * allocate the buffer and build up the buffer description table
+ */
+static int snd_via82xx_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+ int err;
+
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+ err = build_via_table(viadev, substream, chip->pci,
+ params_periods(hw_params),
+ params_period_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ snd_ac97_write(chip->ac97, AC97_LINE1_RATE, params_rate(hw_params));
+ snd_ac97_write(chip->ac97, AC97_LINE1_LEVEL, 0);
+
+ return 0;
+}
+
+/*
+ * hw_free callback:
+ * clean up the buffer description table and release the buffer
+ */
+static int snd_via82xx_hw_free(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+
+ clean_via_table(viadev, substream, chip->pci);
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+
+/*
+ * set up the table pointer
+ */
+static void snd_via82xx_set_table_ptr(via82xx_t *chip, viadev_t *viadev)
+{
+ snd_via82xx_codec_ready(chip, chip->ac97_secondary);
+ outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR));
+ udelay(20);
+ snd_via82xx_codec_ready(chip, chip->ac97_secondary);
+}
+
+/*
+ * prepare callback for playback and capture
+ */
+static int snd_via82xx_pcm_prepare(snd_pcm_substream_t *substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+
+ snd_via82xx_channel_reset(chip, viadev);
+ /* this must be set after channel_reset */
+ snd_via82xx_set_table_ptr(chip, viadev);
+ outb(VIA_REG_TYPE_AUTOSTART|VIA_REG_TYPE_INT_EOL|VIA_REG_TYPE_INT_FLAG,
+ VIADEV_REG(viadev, OFFSET_TYPE));
+ return 0;
+}
+
+/*
+ * pcm hardware definition, identical for both playback and capture
+ */
+static snd_pcm_hardware_t snd_via82xx_hw =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = VIA_TABLE_SIZE / 2,
+ .fifo_size = 0,
+};
+
+
+/*
+ * open callback skeleton
+ */
+static int snd_via82xx_modem_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int err;
+ static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+
+ runtime->hw = snd_via82xx_hw;
+
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
+ return err;
+
+ /* we may remove following constaint when we modify table entries
+ in interrupt */
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+
+ runtime->private_data = viadev;
+ viadev->substream = substream;
+
+ return 0;
+}
+
+
+/*
+ * open callback for playback
+ */
+static int snd_via82xx_playback_open(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = &chip->devs[chip->playback_devno + substream->number];
+
+ return snd_via82xx_modem_pcm_open(chip, viadev, substream);
+}
+
+/*
+ * open callback for capture
+ */
+static int snd_via82xx_capture_open(snd_pcm_substream_t * substream)
+{
+ via82xx_t *chip = snd_pcm_substream_chip(substream);
+ viadev_t *viadev = &chip->devs[chip->capture_devno + substream->pcm->device];
+
+ return snd_via82xx_modem_pcm_open(chip, viadev, substream);
+}
+
+/*
+ * close callback
+ */
+static int snd_via82xx_pcm_close(snd_pcm_substream_t * substream)
+{
+ viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+
+ viadev->substream = NULL;
+ return 0;
+}
+
+
+/* via686 playback callbacks */
+static snd_pcm_ops_t snd_via686_playback_ops = {
+ .open = snd_via82xx_playback_open,
+ .close = snd_via82xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_via82xx_hw_params,
+ .hw_free = snd_via82xx_hw_free,
+ .prepare = snd_via82xx_pcm_prepare,
+ .trigger = snd_via82xx_modem_pcm_trigger,
+ .pointer = snd_via686_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+/* via686 capture callbacks */
+static snd_pcm_ops_t snd_via686_capture_ops = {
+ .open = snd_via82xx_capture_open,
+ .close = snd_via82xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_via82xx_hw_params,
+ .hw_free = snd_via82xx_hw_free,
+ .prepare = snd_via82xx_pcm_prepare,
+ .trigger = snd_via82xx_modem_pcm_trigger,
+ .pointer = snd_via686_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+
+static void init_viadev(via82xx_t *chip, int idx, unsigned int reg_offset, int direction)
+{
+ chip->devs[idx].reg_offset = reg_offset;
+ chip->devs[idx].direction = direction;
+ chip->devs[idx].port = chip->port + reg_offset;
+}
+
+/*
+ * create a pcm instance for via686a/b
+ */
+static int __devinit snd_via686_pcm_new(via82xx_t *chip)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ chip->playback_devno = 0;
+ chip->capture_devno = 1;
+ chip->num_devs = 2;
+ chip->intr_mask = 0x330000; /* FLAGS | EOL for MR, MW */
+
+ err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
+ pcm->private_data = chip;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcms[0] = pcm;
+ init_viadev(chip, 0, VIA_REG_MO_STATUS, 0);
+ init_viadev(chip, 1, VIA_REG_MI_STATUS, 1);
+
+ if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
+ return err;
+
+ return 0;
+}
+
+
+/*
+ * Mixer part
+ */
+
+
+static void snd_via82xx_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ via82xx_t *chip = bus->private_data;
+ chip->ac97_bus = NULL;
+}
+
+static void snd_via82xx_mixer_free_ac97(ac97_t *ac97)
+{
+ via82xx_t *chip = ac97->private_data;
+ chip->ac97 = NULL;
+}
+
+
+static int __devinit snd_via82xx_mixer_new(via82xx_t *chip)
+{
+ ac97_template_t ac97;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_via82xx_codec_write,
+ .read = snd_via82xx_codec_read,
+ .wait = snd_via82xx_codec_wait,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ return err;
+ chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus;
+ chip->ac97_bus->clock = chip->ac97_clock;
+ chip->ac97_bus->shared_type = AC97_SHARED_TYPE_VIA;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_via82xx_mixer_free_ac97;
+ ac97.pci = chip->pci;
+ ac97.scaps = AC97_SCAP_SKIP_AUDIO;
+ ac97.num = chip->ac97_secondary;
+
+ if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ return err;
+
+ return 0;
+}
+
+
+/*
+ * proc interface
+ */
+static void snd_via82xx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+ via82xx_t *chip = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "%s\n\n", chip->card->longname);
+ for (i = 0; i < 0xa0; i += 4) {
+ snd_iprintf(buffer, "%02x: %08x\n", i, inl(chip->port + i));
+ }
+}
+
+static void __devinit snd_via82xx_proc_init(via82xx_t *chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(chip->card, "via82xx", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read);
+}
+
+/*
+ *
+ */
+
+static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
+{
+ unsigned int val;
+ int max_count;
+ unsigned char pval;
+
+ pci_read_config_byte(chip->pci, VIA_MC97_CTRL, &pval);
+ if((pval & VIA_MC97_CTRL_INIT) != VIA_MC97_CTRL_INIT) {
+ pci_write_config_byte(chip->pci, 0x44, pval|VIA_MC97_CTRL_INIT);
+ udelay(100);
+ }
+
+ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+ if (! (pval & VIA_ACLINK_C00_READY)) { /* codec not ready? */
+ /* deassert ACLink reset, force SYNC */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+ VIA_ACLINK_CTRL_ENABLE |
+ VIA_ACLINK_CTRL_RESET |
+ VIA_ACLINK_CTRL_SYNC);
+ udelay(100);
+#if 1 /* FIXME: should we do full reset here for all chip models? */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, 0x00);
+ udelay(100);
+#else
+ /* deassert ACLink reset, force SYNC (warm AC'97 reset) */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+ VIA_ACLINK_CTRL_RESET|VIA_ACLINK_CTRL_SYNC);
+ udelay(2);
+#endif
+ /* ACLink on, deassert ACLink reset, VSR, SGD data out */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+ udelay(100);
+ }
+
+ pci_read_config_byte(chip->pci, VIA_ACLINK_CTRL, &pval);
+ if ((pval & VIA_ACLINK_CTRL_INIT) != VIA_ACLINK_CTRL_INIT) {
+ /* ACLink on, deassert ACLink reset, VSR, SGD data out */
+ pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+ udelay(100);
+ }
+
+ /* wait until codec ready */
+ max_count = ((3 * HZ) / 4) + 1;
+ do {
+ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+ if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
+ break;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (--max_count > 0);
+
+ if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+ snd_printk("AC'97 codec is not ready [0x%x]\n", val);
+
+ snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+ VIA_REG_AC97_SECONDARY_VALID |
+ (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+ max_count = ((3 * HZ) / 4) + 1;
+ snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+ VIA_REG_AC97_SECONDARY_VALID |
+ (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+ do {
+ if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+ chip->ac97_secondary = 1;
+ goto __ac97_ok2;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (--max_count > 0);
+ /* This is ok, the most of motherboards have only one codec */
+
+ __ac97_ok2:
+
+ /* route FM trap to IRQ, disable FM trap */
+ // pci_write_config_byte(chip->pci, VIA_FM_NMI_CTRL, 0);
+ /* disable all GPI interrupts */
+ outl(0, VIAREG(chip, GPI_INTR));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int snd_via82xx_suspend(snd_card_t *card, pm_message_t state)
+{
+ via82xx_t *chip = card->pm_private_data;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (chip->pcms[i])
+ snd_pcm_suspend_all(chip->pcms[i]);
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+ synchronize_irq(chip->irq);
+ snd_ac97_suspend(chip->ac97);
+ pci_set_power_state(chip->pci, 3);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int snd_via82xx_resume(snd_card_t *card)
+{
+ via82xx_t *chip = card->pm_private_data;
+ int i;
+
+ pci_enable_device(chip->pci);
+ pci_set_power_state(chip->pci, 0);
+ pci_set_master(chip->pci);
+
+ snd_via82xx_chip_init(chip);
+
+ snd_ac97_resume(chip->ac97);
+
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_via82xx_free(via82xx_t *chip)
+{
+ unsigned int i;
+
+ if (chip->irq < 0)
+ goto __end_hw;
+ /* disable interrupts */
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+ synchronize_irq(chip->irq);
+ __end_hw:
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_via82xx_dev_free(snd_device_t *device)
+{
+ via82xx_t *chip = device->device_data;
+ return snd_via82xx_free(chip);
+}
+
+static int __devinit snd_via82xx_create(snd_card_t * card,
+ struct pci_dev *pci,
+ int chip_type,
+ int revision,
+ unsigned int ac97_clock,
+ via82xx_t ** r_via)
+{
+ via82xx_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_via82xx_dev_free,
+ };
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ if ((chip = kcalloc(1, sizeof(*chip), GFP_KERNEL)) == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ if ((err = pci_request_regions(pci, card->driver)) < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->port = pci_resource_start(pci, 0);
+ if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ card->driver, (void *)chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_via82xx_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ if (ac97_clock >= 8000 && ac97_clock <= 48000)
+ chip->ac97_clock = ac97_clock;
+ synchronize_irq(chip->irq);
+
+ if ((err = snd_via82xx_chip_init(chip)) < 0) {
+ snd_via82xx_free(chip);
+ return err;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_via82xx_free(chip);
+ return err;
+ }
+
+ /* The 8233 ac97 controller does not implement the master bit
+ * in the pci command register. IMHO this is a violation of the PCI spec.
+ * We call pci_set_master here because it does not hurt. */
+ pci_set_master(pci);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *r_via = chip;
+ return 0;
+}
+
+
+static int __devinit snd_via82xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ via82xx_t *chip;
+ unsigned char revision;
+ int chip_type = 0, card_type;
+ unsigned int i;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ card_type = pci_id->driver_data;
+ pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+ switch (card_type) {
+ case TYPE_CARD_VIA82XX_MODEM:
+ strcpy(card->driver, "VIA82XX-MODEM");
+ sprintf(card->shortname, "VIA 82XX modem");
+ break;
+ default:
+ snd_printk(KERN_ERR "invalid card type %d\n", card_type);
+ err = -EINVAL;
+ goto __error;
+ }
+
+ if ((err = snd_via82xx_create(card, pci, chip_type, revision, ac97_clock[dev], &chip)) < 0)
+ goto __error;
+ if ((err = snd_via82xx_mixer_new(chip)) < 0)
+ goto __error;
+
+ if ((err = snd_via686_pcm_new(chip)) < 0 )
+ goto __error;
+
+ snd_card_set_pm_callback(card, snd_via82xx_suspend, snd_via82xx_resume, chip);
+
+ /* disable interrupts */
+ for (i = 0; i < chip->num_devs; i++)
+ snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+ sprintf(card->longname, "%s at 0x%lx, irq %d",
+ card->shortname, chip->port, chip->irq);
+
+ snd_via82xx_proc_init(chip);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+
+ __error:
+ snd_card_free(card);
+ return err;
+}
+
+static void __devexit snd_via82xx_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "VIA 82xx Modem",
+ .id_table = snd_via82xx_modem_ids,
+ .probe = snd_via82xx_probe,
+ .remove = __devexit_p(snd_via82xx_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_via82xx_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_via82xx_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via82xx_init)
+module_exit(alsa_card_via82xx_exit)
diff --git a/sound/pci/vx222/Makefile b/sound/pci/vx222/Makefile
new file mode 100644
index 00000000000..058c8bff7c1
--- /dev/null
+++ b/sound/pci/vx222/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-vx222-objs := vx222.o vx222_ops.o
+
+obj-$(CONFIG_SND_VX222) += snd-vx222.o
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
new file mode 100644
index 00000000000..4ffbb25658a
--- /dev/null
+++ b/sound/pci/vx222/vx222.c
@@ -0,0 +1,272 @@
+/*
+ * Driver for Digigram VX222 V2/Mic PCI soundcards
+ *
+ * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "vx222.h"
+
+#define CARD_NAME "VX222"
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Digigram VX222 V2/Mic");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int mic[SNDRV_CARDS]; /* microphone */
+static int ibl[SNDRV_CARDS]; /* microphone */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
+module_param_array(mic, bool, NULL, 0444);
+MODULE_PARM_DESC(mic, "Enable Microphone.");
+module_param_array(ibl, int, NULL, 0444);
+MODULE_PARM_DESC(ibl, "Capture IBL size.");
+
+/*
+ */
+
+enum {
+ VX_PCI_VX222_OLD,
+ VX_PCI_VX222_NEW
+};
+
+static struct pci_device_id snd_vx222_ids[] = {
+ { 0x10b5, 0x9050, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, }, /* PLX */
+ { 0x10b5, 0x9030, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, }, /* PLX */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_vx222_ids);
+
+
+/*
+ */
+
+static struct snd_vx_hardware vx222_old_hw = {
+
+ .name = "VX222/Old",
+ .type = VX_TYPE_BOARD,
+ /* hw specs */
+ .num_codecs = 1,
+ .num_ins = 1,
+ .num_outs = 1,
+ .output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
+};
+
+static struct snd_vx_hardware vx222_v2_hw = {
+
+ .name = "VX222/v2",
+ .type = VX_TYPE_V2,
+ /* hw specs */
+ .num_codecs = 1,
+ .num_ins = 1,
+ .num_outs = 1,
+ .output_level_max = VX2_AKM_LEVEL_MAX,
+};
+
+static struct snd_vx_hardware vx222_mic_hw = {
+
+ .name = "VX222/Mic",
+ .type = VX_TYPE_MIC,
+ /* hw specs */
+ .num_codecs = 1,
+ .num_ins = 1,
+ .num_outs = 1,
+ .output_level_max = VX2_AKM_LEVEL_MAX,
+};
+
+
+/*
+ */
+static int snd_vx222_free(vx_core_t *chip)
+{
+ struct snd_vx222 *vx = (struct snd_vx222 *)chip;
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void*)chip);
+ if (vx->port[0])
+ pci_release_regions(vx->pci);
+ pci_disable_device(vx->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_vx222_dev_free(snd_device_t *device)
+{
+ vx_core_t *chip = device->device_data;
+ return snd_vx222_free(chip);
+}
+
+
+static int __devinit snd_vx222_create(snd_card_t *card, struct pci_dev *pci,
+ struct snd_vx_hardware *hw,
+ struct snd_vx222 **rchip)
+{
+ vx_core_t *chip;
+ struct snd_vx222 *vx;
+ int i, err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_vx222_dev_free,
+ };
+ struct snd_vx_ops *vx_ops;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ pci_set_master(pci);
+
+ vx_ops = hw->type == VX_TYPE_BOARD ? &vx222_old_ops : &vx222_ops;
+ chip = snd_vx_create(card, hw, vx_ops,
+ sizeof(struct snd_vx222) - sizeof(vx_core_t));
+ if (! chip) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ vx = (struct snd_vx222 *)chip;
+ vx->pci = pci;
+
+ if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
+ snd_vx222_free(chip);
+ return err;
+ }
+ for (i = 0; i < 2; i++)
+ vx->port[i] = pci_resource_start(pci, i + 1);
+
+ if (request_irq(pci->irq, snd_vx_irq_handler, SA_INTERRUPT|SA_SHIRQ,
+ CARD_NAME, (void *) chip)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ snd_vx222_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_vx222_free(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = vx;
+ return 0;
+}
+
+
+static int __devinit snd_vx222_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ struct snd_vx_hardware *hw;
+ struct snd_vx222 *vx;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ switch ((int)pci_id->driver_data) {
+ case VX_PCI_VX222_OLD:
+ hw = &vx222_old_hw;
+ break;
+ case VX_PCI_VX222_NEW:
+ default:
+ if (mic[dev])
+ hw = &vx222_mic_hw;
+ else
+ hw = &vx222_v2_hw;
+ break;
+ }
+ if ((err = snd_vx222_create(card, pci, hw, &vx)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ vx->core.ibl.size = ibl[dev];
+
+ sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %i",
+ card->shortname, vx->port[0], vx->port[1], vx->core.irq);
+ snd_printdd("%s at 0x%lx & 0x%lx, irq %i\n",
+ card->shortname, vx->port[0], vx->port[1], vx->core.irq);
+
+#ifdef SND_VX_FW_LOADER
+ vx->core.dev = &pci->dev;
+#endif
+
+ if ((err = snd_vx_setup_firmware(&vx->core)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_vx222_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Digigram VX222",
+ .id_table = snd_vx222_ids,
+ .probe = snd_vx222_probe,
+ .remove = __devexit_p(snd_vx222_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_vx222_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_vx222_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_vx222_init)
+module_exit(alsa_card_vx222_exit)
diff --git a/sound/pci/vx222/vx222.h b/sound/pci/vx222/vx222.h
new file mode 100644
index 00000000000..18478ae124a
--- /dev/null
+++ b/sound/pci/vx222/vx222.h
@@ -0,0 +1,114 @@
+/*
+ * Driver for Digigram VX222 PCI soundcards
+ *
+ * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __VX222_H
+#define __VX222_H
+
+#include <sound/vx_core.h>
+
+struct snd_vx222 {
+
+ vx_core_t core;
+
+ /* h/w config; for PLX and for DSP */
+ struct pci_dev *pci;
+ unsigned long port[2];
+
+ unsigned int regCDSP; /* current CDSP register */
+ unsigned int regCFG; /* current CFG register */
+ unsigned int regSELMIC; /* current SELMIC reg. (for VX222 Mic) */
+
+ int input_level[2]; /* input level for vx222 mic */
+ int mic_level; /* mic level for vx222 mic */
+};
+
+/* we use a lookup table with 148 values, see vx_mixer.c */
+#define VX2_AKM_LEVEL_MAX 0x93
+
+extern struct snd_vx_ops vx222_ops;
+extern struct snd_vx_ops vx222_old_ops;
+
+/* Offset of registers with base equal to portDSP. */
+#define VX_RESET_DMA_REGISTER_OFFSET 0x00000008
+
+/* Constants used to access the INTCSR register. */
+#define VX_INTCSR_VALUE 0x00000001
+#define VX_PCI_INTERRUPT_MASK 0x00000040
+
+/* Constants used to access the CDSP register (0x20). */
+#define VX_CDSP_TEST1_MASK 0x00000080
+#define VX_CDSP_TOR1_MASK 0x00000040
+#define VX_CDSP_TOR2_MASK 0x00000020
+#define VX_CDSP_RESERVED0_0_MASK 0x00000010
+#define VX_CDSP_CODEC_RESET_MASK 0x00000008
+#define VX_CDSP_VALID_IRQ_MASK 0x00000004
+#define VX_CDSP_TEST0_MASK 0x00000002
+#define VX_CDSP_DSP_RESET_MASK 0x00000001
+
+#define VX_CDSP_GPIO_OUT_MASK 0x00000060
+#define VX_GPIO_OUT_BIT_OFFSET 5 // transform output to bit 0 and 1
+
+/* Constants used to access the CFG register (0x24). */
+#define VX_CFG_SYNCDSP_MASK 0x00000080
+#define VX_CFG_RESERVED0_0_MASK 0x00000040
+#define VX_CFG_RESERVED1_0_MASK 0x00000020
+#define VX_CFG_RESERVED2_0_MASK 0x00000010
+#define VX_CFG_DATAIN_SEL_MASK 0x00000008 // 0 (ana), 1 (UER)
+#define VX_CFG_RESERVED3_0_MASK 0x00000004
+#define VX_CFG_RESERVED4_0_MASK 0x00000002
+#define VX_CFG_CLOCKIN_SEL_MASK 0x00000001 // 0 (internal), 1 (AES/EBU)
+
+/* Constants used to access the STATUS register (0x30). */
+#define VX_STATUS_DATA_XICOR_MASK 0x00000080
+#define VX_STATUS_VAL_TEST1_MASK 0x00000040
+#define VX_STATUS_VAL_TEST0_MASK 0x00000020
+#define VX_STATUS_RESERVED0_MASK 0x00000010
+#define VX_STATUS_VAL_TOR1_MASK 0x00000008
+#define VX_STATUS_VAL_TOR0_MASK 0x00000004
+#define VX_STATUS_LEVEL_IN_MASK 0x00000002 // 6 dBu (0), 22 dBu (1)
+#define VX_STATUS_MEMIRQ_MASK 0x00000001
+
+#define VX_STATUS_GPIO_IN_MASK 0x0000000C
+#define VX_GPIO_IN_BIT_OFFSET 0 // leave input as bit 2 and 3
+
+/* Constants used to access the MICRO INPUT SELECT register (0x40). */
+#define MICRO_SELECT_INPUT_NORM 0x00
+#define MICRO_SELECT_INPUT_MUTE 0x01
+#define MICRO_SELECT_INPUT_LIMIT 0x02
+#define MICRO_SELECT_INPUT_MASK 0x03
+
+#define MICRO_SELECT_PREAMPLI_G_0 0x00
+#define MICRO_SELECT_PREAMPLI_G_1 0x04
+#define MICRO_SELECT_PREAMPLI_G_2 0x08
+#define MICRO_SELECT_PREAMPLI_G_3 0x0C
+#define MICRO_SELECT_PREAMPLI_MASK 0x0C
+#define MICRO_SELECT_PREAMPLI_OFFSET 2
+
+#define MICRO_SELECT_RAISE_COMPR 0x10
+
+#define MICRO_SELECT_NOISE_T_52DB 0x00
+#define MICRO_SELECT_NOISE_T_42DB 0x20
+#define MICRO_SELECT_NOISE_T_32DB 0x40
+#define MICRO_SELECT_NOISE_T_MASK 0x60
+
+#define MICRO_SELECT_PHANTOM_ALIM 0x80
+
+
+#endif /* __VX222_H */
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
new file mode 100644
index 00000000000..683e9799976
--- /dev/null
+++ b/sound/pci/vx222/vx222_ops.c
@@ -0,0 +1,1004 @@
+/*
+ * Driver for Digigram VX222 V2/Mic soundcards
+ *
+ * VX222-specific low-level routines
+ *
+ * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <asm/io.h>
+#include "vx222.h"
+
+
+static int vx2_reg_offset[VX_REG_MAX] = {
+ [VX_ICR] = 0x00,
+ [VX_CVR] = 0x04,
+ [VX_ISR] = 0x08,
+ [VX_IVR] = 0x0c,
+ [VX_RXH] = 0x14,
+ [VX_RXM] = 0x18,
+ [VX_RXL] = 0x1c,
+ [VX_DMA] = 0x10,
+ [VX_CDSP] = 0x20,
+ [VX_CFG] = 0x24,
+ [VX_RUER] = 0x28,
+ [VX_DATA] = 0x2c,
+ [VX_STATUS] = 0x30,
+ [VX_LOFREQ] = 0x34,
+ [VX_HIFREQ] = 0x38,
+ [VX_CSUER] = 0x3c,
+ [VX_SELMIC] = 0x40,
+ [VX_COMPOT] = 0x44, // Write: POTENTIOMETER ; Read: COMPRESSION LEVEL activate
+ [VX_SCOMPR] = 0x48, // Read: COMPRESSION THRESHOLD activate
+ [VX_GLIMIT] = 0x4c, // Read: LEVEL LIMITATION activate
+ [VX_INTCSR] = 0x4c, // VX_INTCSR_REGISTER_OFFSET
+ [VX_CNTRL] = 0x50, // VX_CNTRL_REGISTER_OFFSET
+ [VX_GPIOC] = 0x54, // VX_GPIOC (new with PLX9030)
+};
+
+static int vx2_reg_index[VX_REG_MAX] = {
+ [VX_ICR] = 1,
+ [VX_CVR] = 1,
+ [VX_ISR] = 1,
+ [VX_IVR] = 1,
+ [VX_RXH] = 1,
+ [VX_RXM] = 1,
+ [VX_RXL] = 1,
+ [VX_DMA] = 1,
+ [VX_CDSP] = 1,
+ [VX_CFG] = 1,
+ [VX_RUER] = 1,
+ [VX_DATA] = 1,
+ [VX_STATUS] = 1,
+ [VX_LOFREQ] = 1,
+ [VX_HIFREQ] = 1,
+ [VX_CSUER] = 1,
+ [VX_SELMIC] = 1,
+ [VX_COMPOT] = 1,
+ [VX_SCOMPR] = 1,
+ [VX_GLIMIT] = 1,
+ [VX_INTCSR] = 0, /* on the PLX */
+ [VX_CNTRL] = 0, /* on the PLX */
+ [VX_GPIOC] = 0, /* on the PLX */
+};
+
+inline static unsigned long vx2_reg_addr(vx_core_t *_chip, int reg)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ return chip->port[vx2_reg_index[reg]] + vx2_reg_offset[reg];
+}
+
+/**
+ * snd_vx_inb - read a byte from the register
+ * @offset: register enum
+ */
+static unsigned char vx2_inb(vx_core_t *chip, int offset)
+{
+ return inb(vx2_reg_addr(chip, offset));
+}
+
+/**
+ * snd_vx_outb - write a byte on the register
+ * @offset: the register offset
+ * @val: the value to write
+ */
+static void vx2_outb(vx_core_t *chip, int offset, unsigned char val)
+{
+ outb(val, vx2_reg_addr(chip, offset));
+ //printk("outb: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+}
+
+/**
+ * snd_vx_inl - read a 32bit word from the register
+ * @offset: register enum
+ */
+static unsigned int vx2_inl(vx_core_t *chip, int offset)
+{
+ return inl(vx2_reg_addr(chip, offset));
+}
+
+/**
+ * snd_vx_outl - write a 32bit word on the register
+ * @offset: the register enum
+ * @val: the value to write
+ */
+static void vx2_outl(vx_core_t *chip, int offset, unsigned int val)
+{
+ // printk("outl: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+ outl(val, vx2_reg_addr(chip, offset));
+}
+
+/*
+ * redefine macros to call directly
+ */
+#undef vx_inb
+#define vx_inb(chip,reg) vx2_inb((vx_core_t*)(chip), VX_##reg)
+#undef vx_outb
+#define vx_outb(chip,reg,val) vx2_outb((vx_core_t*)(chip), VX_##reg, val)
+#undef vx_inl
+#define vx_inl(chip,reg) vx2_inl((vx_core_t*)(chip), VX_##reg)
+#undef vx_outl
+#define vx_outl(chip,reg,val) vx2_outl((vx_core_t*)(chip), VX_##reg, val)
+
+
+/*
+ * vx_reset_dsp - reset the DSP
+ */
+
+#define XX_DSP_RESET_WAIT_TIME 2 /* ms */
+
+static void vx2_reset_dsp(vx_core_t *_chip)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+ /* set the reset dsp bit to 0 */
+ vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_DSP_RESET_MASK);
+
+ snd_vx_delay(_chip, XX_DSP_RESET_WAIT_TIME);
+
+ chip->regCDSP |= VX_CDSP_DSP_RESET_MASK;
+ /* set the reset dsp bit to 1 */
+ vx_outl(chip, CDSP, chip->regCDSP);
+}
+
+
+static int vx2_test_xilinx(vx_core_t *_chip)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ unsigned int data;
+
+ snd_printdd("testing xilinx...\n");
+ /* This test uses several write/read sequences on TEST0 and TEST1 bits
+ * to figure out whever or not the xilinx was correctly loaded
+ */
+
+ /* We write 1 on CDSP.TEST0. We should get 0 on STATUS.TEST0. */
+ vx_outl(chip, CDSP, chip->regCDSP | VX_CDSP_TEST0_MASK);
+ vx_inl(chip, ISR);
+ data = vx_inl(chip, STATUS);
+ if ((data & VX_STATUS_VAL_TEST0_MASK) == VX_STATUS_VAL_TEST0_MASK) {
+ snd_printdd("bad!\n");
+ return -ENODEV;
+ }
+
+ /* We write 0 on CDSP.TEST0. We should get 1 on STATUS.TEST0. */
+ vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_TEST0_MASK);
+ vx_inl(chip, ISR);
+ data = vx_inl(chip, STATUS);
+ if (! (data & VX_STATUS_VAL_TEST0_MASK)) {
+ snd_printdd("bad! #2\n");
+ return -ENODEV;
+ }
+
+ if (_chip->type == VX_TYPE_BOARD) {
+ /* not implemented on VX_2_BOARDS */
+ /* We write 1 on CDSP.TEST1. We should get 0 on STATUS.TEST1. */
+ vx_outl(chip, CDSP, chip->regCDSP | VX_CDSP_TEST1_MASK);
+ vx_inl(chip, ISR);
+ data = vx_inl(chip, STATUS);
+ if ((data & VX_STATUS_VAL_TEST1_MASK) == VX_STATUS_VAL_TEST1_MASK) {
+ snd_printdd("bad! #3\n");
+ return -ENODEV;
+ }
+
+ /* We write 0 on CDSP.TEST1. We should get 1 on STATUS.TEST1. */
+ vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_TEST1_MASK);
+ vx_inl(chip, ISR);
+ data = vx_inl(chip, STATUS);
+ if (! (data & VX_STATUS_VAL_TEST1_MASK)) {
+ snd_printdd("bad! #4\n");
+ return -ENODEV;
+ }
+ }
+ snd_printdd("ok, xilinx fine.\n");
+ return 0;
+}
+
+
+/**
+ * vx_setup_pseudo_dma - set up the pseudo dma read/write mode.
+ * @do_write: 0 = read, 1 = set up for DMA write
+ */
+static void vx2_setup_pseudo_dma(vx_core_t *chip, int do_write)
+{
+ /* Interrupt mode and HREQ pin enabled for host transmit data transfers
+ * (in case of the use of the pseudo-dma facility).
+ */
+ vx_outl(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ);
+
+ /* Reset the pseudo-dma register (in case of the use of the
+ * pseudo-dma facility).
+ */
+ vx_outl(chip, RESET_DMA, 0);
+}
+
+/*
+ * vx_release_pseudo_dma - disable the pseudo-DMA mode
+ */
+inline static void vx2_release_pseudo_dma(vx_core_t *chip)
+{
+ /* HREQ pin disabled. */
+ vx_outl(chip, ICR, 0);
+}
+
+
+
+/* pseudo-dma write */
+static void vx2_dma_write(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+ vx_pipe_t *pipe, int count)
+{
+ unsigned long port = vx2_reg_addr(chip, VX_DMA);
+ int offset = pipe->hw_ptr;
+ u32 *addr = (u32 *)(runtime->dma_area + offset);
+
+ snd_assert(count % 4 == 0, return);
+
+ vx2_setup_pseudo_dma(chip, 1);
+
+ /* Transfer using pseudo-dma.
+ */
+ if (offset + count > pipe->buffer_bytes) {
+ int length = pipe->buffer_bytes - offset;
+ count -= length;
+ length >>= 2; /* in 32bit words */
+ /* Transfer using pseudo-dma. */
+ while (length-- > 0) {
+ outl(cpu_to_le32(*addr), port);
+ addr++;
+ }
+ addr = (u32 *)runtime->dma_area;
+ pipe->hw_ptr = 0;
+ }
+ pipe->hw_ptr += count;
+ count >>= 2; /* in 32bit words */
+ /* Transfer using pseudo-dma. */
+ while (count-- > 0) {
+ outl(cpu_to_le32(*addr), port);
+ addr++;
+ }
+
+ vx2_release_pseudo_dma(chip);
+}
+
+
+/* pseudo dma read */
+static void vx2_dma_read(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+ vx_pipe_t *pipe, int count)
+{
+ int offset = pipe->hw_ptr;
+ u32 *addr = (u32 *)(runtime->dma_area + offset);
+ unsigned long port = vx2_reg_addr(chip, VX_DMA);
+
+ snd_assert(count % 4 == 0, return);
+
+ vx2_setup_pseudo_dma(chip, 0);
+ /* Transfer using pseudo-dma.
+ */
+ if (offset + count > pipe->buffer_bytes) {
+ int length = pipe->buffer_bytes - offset;
+ count -= length;
+ length >>= 2; /* in 32bit words */
+ /* Transfer using pseudo-dma. */
+ while (length-- > 0)
+ *addr++ = le32_to_cpu(inl(port));
+ addr = (u32 *)runtime->dma_area;
+ pipe->hw_ptr = 0;
+ }
+ pipe->hw_ptr += count;
+ count >>= 2; /* in 32bit words */
+ /* Transfer using pseudo-dma. */
+ while (count-- > 0)
+ *addr++ = le32_to_cpu(inl(port));
+
+ vx2_release_pseudo_dma(chip);
+}
+
+#define VX_XILINX_RESET_MASK 0x40000000
+#define VX_USERBIT0_MASK 0x00000004
+#define VX_USERBIT1_MASK 0x00000020
+#define VX_CNTRL_REGISTER_VALUE 0x00172012
+
+/*
+ * transfer counts bits to PLX
+ */
+static int put_xilinx_data(vx_core_t *chip, unsigned int port, unsigned int counts, unsigned char data)
+{
+ unsigned int i;
+
+ for (i = 0; i < counts; i++) {
+ unsigned int val;
+
+ /* set the clock bit to 0. */
+ val = VX_CNTRL_REGISTER_VALUE & ~VX_USERBIT0_MASK;
+ vx2_outl(chip, port, val);
+ vx2_inl(chip, port);
+ udelay(1);
+
+ if (data & (1 << i))
+ val |= VX_USERBIT1_MASK;
+ else
+ val &= ~VX_USERBIT1_MASK;
+ vx2_outl(chip, port, val);
+ vx2_inl(chip, port);
+
+ /* set the clock bit to 1. */
+ val |= VX_USERBIT0_MASK;
+ vx2_outl(chip, port, val);
+ vx2_inl(chip, port);
+ udelay(1);
+ }
+ return 0;
+}
+
+/*
+ * load the xilinx image
+ */
+static int vx2_load_xilinx_binary(vx_core_t *chip, const struct firmware *xilinx)
+{
+ unsigned int i;
+ unsigned int port;
+ unsigned char *image;
+
+ /* XILINX reset (wait at least 1 milisecond between reset on and off). */
+ vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE | VX_XILINX_RESET_MASK);
+ vx_inl(chip, CNTRL);
+ snd_vx_delay(chip, 10);
+ vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE);
+ vx_inl(chip, CNTRL);
+ snd_vx_delay(chip, 10);
+
+ if (chip->type == VX_TYPE_BOARD)
+ port = VX_CNTRL;
+ else
+ port = VX_GPIOC; /* VX222 V2 and VX222_MIC_BOARD with new PLX9030 use this register */
+
+ image = xilinx->data;
+ for (i = 0; i < xilinx->size; i++, image++) {
+ if (put_xilinx_data(chip, port, 8, *image) < 0)
+ return -EINVAL;
+ /* don't take too much time in this loop... */
+ cond_resched();
+ }
+ put_xilinx_data(chip, port, 4, 0xff); /* end signature */
+
+ snd_vx_delay(chip, 200);
+
+ /* test after loading (is buggy with VX222) */
+ if (chip->type != VX_TYPE_BOARD) {
+ /* Test if load successful: test bit 8 of register GPIOC (VX222: use CNTRL) ! */
+ i = vx_inl(chip, GPIOC);
+ if (i & 0x0100)
+ return 0;
+ snd_printk(KERN_ERR "vx222: xilinx test failed after load, GPIOC=0x%x\n", i);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/*
+ * load the boot/dsp images
+ */
+static int vx2_load_dsp(vx_core_t *vx, int index, const struct firmware *dsp)
+{
+ int err;
+
+ switch (index) {
+ case 1:
+ /* xilinx image */
+ if ((err = vx2_load_xilinx_binary(vx, dsp)) < 0)
+ return err;
+ if ((err = vx2_test_xilinx(vx)) < 0)
+ return err;
+ return 0;
+ case 2:
+ /* DSP boot */
+ return snd_vx_dsp_boot(vx, dsp);
+ case 3:
+ /* DSP image */
+ return snd_vx_dsp_load(vx, dsp);
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * vx_test_and_ack - test and acknowledge interrupt
+ *
+ * called from irq hander, too
+ *
+ * spinlock held!
+ */
+static int vx2_test_and_ack(vx_core_t *chip)
+{
+ /* not booted yet? */
+ if (! (chip->chip_status & VX_STAT_XILINX_LOADED))
+ return -ENXIO;
+
+ if (! (vx_inl(chip, STATUS) & VX_STATUS_MEMIRQ_MASK))
+ return -EIO;
+
+ /* ok, interrupts generated, now ack it */
+ /* set ACQUIT bit up and down */
+ vx_outl(chip, STATUS, 0);
+ /* useless read just to spend some time and maintain
+ * the ACQUIT signal up for a while ( a bus cycle )
+ */
+ vx_inl(chip, STATUS);
+ /* ack */
+ vx_outl(chip, STATUS, VX_STATUS_MEMIRQ_MASK);
+ /* useless read just to spend some time and maintain
+ * the ACQUIT signal up for a while ( a bus cycle ) */
+ vx_inl(chip, STATUS);
+ /* clear */
+ vx_outl(chip, STATUS, 0);
+
+ return 0;
+}
+
+
+/*
+ * vx_validate_irq - enable/disable IRQ
+ */
+static void vx2_validate_irq(vx_core_t *_chip, int enable)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+ /* Set the interrupt enable bit to 1 in CDSP register */
+ if (enable) {
+ /* Set the PCI interrupt enable bit to 1.*/
+ vx_outl(chip, INTCSR, VX_INTCSR_VALUE|VX_PCI_INTERRUPT_MASK);
+ chip->regCDSP |= VX_CDSP_VALID_IRQ_MASK;
+ } else {
+ /* Set the PCI interrupt enable bit to 0. */
+ vx_outl(chip, INTCSR, VX_INTCSR_VALUE&~VX_PCI_INTERRUPT_MASK);
+ chip->regCDSP &= ~VX_CDSP_VALID_IRQ_MASK;
+ }
+ vx_outl(chip, CDSP, chip->regCDSP);
+}
+
+
+/*
+ * write an AKM codec data (24bit)
+ */
+static void vx2_write_codec_reg(vx_core_t *chip, unsigned int data)
+{
+ unsigned int i;
+
+ vx_inl(chip, HIFREQ);
+
+ /* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */
+ for (i = 0; i < 24; i++, data <<= 1)
+ vx_outl(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0));
+ /* Terminate access to codec registers */
+ vx_inl(chip, RUER);
+}
+
+
+#define AKM_CODEC_POWER_CONTROL_CMD 0xA007
+#define AKM_CODEC_RESET_ON_CMD 0xA100
+#define AKM_CODEC_RESET_OFF_CMD 0xA103
+#define AKM_CODEC_CLOCK_FORMAT_CMD 0xA240
+#define AKM_CODEC_MUTE_CMD 0xA38D
+#define AKM_CODEC_UNMUTE_CMD 0xA30D
+#define AKM_CODEC_LEFT_LEVEL_CMD 0xA400
+#define AKM_CODEC_RIGHT_LEVEL_CMD 0xA500
+
+static const u8 vx2_akm_gains_lut[VX2_AKM_LEVEL_MAX+1] = {
+ 0x7f, // [000] = +0.000 dB -> AKM(0x7f) = +0.000 dB error(+0.000 dB)
+ 0x7d, // [001] = -0.500 dB -> AKM(0x7d) = -0.572 dB error(-0.072 dB)
+ 0x7c, // [002] = -1.000 dB -> AKM(0x7c) = -0.873 dB error(+0.127 dB)
+ 0x7a, // [003] = -1.500 dB -> AKM(0x7a) = -1.508 dB error(-0.008 dB)
+ 0x79, // [004] = -2.000 dB -> AKM(0x79) = -1.844 dB error(+0.156 dB)
+ 0x77, // [005] = -2.500 dB -> AKM(0x77) = -2.557 dB error(-0.057 dB)
+ 0x76, // [006] = -3.000 dB -> AKM(0x76) = -2.937 dB error(+0.063 dB)
+ 0x75, // [007] = -3.500 dB -> AKM(0x75) = -3.334 dB error(+0.166 dB)
+ 0x73, // [008] = -4.000 dB -> AKM(0x73) = -4.188 dB error(-0.188 dB)
+ 0x72, // [009] = -4.500 dB -> AKM(0x72) = -4.648 dB error(-0.148 dB)
+ 0x71, // [010] = -5.000 dB -> AKM(0x71) = -5.134 dB error(-0.134 dB)
+ 0x70, // [011] = -5.500 dB -> AKM(0x70) = -5.649 dB error(-0.149 dB)
+ 0x6f, // [012] = -6.000 dB -> AKM(0x6f) = -6.056 dB error(-0.056 dB)
+ 0x6d, // [013] = -6.500 dB -> AKM(0x6d) = -6.631 dB error(-0.131 dB)
+ 0x6c, // [014] = -7.000 dB -> AKM(0x6c) = -6.933 dB error(+0.067 dB)
+ 0x6a, // [015] = -7.500 dB -> AKM(0x6a) = -7.571 dB error(-0.071 dB)
+ 0x69, // [016] = -8.000 dB -> AKM(0x69) = -7.909 dB error(+0.091 dB)
+ 0x67, // [017] = -8.500 dB -> AKM(0x67) = -8.626 dB error(-0.126 dB)
+ 0x66, // [018] = -9.000 dB -> AKM(0x66) = -9.008 dB error(-0.008 dB)
+ 0x65, // [019] = -9.500 dB -> AKM(0x65) = -9.407 dB error(+0.093 dB)
+ 0x64, // [020] = -10.000 dB -> AKM(0x64) = -9.826 dB error(+0.174 dB)
+ 0x62, // [021] = -10.500 dB -> AKM(0x62) = -10.730 dB error(-0.230 dB)
+ 0x61, // [022] = -11.000 dB -> AKM(0x61) = -11.219 dB error(-0.219 dB)
+ 0x60, // [023] = -11.500 dB -> AKM(0x60) = -11.738 dB error(-0.238 dB)
+ 0x5f, // [024] = -12.000 dB -> AKM(0x5f) = -12.149 dB error(-0.149 dB)
+ 0x5e, // [025] = -12.500 dB -> AKM(0x5e) = -12.434 dB error(+0.066 dB)
+ 0x5c, // [026] = -13.000 dB -> AKM(0x5c) = -13.033 dB error(-0.033 dB)
+ 0x5b, // [027] = -13.500 dB -> AKM(0x5b) = -13.350 dB error(+0.150 dB)
+ 0x59, // [028] = -14.000 dB -> AKM(0x59) = -14.018 dB error(-0.018 dB)
+ 0x58, // [029] = -14.500 dB -> AKM(0x58) = -14.373 dB error(+0.127 dB)
+ 0x56, // [030] = -15.000 dB -> AKM(0x56) = -15.130 dB error(-0.130 dB)
+ 0x55, // [031] = -15.500 dB -> AKM(0x55) = -15.534 dB error(-0.034 dB)
+ 0x54, // [032] = -16.000 dB -> AKM(0x54) = -15.958 dB error(+0.042 dB)
+ 0x53, // [033] = -16.500 dB -> AKM(0x53) = -16.404 dB error(+0.096 dB)
+ 0x52, // [034] = -17.000 dB -> AKM(0x52) = -16.874 dB error(+0.126 dB)
+ 0x51, // [035] = -17.500 dB -> AKM(0x51) = -17.371 dB error(+0.129 dB)
+ 0x50, // [036] = -18.000 dB -> AKM(0x50) = -17.898 dB error(+0.102 dB)
+ 0x4e, // [037] = -18.500 dB -> AKM(0x4e) = -18.605 dB error(-0.105 dB)
+ 0x4d, // [038] = -19.000 dB -> AKM(0x4d) = -18.905 dB error(+0.095 dB)
+ 0x4b, // [039] = -19.500 dB -> AKM(0x4b) = -19.538 dB error(-0.038 dB)
+ 0x4a, // [040] = -20.000 dB -> AKM(0x4a) = -19.872 dB error(+0.128 dB)
+ 0x48, // [041] = -20.500 dB -> AKM(0x48) = -20.583 dB error(-0.083 dB)
+ 0x47, // [042] = -21.000 dB -> AKM(0x47) = -20.961 dB error(+0.039 dB)
+ 0x46, // [043] = -21.500 dB -> AKM(0x46) = -21.356 dB error(+0.144 dB)
+ 0x44, // [044] = -22.000 dB -> AKM(0x44) = -22.206 dB error(-0.206 dB)
+ 0x43, // [045] = -22.500 dB -> AKM(0x43) = -22.664 dB error(-0.164 dB)
+ 0x42, // [046] = -23.000 dB -> AKM(0x42) = -23.147 dB error(-0.147 dB)
+ 0x41, // [047] = -23.500 dB -> AKM(0x41) = -23.659 dB error(-0.159 dB)
+ 0x40, // [048] = -24.000 dB -> AKM(0x40) = -24.203 dB error(-0.203 dB)
+ 0x3f, // [049] = -24.500 dB -> AKM(0x3f) = -24.635 dB error(-0.135 dB)
+ 0x3e, // [050] = -25.000 dB -> AKM(0x3e) = -24.935 dB error(+0.065 dB)
+ 0x3c, // [051] = -25.500 dB -> AKM(0x3c) = -25.569 dB error(-0.069 dB)
+ 0x3b, // [052] = -26.000 dB -> AKM(0x3b) = -25.904 dB error(+0.096 dB)
+ 0x39, // [053] = -26.500 dB -> AKM(0x39) = -26.615 dB error(-0.115 dB)
+ 0x38, // [054] = -27.000 dB -> AKM(0x38) = -26.994 dB error(+0.006 dB)
+ 0x37, // [055] = -27.500 dB -> AKM(0x37) = -27.390 dB error(+0.110 dB)
+ 0x36, // [056] = -28.000 dB -> AKM(0x36) = -27.804 dB error(+0.196 dB)
+ 0x34, // [057] = -28.500 dB -> AKM(0x34) = -28.699 dB error(-0.199 dB)
+ 0x33, // [058] = -29.000 dB -> AKM(0x33) = -29.183 dB error(-0.183 dB)
+ 0x32, // [059] = -29.500 dB -> AKM(0x32) = -29.696 dB error(-0.196 dB)
+ 0x31, // [060] = -30.000 dB -> AKM(0x31) = -30.241 dB error(-0.241 dB)
+ 0x31, // [061] = -30.500 dB -> AKM(0x31) = -30.241 dB error(+0.259 dB)
+ 0x30, // [062] = -31.000 dB -> AKM(0x30) = -30.823 dB error(+0.177 dB)
+ 0x2e, // [063] = -31.500 dB -> AKM(0x2e) = -31.610 dB error(-0.110 dB)
+ 0x2d, // [064] = -32.000 dB -> AKM(0x2d) = -31.945 dB error(+0.055 dB)
+ 0x2b, // [065] = -32.500 dB -> AKM(0x2b) = -32.659 dB error(-0.159 dB)
+ 0x2a, // [066] = -33.000 dB -> AKM(0x2a) = -33.038 dB error(-0.038 dB)
+ 0x29, // [067] = -33.500 dB -> AKM(0x29) = -33.435 dB error(+0.065 dB)
+ 0x28, // [068] = -34.000 dB -> AKM(0x28) = -33.852 dB error(+0.148 dB)
+ 0x27, // [069] = -34.500 dB -> AKM(0x27) = -34.289 dB error(+0.211 dB)
+ 0x25, // [070] = -35.000 dB -> AKM(0x25) = -35.235 dB error(-0.235 dB)
+ 0x24, // [071] = -35.500 dB -> AKM(0x24) = -35.750 dB error(-0.250 dB)
+ 0x24, // [072] = -36.000 dB -> AKM(0x24) = -35.750 dB error(+0.250 dB)
+ 0x23, // [073] = -36.500 dB -> AKM(0x23) = -36.297 dB error(+0.203 dB)
+ 0x22, // [074] = -37.000 dB -> AKM(0x22) = -36.881 dB error(+0.119 dB)
+ 0x21, // [075] = -37.500 dB -> AKM(0x21) = -37.508 dB error(-0.008 dB)
+ 0x20, // [076] = -38.000 dB -> AKM(0x20) = -38.183 dB error(-0.183 dB)
+ 0x1f, // [077] = -38.500 dB -> AKM(0x1f) = -38.726 dB error(-0.226 dB)
+ 0x1e, // [078] = -39.000 dB -> AKM(0x1e) = -39.108 dB error(-0.108 dB)
+ 0x1d, // [079] = -39.500 dB -> AKM(0x1d) = -39.507 dB error(-0.007 dB)
+ 0x1c, // [080] = -40.000 dB -> AKM(0x1c) = -39.926 dB error(+0.074 dB)
+ 0x1b, // [081] = -40.500 dB -> AKM(0x1b) = -40.366 dB error(+0.134 dB)
+ 0x1a, // [082] = -41.000 dB -> AKM(0x1a) = -40.829 dB error(+0.171 dB)
+ 0x19, // [083] = -41.500 dB -> AKM(0x19) = -41.318 dB error(+0.182 dB)
+ 0x18, // [084] = -42.000 dB -> AKM(0x18) = -41.837 dB error(+0.163 dB)
+ 0x17, // [085] = -42.500 dB -> AKM(0x17) = -42.389 dB error(+0.111 dB)
+ 0x16, // [086] = -43.000 dB -> AKM(0x16) = -42.978 dB error(+0.022 dB)
+ 0x15, // [087] = -43.500 dB -> AKM(0x15) = -43.610 dB error(-0.110 dB)
+ 0x14, // [088] = -44.000 dB -> AKM(0x14) = -44.291 dB error(-0.291 dB)
+ 0x14, // [089] = -44.500 dB -> AKM(0x14) = -44.291 dB error(+0.209 dB)
+ 0x13, // [090] = -45.000 dB -> AKM(0x13) = -45.031 dB error(-0.031 dB)
+ 0x12, // [091] = -45.500 dB -> AKM(0x12) = -45.840 dB error(-0.340 dB)
+ 0x12, // [092] = -46.000 dB -> AKM(0x12) = -45.840 dB error(+0.160 dB)
+ 0x11, // [093] = -46.500 dB -> AKM(0x11) = -46.731 dB error(-0.231 dB)
+ 0x11, // [094] = -47.000 dB -> AKM(0x11) = -46.731 dB error(+0.269 dB)
+ 0x10, // [095] = -47.500 dB -> AKM(0x10) = -47.725 dB error(-0.225 dB)
+ 0x10, // [096] = -48.000 dB -> AKM(0x10) = -47.725 dB error(+0.275 dB)
+ 0x0f, // [097] = -48.500 dB -> AKM(0x0f) = -48.553 dB error(-0.053 dB)
+ 0x0e, // [098] = -49.000 dB -> AKM(0x0e) = -49.152 dB error(-0.152 dB)
+ 0x0d, // [099] = -49.500 dB -> AKM(0x0d) = -49.796 dB error(-0.296 dB)
+ 0x0d, // [100] = -50.000 dB -> AKM(0x0d) = -49.796 dB error(+0.204 dB)
+ 0x0c, // [101] = -50.500 dB -> AKM(0x0c) = -50.491 dB error(+0.009 dB)
+ 0x0b, // [102] = -51.000 dB -> AKM(0x0b) = -51.247 dB error(-0.247 dB)
+ 0x0b, // [103] = -51.500 dB -> AKM(0x0b) = -51.247 dB error(+0.253 dB)
+ 0x0a, // [104] = -52.000 dB -> AKM(0x0a) = -52.075 dB error(-0.075 dB)
+ 0x0a, // [105] = -52.500 dB -> AKM(0x0a) = -52.075 dB error(+0.425 dB)
+ 0x09, // [106] = -53.000 dB -> AKM(0x09) = -52.990 dB error(+0.010 dB)
+ 0x09, // [107] = -53.500 dB -> AKM(0x09) = -52.990 dB error(+0.510 dB)
+ 0x08, // [108] = -54.000 dB -> AKM(0x08) = -54.013 dB error(-0.013 dB)
+ 0x08, // [109] = -54.500 dB -> AKM(0x08) = -54.013 dB error(+0.487 dB)
+ 0x07, // [110] = -55.000 dB -> AKM(0x07) = -55.173 dB error(-0.173 dB)
+ 0x07, // [111] = -55.500 dB -> AKM(0x07) = -55.173 dB error(+0.327 dB)
+ 0x06, // [112] = -56.000 dB -> AKM(0x06) = -56.512 dB error(-0.512 dB)
+ 0x06, // [113] = -56.500 dB -> AKM(0x06) = -56.512 dB error(-0.012 dB)
+ 0x06, // [114] = -57.000 dB -> AKM(0x06) = -56.512 dB error(+0.488 dB)
+ 0x05, // [115] = -57.500 dB -> AKM(0x05) = -58.095 dB error(-0.595 dB)
+ 0x05, // [116] = -58.000 dB -> AKM(0x05) = -58.095 dB error(-0.095 dB)
+ 0x05, // [117] = -58.500 dB -> AKM(0x05) = -58.095 dB error(+0.405 dB)
+ 0x05, // [118] = -59.000 dB -> AKM(0x05) = -58.095 dB error(+0.905 dB)
+ 0x04, // [119] = -59.500 dB -> AKM(0x04) = -60.034 dB error(-0.534 dB)
+ 0x04, // [120] = -60.000 dB -> AKM(0x04) = -60.034 dB error(-0.034 dB)
+ 0x04, // [121] = -60.500 dB -> AKM(0x04) = -60.034 dB error(+0.466 dB)
+ 0x04, // [122] = -61.000 dB -> AKM(0x04) = -60.034 dB error(+0.966 dB)
+ 0x03, // [123] = -61.500 dB -> AKM(0x03) = -62.532 dB error(-1.032 dB)
+ 0x03, // [124] = -62.000 dB -> AKM(0x03) = -62.532 dB error(-0.532 dB)
+ 0x03, // [125] = -62.500 dB -> AKM(0x03) = -62.532 dB error(-0.032 dB)
+ 0x03, // [126] = -63.000 dB -> AKM(0x03) = -62.532 dB error(+0.468 dB)
+ 0x03, // [127] = -63.500 dB -> AKM(0x03) = -62.532 dB error(+0.968 dB)
+ 0x03, // [128] = -64.000 dB -> AKM(0x03) = -62.532 dB error(+1.468 dB)
+ 0x02, // [129] = -64.500 dB -> AKM(0x02) = -66.054 dB error(-1.554 dB)
+ 0x02, // [130] = -65.000 dB -> AKM(0x02) = -66.054 dB error(-1.054 dB)
+ 0x02, // [131] = -65.500 dB -> AKM(0x02) = -66.054 dB error(-0.554 dB)
+ 0x02, // [132] = -66.000 dB -> AKM(0x02) = -66.054 dB error(-0.054 dB)
+ 0x02, // [133] = -66.500 dB -> AKM(0x02) = -66.054 dB error(+0.446 dB)
+ 0x02, // [134] = -67.000 dB -> AKM(0x02) = -66.054 dB error(+0.946 dB)
+ 0x02, // [135] = -67.500 dB -> AKM(0x02) = -66.054 dB error(+1.446 dB)
+ 0x02, // [136] = -68.000 dB -> AKM(0x02) = -66.054 dB error(+1.946 dB)
+ 0x02, // [137] = -68.500 dB -> AKM(0x02) = -66.054 dB error(+2.446 dB)
+ 0x02, // [138] = -69.000 dB -> AKM(0x02) = -66.054 dB error(+2.946 dB)
+ 0x01, // [139] = -69.500 dB -> AKM(0x01) = -72.075 dB error(-2.575 dB)
+ 0x01, // [140] = -70.000 dB -> AKM(0x01) = -72.075 dB error(-2.075 dB)
+ 0x01, // [141] = -70.500 dB -> AKM(0x01) = -72.075 dB error(-1.575 dB)
+ 0x01, // [142] = -71.000 dB -> AKM(0x01) = -72.075 dB error(-1.075 dB)
+ 0x01, // [143] = -71.500 dB -> AKM(0x01) = -72.075 dB error(-0.575 dB)
+ 0x01, // [144] = -72.000 dB -> AKM(0x01) = -72.075 dB error(-0.075 dB)
+ 0x01, // [145] = -72.500 dB -> AKM(0x01) = -72.075 dB error(+0.425 dB)
+ 0x01, // [146] = -73.000 dB -> AKM(0x01) = -72.075 dB error(+0.925 dB)
+ 0x00}; // [147] = -73.500 dB -> AKM(0x00) = mute error(+infini)
+
+/*
+ * pseudo-codec write entry
+ */
+static void vx2_write_akm(vx_core_t *chip, int reg, unsigned int data)
+{
+ unsigned int val;
+
+ if (reg == XX_CODEC_DAC_CONTROL_REGISTER) {
+ vx2_write_codec_reg(chip, data ? AKM_CODEC_MUTE_CMD : AKM_CODEC_UNMUTE_CMD);
+ return;
+ }
+
+ /* `data' is a value between 0x0 and VX2_AKM_LEVEL_MAX = 0x093, in the case of the AKM codecs, we need
+ a look up table, as there is no linear matching between the driver codec values
+ and the real dBu value
+ */
+ snd_assert(data < sizeof(vx2_akm_gains_lut), return);
+
+ switch (reg) {
+ case XX_CODEC_LEVEL_LEFT_REGISTER:
+ val = AKM_CODEC_LEFT_LEVEL_CMD;
+ break;
+ case XX_CODEC_LEVEL_RIGHT_REGISTER:
+ val = AKM_CODEC_RIGHT_LEVEL_CMD;
+ break;
+ default:
+ snd_BUG();
+ return;
+ }
+ val |= vx2_akm_gains_lut[data];
+
+ vx2_write_codec_reg(chip, val);
+}
+
+
+/*
+ * write codec bit for old VX222 board
+ */
+static void vx2_old_write_codec_bit(vx_core_t *chip, int codec, unsigned int data)
+{
+ int i;
+
+ /* activate access to codec registers */
+ vx_inl(chip, HIFREQ);
+
+ for (i = 0; i < 24; i++, data <<= 1)
+ vx_outl(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0));
+
+ /* Terminate access to codec registers */
+ vx_inl(chip, RUER);
+}
+
+
+/*
+ * reset codec bit
+ */
+static void vx2_reset_codec(vx_core_t *_chip)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+ /* Set the reset CODEC bit to 0. */
+ vx_outl(chip, CDSP, chip->regCDSP &~ VX_CDSP_CODEC_RESET_MASK);
+ vx_inl(chip, CDSP);
+ snd_vx_delay(_chip, 10);
+ /* Set the reset CODEC bit to 1. */
+ chip->regCDSP |= VX_CDSP_CODEC_RESET_MASK;
+ vx_outl(chip, CDSP, chip->regCDSP);
+ vx_inl(chip, CDSP);
+ if (_chip->type == VX_TYPE_BOARD) {
+ snd_vx_delay(_chip, 1);
+ return;
+ }
+
+ snd_vx_delay(_chip, 5); /* additionnel wait time for AKM's */
+
+ vx2_write_codec_reg(_chip, AKM_CODEC_POWER_CONTROL_CMD); /* DAC power up, ADC power up, Vref power down */
+
+ vx2_write_codec_reg(_chip, AKM_CODEC_CLOCK_FORMAT_CMD); /* default */
+ vx2_write_codec_reg(_chip, AKM_CODEC_MUTE_CMD); /* Mute = ON ,Deemphasis = OFF */
+ vx2_write_codec_reg(_chip, AKM_CODEC_RESET_OFF_CMD); /* DAC and ADC normal operation */
+
+ if (_chip->type == VX_TYPE_MIC) {
+ /* set up the micro input selector */
+ chip->regSELMIC = MICRO_SELECT_INPUT_NORM |
+ MICRO_SELECT_PREAMPLI_G_0 |
+ MICRO_SELECT_NOISE_T_52DB;
+
+ /* reset phantom power supply */
+ chip->regSELMIC &= ~MICRO_SELECT_PHANTOM_ALIM;
+
+ vx_outl(_chip, SELMIC, chip->regSELMIC);
+ }
+}
+
+
+/*
+ * change the audio source
+ */
+static void vx2_change_audio_source(vx_core_t *_chip, int src)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+ switch (src) {
+ case VX_AUDIO_SRC_DIGITAL:
+ chip->regCFG |= VX_CFG_DATAIN_SEL_MASK;
+ break;
+ default:
+ chip->regCFG &= ~VX_CFG_DATAIN_SEL_MASK;
+ break;
+ }
+ vx_outl(chip, CFG, chip->regCFG);
+}
+
+
+/*
+ * set the clock source
+ */
+static void vx2_set_clock_source(vx_core_t *_chip, int source)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+ if (source == INTERNAL_QUARTZ)
+ chip->regCFG &= ~VX_CFG_CLOCKIN_SEL_MASK;
+ else
+ chip->regCFG |= VX_CFG_CLOCKIN_SEL_MASK;
+ vx_outl(chip, CFG, chip->regCFG);
+}
+
+/*
+ * reset the board
+ */
+static void vx2_reset_board(vx_core_t *_chip, int cold_reset)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+ /* initialize the register values */
+ chip->regCDSP = VX_CDSP_CODEC_RESET_MASK | VX_CDSP_DSP_RESET_MASK ;
+ chip->regCFG = 0;
+}
+
+
+
+/*
+ * input level controls for VX222 Mic
+ */
+
+/* Micro level is specified to be adjustable from -96dB to 63 dB (board coded 0x00 ... 318),
+ * 318 = 210 + 36 + 36 + 36 (210 = +9dB variable) (3 * 36 = 3 steps of 18dB pre ampli)
+ * as we will mute if less than -110dB, so let's simply use line input coded levels and add constant offset !
+ */
+#define V2_MICRO_LEVEL_RANGE (318 - 255)
+
+static void vx2_set_input_level(struct snd_vx222 *chip)
+{
+ int i, miclevel, preamp;
+ unsigned int data;
+
+ miclevel = chip->mic_level;
+ miclevel += V2_MICRO_LEVEL_RANGE; /* add 318 - 0xff */
+ preamp = 0;
+ while (miclevel > 210) { /* limitation to +9dB of 3310 real gain */
+ preamp++; /* raise pre ampli + 18dB */
+ miclevel -= (18 * 2); /* lower level 18 dB (*2 because of 0.5 dB steps !) */
+ }
+ snd_assert(preamp < 4, return);
+
+ /* set pre-amp level */
+ chip->regSELMIC &= ~MICRO_SELECT_PREAMPLI_MASK;
+ chip->regSELMIC |= (preamp << MICRO_SELECT_PREAMPLI_OFFSET) & MICRO_SELECT_PREAMPLI_MASK;
+ vx_outl(chip, SELMIC, chip->regSELMIC);
+
+ data = (unsigned int)miclevel << 16 |
+ (unsigned int)chip->input_level[1] << 8 |
+ (unsigned int)chip->input_level[0];
+ vx_inl(chip, DATA); /* Activate input level programming */
+
+ /* We have to send 32 bits (4 x 8 bits) */
+ for (i = 0; i < 32; i++, data <<= 1)
+ vx_outl(chip, DATA, ((data & 0x80000000) ? VX_DATA_CODEC_MASK : 0));
+
+ vx_inl(chip, RUER); /* Terminate input level programming */
+}
+
+
+#define MIC_LEVEL_MAX 0xff
+
+/*
+ * controls API for input levels
+ */
+
+/* input levels */
+static int vx_input_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = MIC_LEVEL_MAX;
+ return 0;
+}
+
+static int vx_input_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ down(&_chip->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->input_level[0];
+ ucontrol->value.integer.value[1] = chip->input_level[1];
+ up(&_chip->mixer_mutex);
+ return 0;
+}
+
+static int vx_input_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ down(&_chip->mixer_mutex);
+ if (chip->input_level[0] != ucontrol->value.integer.value[0] ||
+ chip->input_level[1] != ucontrol->value.integer.value[1]) {
+ chip->input_level[0] = ucontrol->value.integer.value[0];
+ chip->input_level[1] = ucontrol->value.integer.value[1];
+ vx2_set_input_level(chip);
+ up(&_chip->mixer_mutex);
+ return 1;
+ }
+ up(&_chip->mixer_mutex);
+ return 0;
+}
+
+/* mic level */
+static int vx_mic_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = MIC_LEVEL_MAX;
+ return 0;
+}
+
+static int vx_mic_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ ucontrol->value.integer.value[0] = chip->mic_level;
+ return 0;
+}
+
+static int vx_mic_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ down(&_chip->mixer_mutex);
+ if (chip->mic_level != ucontrol->value.integer.value[0]) {
+ chip->mic_level = ucontrol->value.integer.value[0];
+ vx2_set_input_level(chip);
+ up(&_chip->mixer_mutex);
+ return 1;
+ }
+ up(&_chip->mixer_mutex);
+ return 0;
+}
+
+static snd_kcontrol_new_t vx_control_input_level = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .info = vx_input_level_info,
+ .get = vx_input_level_get,
+ .put = vx_input_level_put,
+};
+
+static snd_kcontrol_new_t vx_control_mic_level = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Volume",
+ .info = vx_mic_level_info,
+ .get = vx_mic_level_get,
+ .put = vx_mic_level_put,
+};
+
+/*
+ * FIXME: compressor/limiter implementation is missing yet...
+ */
+
+static int vx2_add_mic_controls(vx_core_t *_chip)
+{
+ struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ int err;
+
+ if (_chip->type != VX_TYPE_MIC)
+ return 0;
+
+ /* mute input levels */
+ chip->input_level[0] = chip->input_level[1] = 0;
+ chip->mic_level = 0;
+ vx2_set_input_level(chip);
+
+ /* controls */
+ if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_input_level, chip))) < 0)
+ return err;
+ if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip))) < 0)
+ return err;
+
+ return 0;
+}
+
+
+/*
+ * callbacks
+ */
+struct snd_vx_ops vx222_ops = {
+ .in8 = vx2_inb,
+ .in32 = vx2_inl,
+ .out8 = vx2_outb,
+ .out32 = vx2_outl,
+ .test_and_ack = vx2_test_and_ack,
+ .validate_irq = vx2_validate_irq,
+ .akm_write = vx2_write_akm,
+ .reset_codec = vx2_reset_codec,
+ .change_audio_source = vx2_change_audio_source,
+ .set_clock_source = vx2_set_clock_source,
+ .load_dsp = vx2_load_dsp,
+ .reset_dsp = vx2_reset_dsp,
+ .reset_board = vx2_reset_board,
+ .dma_write = vx2_dma_write,
+ .dma_read = vx2_dma_read,
+ .add_controls = vx2_add_mic_controls,
+};
+
+/* for old VX222 board */
+struct snd_vx_ops vx222_old_ops = {
+ .in8 = vx2_inb,
+ .in32 = vx2_inl,
+ .out8 = vx2_outb,
+ .out32 = vx2_outl,
+ .test_and_ack = vx2_test_and_ack,
+ .validate_irq = vx2_validate_irq,
+ .write_codec = vx2_old_write_codec_bit,
+ .reset_codec = vx2_reset_codec,
+ .change_audio_source = vx2_change_audio_source,
+ .set_clock_source = vx2_set_clock_source,
+ .load_dsp = vx2_load_dsp,
+ .reset_dsp = vx2_reset_dsp,
+ .reset_board = vx2_reset_board,
+ .dma_write = vx2_dma_write,
+ .dma_read = vx2_dma_read,
+};
+
diff --git a/sound/pci/ymfpci/Makefile b/sound/pci/ymfpci/Makefile
new file mode 100644
index 00000000000..8790c5f3ed0
--- /dev/null
+++ b/sound/pci/ymfpci/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-ymfpci-objs := ymfpci.o ymfpci_main.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
new file mode 100644
index 00000000000..9f3ef22df08
--- /dev/null
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -0,0 +1,372 @@
+/*
+ * The driver for the Yamaha's DS1/DS1E cards
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/ymfpci.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Yamaha DS-XG PCI");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF724},"
+ "{Yamaha,YMF724F},"
+ "{Yamaha,YMF740},"
+ "{Yamaha,YMF740C},"
+ "{Yamaha,YMF744},"
+ "{Yamaha,YMF754}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static long fm_port[SNDRV_CARDS];
+static long mpu_port[SNDRV_CARDS];
+#ifdef SUPPORT_JOYSTICK
+static long joystick_port[SNDRV_CARDS];
+#endif
+static int rear_switch[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the Yamaha DS-XG PCI soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the Yamaha DS-XG PCI soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Yamaha DS-XG soundcard.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 Port.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM OPL-3 Port.");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(joystick_port, long, NULL, 0444);
+MODULE_PARM_DESC(joystick_port, "Joystick port address");
+#endif
+module_param_array(rear_switch, bool, NULL, 0444);
+MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch");
+
+static struct pci_device_id snd_ymfpci_ids[] = {
+ { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724 */
+ { 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724F */
+ { 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740 */
+ { 0x1073, 0x000c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740C */
+ { 0x1073, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF744 */
+ { 0x1073, 0x0012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF754 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids);
+
+#ifdef SUPPORT_JOYSTICK
+static int __devinit snd_ymfpci_create_gameport(ymfpci_t *chip, int dev,
+ int legacy_ctrl, int legacy_ctrl2)
+{
+ struct gameport *gp;
+ struct resource *r = NULL;
+ int io_port = joystick_port[dev];
+
+ if (!io_port)
+ return -ENODEV;
+
+ if (chip->pci->device >= 0x0010) { /* YMF 744/754 */
+
+ if (io_port == 1) {
+ /* auto-detect */
+ if (!(io_port = pci_resource_start(chip->pci, 2)))
+ return -ENODEV;
+ }
+ } else {
+ if (io_port == 1) {
+ /* auto-detect */
+ for (io_port = 0x201; io_port <= 0x205; io_port++) {
+ if (io_port == 0x203)
+ continue;
+ if ((r = request_region(io_port, 1, "YMFPCI gameport")) != NULL)
+ break;
+ }
+ if (!r) {
+ printk(KERN_ERR "ymfpci: no gameport ports available\n");
+ return -EBUSY;
+ }
+ }
+ switch (io_port) {
+ case 0x201: legacy_ctrl2 |= 0 << 6; break;
+ case 0x202: legacy_ctrl2 |= 1 << 6; break;
+ case 0x204: legacy_ctrl2 |= 2 << 6; break;
+ case 0x205: legacy_ctrl2 |= 3 << 6; break;
+ default:
+ printk(KERN_ERR "ymfpci: invalid joystick port %#x", io_port);
+ return -EINVAL;
+ }
+ }
+
+ if (!r && !(r = request_region(io_port, 1, "YMFPCI gameport"))) {
+ printk(KERN_ERR "ymfpci: joystick port %#x is in use.\n", io_port);
+ return -EBUSY;
+ }
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "ymfpci: cannot allocate memory for gameport\n");
+ release_resource(r);
+ kfree_nocheck(r);
+ return -ENOMEM;
+ }
+
+
+ gameport_set_name(gp, "Yamaha YMF Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gp->io = io_port;
+ gameport_set_port_data(gp, r);
+
+ if (chip->pci->device >= 0x0010) /* YMF 744/754 */
+ pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE, io_port);
+
+ pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, legacy_ctrl | YMFPCI_LEGACY_JPEN);
+ pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
+
+ gameport_register_port(chip->gameport);
+
+ return 0;
+}
+
+void snd_ymfpci_free_gameport(ymfpci_t *chip)
+{
+ if (chip->gameport) {
+ struct resource *r = gameport_get_port_data(chip->gameport);
+
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+
+ release_resource(r);
+ kfree_nocheck(r);
+ }
+}
+#else
+static inline int snd_ymfpci_create_gameport(ymfpci_t *chip, int dev, int l, int l2) { return -ENOSYS; }
+void snd_ymfpci_free_gameport(ymfpci_t *chip) { }
+#endif /* SUPPORT_JOYSTICK */
+
+static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ snd_card_t *card;
+ struct resource *fm_res = NULL;
+ struct resource *mpu_res = NULL;
+ ymfpci_t *chip;
+ opl3_t *opl3;
+ char *str;
+ int err;
+ u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ switch (pci_id->device) {
+ case 0x0004: str = "YMF724"; break;
+ case 0x000d: str = "YMF724F"; break;
+ case 0x000a: str = "YMF740"; break;
+ case 0x000c: str = "YMF740C"; break;
+ case 0x0010: str = "YMF744"; break;
+ case 0x0012: str = "YMF754"; break;
+ default: str = "???"; break;
+ }
+
+ legacy_ctrl = 0;
+ legacy_ctrl2 = 0x0800; /* SBEN = 0, SMOD = 01, LAD = 0 */
+
+ if (pci_id->device >= 0x0010) { /* YMF 744/754 */
+ if (fm_port[dev] == 1) {
+ /* auto-detect */
+ fm_port[dev] = pci_resource_start(pci, 1);
+ }
+ if (fm_port[dev] > 0 &&
+ (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+ legacy_ctrl |= YMFPCI_LEGACY_FMEN;
+ pci_write_config_word(pci, PCIR_DSXG_FMBASE, fm_port[dev]);
+ }
+ if (mpu_port[dev] == 1) {
+ /* auto-detect */
+ mpu_port[dev] = pci_resource_start(pci, 1) + 0x20;
+ }
+ if (mpu_port[dev] > 0 &&
+ (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+ legacy_ctrl |= YMFPCI_LEGACY_MEN;
+ pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, mpu_port[dev]);
+ }
+ } else {
+ switch (fm_port[dev]) {
+ case 0x388: legacy_ctrl2 |= 0; break;
+ case 0x398: legacy_ctrl2 |= 1; break;
+ case 0x3a0: legacy_ctrl2 |= 2; break;
+ case 0x3a8: legacy_ctrl2 |= 3; break;
+ default: fm_port[dev] = 0; break;
+ }
+ if (fm_port[dev] > 0 &&
+ (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+ legacy_ctrl |= YMFPCI_LEGACY_FMEN;
+ } else {
+ legacy_ctrl2 &= ~YMFPCI_LEGACY2_FMIO;
+ fm_port[dev] = 0;
+ }
+ switch (mpu_port[dev]) {
+ case 0x330: legacy_ctrl2 |= 0 << 4; break;
+ case 0x300: legacy_ctrl2 |= 1 << 4; break;
+ case 0x332: legacy_ctrl2 |= 2 << 4; break;
+ case 0x334: legacy_ctrl2 |= 3 << 4; break;
+ default: mpu_port[dev] = 0; break;
+ }
+ if (mpu_port[dev] > 0 &&
+ (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+ legacy_ctrl |= YMFPCI_LEGACY_MEN;
+ } else {
+ legacy_ctrl2 &= ~YMFPCI_LEGACY2_MPUIO;
+ mpu_port[dev] = 0;
+ }
+ }
+ if (mpu_res) {
+ legacy_ctrl |= YMFPCI_LEGACY_MIEN;
+ legacy_ctrl2 |= YMFPCI_LEGACY2_IMOD;
+ }
+ pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl);
+ pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+ pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
+ if ((err = snd_ymfpci_create(card, pci,
+ old_legacy_ctrl,
+ &chip)) < 0) {
+ snd_card_free(card);
+ if (mpu_res) {
+ release_resource(mpu_res);
+ kfree_nocheck(mpu_res);
+ }
+ if (fm_res) {
+ release_resource(fm_res);
+ kfree_nocheck(fm_res);
+ }
+ return err;
+ }
+ chip->fm_res = fm_res;
+ chip->mpu_res = mpu_res;
+ strcpy(card->driver, str);
+ sprintf(card->shortname, "Yamaha DS-XG (%s)", str);
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname,
+ chip->reg_area_phys,
+ chip->irq);
+ if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ymfpci_mixer(chip, rear_switch[dev])) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_ymfpci_timer(chip, 0)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (chip->mpu_res) {
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
+ mpu_port[dev], 1,
+ pci->irq, 0, &chip->rawmidi)) < 0) {
+ printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]);
+ legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
+ pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+ }
+ }
+ if (chip->fm_res) {
+ if ((err = snd_opl3_create(card,
+ fm_port[dev],
+ fm_port[dev] + 2,
+ OPL3_HW_OPL3, 1, &opl3)) < 0) {
+ printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", fm_port[dev]);
+ legacy_ctrl &= ~YMFPCI_LEGACY_FMEN;
+ pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+ } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ snd_printk("cannot create opl3 hwdep\n");
+ return err;
+ }
+ }
+
+ snd_ymfpci_create_gameport(chip, dev, legacy_ctrl, legacy_ctrl2);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+ .name = "Yamaha DS-XG PCI",
+ .id_table = snd_ymfpci_ids,
+ .probe = snd_card_ymfpci_probe,
+ .remove = __devexit_p(snd_card_ymfpci_remove),
+ SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_ymfpci_init(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_ymfpci_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ymfpci_init)
+module_exit(alsa_card_ymfpci_exit)
diff --git a/sound/pci/ymfpci/ymfpci_image.h b/sound/pci/ymfpci/ymfpci_image.h
new file mode 100644
index 00000000000..1b074699166
--- /dev/null
+++ b/sound/pci/ymfpci/ymfpci_image.h
@@ -0,0 +1,1565 @@
+#ifndef _HWMCODE_
+#define _HWMCODE_
+
+static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = {
+ 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
+ 0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
+ 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
+ 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = {
+ 0x000007, 0x240007, 0x0C0007, 0x1C0007,
+ 0x060007, 0x700002, 0x000020, 0x030040,
+ 0x007104, 0x004286, 0x030040, 0x000F0D,
+ 0x000810, 0x20043A, 0x000282, 0x00020D,
+ 0x000810, 0x20043A, 0x001282, 0x200E82,
+ 0x001A82, 0x032D0D, 0x000810, 0x10043A,
+ 0x02D38D, 0x000810, 0x18043A, 0x00010D,
+ 0x020015, 0x0000FD, 0x000020, 0x038860,
+ 0x039060, 0x038060, 0x038040, 0x038040,
+ 0x038040, 0x018040, 0x000A7D, 0x038040,
+ 0x038040, 0x018040, 0x200402, 0x000882,
+ 0x08001A, 0x000904, 0x015986, 0x000007,
+ 0x260007, 0x000007, 0x000007, 0x018A06,
+ 0x000007, 0x030C8D, 0x000810, 0x18043A,
+ 0x260007, 0x00087D, 0x018042, 0x00160A,
+ 0x04A206, 0x000007, 0x00218D, 0x000810,
+ 0x08043A, 0x21C206, 0x000007, 0x0007FD,
+ 0x018042, 0x08000A, 0x000904, 0x029386,
+ 0x000195, 0x090D04, 0x000007, 0x000820,
+ 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD,
+ 0x032206, 0x018040, 0x000A7D, 0x038042,
+ 0x13804A, 0x18000A, 0x001820, 0x059060,
+ 0x058860, 0x018040, 0x0000FD, 0x018042,
+ 0x70000A, 0x000115, 0x071144, 0x032386,
+ 0x030000, 0x007020, 0x034A06, 0x018040,
+ 0x00348D, 0x000810, 0x08043A, 0x21EA06,
+ 0x000007, 0x02D38D, 0x000810, 0x18043A,
+ 0x018206, 0x000007, 0x240007, 0x000F8D,
+ 0x000810, 0x00163A, 0x002402, 0x005C02,
+ 0x0028FD, 0x000020, 0x018040, 0x08000D,
+ 0x000815, 0x510984, 0x000007, 0x00004D,
+ 0x000E5D, 0x000E02, 0x00418D, 0x000810,
+ 0x08043A, 0x2C8A06, 0x000007, 0x00008D,
+ 0x000924, 0x000F02, 0x00458D, 0x000810,
+ 0x08043A, 0x2C8A06, 0x000007, 0x00387D,
+ 0x018042, 0x08000A, 0x001015, 0x010984,
+ 0x018386, 0x000007, 0x01AA06, 0x000007,
+ 0x0008FD, 0x018042, 0x18000A, 0x001904,
+ 0x218086, 0x280007, 0x001810, 0x28043A,
+ 0x280C02, 0x00000D, 0x000810, 0x28143A,
+ 0x08808D, 0x000820, 0x0002FD, 0x018040,
+ 0x200007, 0x00020D, 0x189904, 0x000007,
+ 0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x055A86, 0x000007,
+ 0x000100, 0x000A20, 0x00047D, 0x018040,
+ 0x018042, 0x20000A, 0x003015, 0x012144,
+ 0x034986, 0x000007, 0x002104, 0x034986,
+ 0x000007, 0x000F8D, 0x000810, 0x280C3A,
+ 0x023944, 0x06C986, 0x000007, 0x001810,
+ 0x28043A, 0x08810D, 0x000820, 0x0002FD,
+ 0x018040, 0x200007, 0x002810, 0x78003A,
+ 0x00688D, 0x000810, 0x08043A, 0x288A06,
+ 0x000007, 0x00400D, 0x001015, 0x189904,
+ 0x292904, 0x393904, 0x000007, 0x060206,
+ 0x000007, 0x0004F5, 0x00007D, 0x000020,
+ 0x00008D, 0x010860, 0x018040, 0x00047D,
+ 0x038042, 0x21804A, 0x18000A, 0x021944,
+ 0x215886, 0x000007, 0x004075, 0x71F104,
+ 0x000007, 0x010042, 0x28000A, 0x002904,
+ 0x212086, 0x000007, 0x003C0D, 0x30A904,
+ 0x000007, 0x00077D, 0x018042, 0x08000A,
+ 0x000904, 0x07DA86, 0x00057D, 0x002820,
+ 0x03B060, 0x07F206, 0x018040, 0x003020,
+ 0x03A860, 0x018040, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x07FA86, 0x000007,
+ 0x00057D, 0x018042, 0x28040A, 0x000E8D,
+ 0x000810, 0x280C3A, 0x00000D, 0x000810,
+ 0x28143A, 0x09000D, 0x000820, 0x0002FD,
+ 0x018040, 0x200007, 0x003DFD, 0x000020,
+ 0x018040, 0x00107D, 0x008D8D, 0x000810,
+ 0x08043A, 0x288A06, 0x000007, 0x000815,
+ 0x08001A, 0x010984, 0x095186, 0x00137D,
+ 0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+ 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+ 0x038A60, 0x018040, 0x007FBD, 0x383DC4,
+ 0x000007, 0x001A7D, 0x001375, 0x018042,
+ 0x09004A, 0x10000A, 0x0B8D04, 0x139504,
+ 0x000007, 0x000820, 0x019060, 0x001104,
+ 0x212086, 0x010040, 0x0017FD, 0x018042,
+ 0x08000A, 0x000904, 0x212286, 0x000007,
+ 0x00197D, 0x038042, 0x09804A, 0x10000A,
+ 0x000924, 0x001664, 0x0011FD, 0x038042,
+ 0x2B804A, 0x19804A, 0x00008D, 0x218944,
+ 0x000007, 0x002244, 0x0AE186, 0x000007,
+ 0x001A64, 0x002A24, 0x00197D, 0x080102,
+ 0x100122, 0x000820, 0x039060, 0x018040,
+ 0x003DFD, 0x00008D, 0x000820, 0x018040,
+ 0x001375, 0x001A7D, 0x010042, 0x09804A,
+ 0x10000A, 0x00021D, 0x0189E4, 0x2992E4,
+ 0x309144, 0x000007, 0x00060D, 0x000A15,
+ 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4,
+ 0x000464, 0x01B3E4, 0x0232E4, 0x000464,
+ 0x000464, 0x000464, 0x000464, 0x00040D,
+ 0x08B1C4, 0x000007, 0x000820, 0x000BF5,
+ 0x030040, 0x00197D, 0x038042, 0x09804A,
+ 0x000A24, 0x08000A, 0x080E64, 0x000007,
+ 0x100122, 0x000820, 0x031060, 0x010040,
+ 0x0064AC, 0x00027D, 0x000020, 0x018040,
+ 0x00107D, 0x018042, 0x0011FD, 0x3B804A,
+ 0x09804A, 0x20000A, 0x000095, 0x1A1144,
+ 0x00A144, 0x0D2086, 0x00040D, 0x00B984,
+ 0x0D2186, 0x0018FD, 0x018042, 0x0010FD,
+ 0x09804A, 0x28000A, 0x000095, 0x010924,
+ 0x002A64, 0x0D1186, 0x000007, 0x002904,
+ 0x0D2286, 0x000007, 0x0D2A06, 0x080002,
+ 0x00008D, 0x00387D, 0x000820, 0x018040,
+ 0x00127D, 0x018042, 0x10000A, 0x003904,
+ 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984,
+ 0x0DA186, 0x000025, 0x0E7A06, 0x00002D,
+ 0x000015, 0x00082D, 0x02C78D, 0x000820,
+ 0x0EC206, 0x00000D, 0x7F8035, 0x00B984,
+ 0x0E7186, 0x400025, 0x00008D, 0x110944,
+ 0x000007, 0x00018D, 0x109504, 0x000007,
+ 0x009164, 0x000424, 0x000424, 0x000424,
+ 0x100102, 0x280002, 0x02C68D, 0x000820,
+ 0x0EC206, 0x00018D, 0x00042D, 0x00008D,
+ 0x109504, 0x000007, 0x00020D, 0x109184,
+ 0x000007, 0x02C70D, 0x000820, 0x00008D,
+ 0x0038FD, 0x018040, 0x003BFD, 0x001020,
+ 0x03A860, 0x000815, 0x313184, 0x212184,
+ 0x000007, 0x03B060, 0x03A060, 0x018040,
+ 0x0022FD, 0x000095, 0x010924, 0x000424,
+ 0x000424, 0x001264, 0x100102, 0x000820,
+ 0x039060, 0x018040, 0x001924, 0x00FB8D,
+ 0x00397D, 0x000820, 0x058040, 0x038042,
+ 0x09844A, 0x000606, 0x08040A, 0x000424,
+ 0x000424, 0x00117D, 0x018042, 0x08000A,
+ 0x000A24, 0x280502, 0x280C02, 0x09800D,
+ 0x000820, 0x0002FD, 0x018040, 0x200007,
+ 0x0022FD, 0x018042, 0x08000A, 0x000095,
+ 0x280DC4, 0x011924, 0x00197D, 0x018042,
+ 0x0011FD, 0x09804A, 0x10000A, 0x0000B5,
+ 0x113144, 0x0A8D04, 0x000007, 0x080A44,
+ 0x129504, 0x000007, 0x0023FD, 0x001020,
+ 0x038040, 0x101244, 0x000007, 0x000820,
+ 0x039060, 0x018040, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x10FA86, 0x000007,
+ 0x003BFD, 0x000100, 0x000A10, 0x0B807A,
+ 0x13804A, 0x090984, 0x000007, 0x000095,
+ 0x013D04, 0x118086, 0x10000A, 0x100002,
+ 0x090984, 0x000007, 0x038042, 0x11804A,
+ 0x090D04, 0x000007, 0x10000A, 0x090D84,
+ 0x000007, 0x00257D, 0x000820, 0x018040,
+ 0x00010D, 0x000810, 0x28143A, 0x00127D,
+ 0x018042, 0x20000A, 0x00197D, 0x018042,
+ 0x00117D, 0x31804A, 0x10000A, 0x003124,
+ 0x01280D, 0x00397D, 0x000820, 0x058040,
+ 0x038042, 0x09844A, 0x000606, 0x08040A,
+ 0x300102, 0x003124, 0x000424, 0x000424,
+ 0x001224, 0x280502, 0x001A4C, 0x130186,
+ 0x700002, 0x00002D, 0x030000, 0x00387D,
+ 0x018042, 0x10000A, 0x132A06, 0x002124,
+ 0x0000AD, 0x100002, 0x00010D, 0x000924,
+ 0x006B24, 0x01368D, 0x00397D, 0x000820,
+ 0x058040, 0x038042, 0x09844A, 0x000606,
+ 0x08040A, 0x003264, 0x00008D, 0x000A24,
+ 0x001020, 0x00227D, 0x018040, 0x013C0D,
+ 0x000810, 0x08043A, 0x29D206, 0x000007,
+ 0x002820, 0x00207D, 0x018040, 0x00117D,
+ 0x038042, 0x13804A, 0x33800A, 0x00387D,
+ 0x018042, 0x08000A, 0x000904, 0x163A86,
+ 0x000007, 0x00008D, 0x030964, 0x01478D,
+ 0x00397D, 0x000820, 0x058040, 0x038042,
+ 0x09844A, 0x000606, 0x08040A, 0x380102,
+ 0x000424, 0x000424, 0x001224, 0x0002FD,
+ 0x018042, 0x08000A, 0x000904, 0x14A286,
+ 0x000007, 0x280502, 0x001A4C, 0x163986,
+ 0x000007, 0x032164, 0x00632C, 0x003DFD,
+ 0x018042, 0x08000A, 0x000095, 0x090904,
+ 0x000007, 0x000820, 0x001A4C, 0x156186,
+ 0x018040, 0x030000, 0x157A06, 0x002124,
+ 0x00010D, 0x000924, 0x006B24, 0x015B8D,
+ 0x00397D, 0x000820, 0x058040, 0x038042,
+ 0x09844A, 0x000606, 0x08040A, 0x003A64,
+ 0x000095, 0x001224, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x15DA86, 0x000007,
+ 0x01628D, 0x000810, 0x08043A, 0x29D206,
+ 0x000007, 0x14D206, 0x000007, 0x007020,
+ 0x08010A, 0x10012A, 0x0020FD, 0x038860,
+ 0x039060, 0x018040, 0x00227D, 0x018042,
+ 0x003DFD, 0x08000A, 0x31844A, 0x000904,
+ 0x16D886, 0x18008B, 0x00008D, 0x189904,
+ 0x00312C, 0x17AA06, 0x000007, 0x00324C,
+ 0x173386, 0x000007, 0x001904, 0x173086,
+ 0x000007, 0x000095, 0x199144, 0x00222C,
+ 0x003124, 0x00636C, 0x000E3D, 0x001375,
+ 0x000BFD, 0x010042, 0x09804A, 0x10000A,
+ 0x038AEC, 0x0393EC, 0x00224C, 0x17A986,
+ 0x000007, 0x00008D, 0x189904, 0x00226C,
+ 0x00322C, 0x30050A, 0x301DAB, 0x002083,
+ 0x0018FD, 0x018042, 0x08000A, 0x018924,
+ 0x300502, 0x001083, 0x001875, 0x010042,
+ 0x10000A, 0x00008D, 0x010924, 0x001375,
+ 0x330542, 0x330CCB, 0x332CCB, 0x3334CB,
+ 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB,
+ 0x305C8B, 0x006083, 0x0002F5, 0x010042,
+ 0x08000A, 0x000904, 0x187A86, 0x000007,
+ 0x001E2D, 0x0005FD, 0x018042, 0x08000A,
+ 0x028924, 0x280502, 0x00060D, 0x000810,
+ 0x280C3A, 0x00008D, 0x000810, 0x28143A,
+ 0x0A808D, 0x000820, 0x0002F5, 0x010040,
+ 0x220007, 0x001275, 0x030042, 0x21004A,
+ 0x00008D, 0x1A0944, 0x000007, 0x01980D,
+ 0x000810, 0x08043A, 0x2B2206, 0x000007,
+ 0x0001F5, 0x030042, 0x0D004A, 0x10000A,
+ 0x089144, 0x000007, 0x000820, 0x010040,
+ 0x0025F5, 0x0A3144, 0x000007, 0x000820,
+ 0x032860, 0x030040, 0x00217D, 0x038042,
+ 0x0B804A, 0x10000A, 0x000820, 0x031060,
+ 0x030040, 0x00008D, 0x000124, 0x00012C,
+ 0x000E64, 0x001A64, 0x00636C, 0x08010A,
+ 0x10012A, 0x000820, 0x031060, 0x030040,
+ 0x0020FD, 0x018042, 0x08000A, 0x00227D,
+ 0x018042, 0x10000A, 0x000820, 0x031060,
+ 0x030040, 0x00197D, 0x018042, 0x08000A,
+ 0x0022FD, 0x038042, 0x10000A, 0x000820,
+ 0x031060, 0x030040, 0x090D04, 0x000007,
+ 0x000820, 0x030040, 0x038042, 0x0B804A,
+ 0x10000A, 0x000820, 0x031060, 0x030040,
+ 0x038042, 0x13804A, 0x19804A, 0x110D04,
+ 0x198D04, 0x000007, 0x08000A, 0x001020,
+ 0x031860, 0x030860, 0x030040, 0x00008D,
+ 0x0B0944, 0x000007, 0x000820, 0x010040,
+ 0x0005F5, 0x030042, 0x08000A, 0x000820,
+ 0x010040, 0x0000F5, 0x010042, 0x08000A,
+ 0x000904, 0x1C6086, 0x001E75, 0x030042,
+ 0x01044A, 0x000C0A, 0x1C7206, 0x000007,
+ 0x000402, 0x000C02, 0x00177D, 0x001AF5,
+ 0x018042, 0x03144A, 0x031C4A, 0x03244A,
+ 0x032C4A, 0x03344A, 0x033C4A, 0x03444A,
+ 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD,
+ 0x030042, 0x0B004A, 0x1B804A, 0x13804A,
+ 0x20000A, 0x089144, 0x19A144, 0x0389E4,
+ 0x0399EC, 0x005502, 0x005D0A, 0x030042,
+ 0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+ 0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+ 0x006502, 0x006D0A, 0x030042, 0x0B004A,
+ 0x19004A, 0x2B804A, 0x13804A, 0x21804A,
+ 0x30000A, 0x089144, 0x19A144, 0x2AB144,
+ 0x0389E4, 0x0399EC, 0x007502, 0x007D0A,
+ 0x03A9E4, 0x000702, 0x00107D, 0x000415,
+ 0x018042, 0x08000A, 0x0109E4, 0x000F02,
+ 0x002AF5, 0x0019FD, 0x010042, 0x09804A,
+ 0x10000A, 0x000934, 0x001674, 0x0029F5,
+ 0x010042, 0x10000A, 0x00917C, 0x002075,
+ 0x010042, 0x08000A, 0x000904, 0x1ED286,
+ 0x0026F5, 0x0027F5, 0x030042, 0x09004A,
+ 0x10000A, 0x000A3C, 0x00167C, 0x001A75,
+ 0x000BFD, 0x010042, 0x51804A, 0x48000A,
+ 0x160007, 0x001075, 0x010042, 0x282C0A,
+ 0x281D12, 0x282512, 0x001F32, 0x1E0007,
+ 0x0E0007, 0x001975, 0x010042, 0x002DF5,
+ 0x0D004A, 0x10000A, 0x009144, 0x1FB286,
+ 0x010042, 0x28340A, 0x000E5D, 0x00008D,
+ 0x000375, 0x000820, 0x010040, 0x05D2F4,
+ 0x54D104, 0x00735C, 0x205386, 0x000007,
+ 0x0C0007, 0x080007, 0x0A0007, 0x02040D,
+ 0x000810, 0x08043A, 0x332206, 0x000007,
+ 0x205A06, 0x000007, 0x080007, 0x002275,
+ 0x010042, 0x20000A, 0x002104, 0x212086,
+ 0x001E2D, 0x0002F5, 0x010042, 0x08000A,
+ 0x000904, 0x209286, 0x000007, 0x002010,
+ 0x30043A, 0x00057D, 0x0180C3, 0x08000A,
+ 0x028924, 0x280502, 0x280C02, 0x0A810D,
+ 0x000820, 0x0002F5, 0x010040, 0x220007,
+ 0x0004FD, 0x018042, 0x70000A, 0x030000,
+ 0x007020, 0x06FA06, 0x018040, 0x02180D,
+ 0x000810, 0x08043A, 0x2B2206, 0x000007,
+ 0x0002FD, 0x018042, 0x08000A, 0x000904,
+ 0x218A86, 0x000007, 0x01F206, 0x000007,
+ 0x000875, 0x0009FD, 0x00010D, 0x220A06,
+ 0x000295, 0x000B75, 0x00097D, 0x00000D,
+ 0x000515, 0x010042, 0x18000A, 0x001904,
+ 0x287886, 0x0006F5, 0x001020, 0x010040,
+ 0x0004F5, 0x000820, 0x010040, 0x000775,
+ 0x010042, 0x09804A, 0x10000A, 0x001124,
+ 0x000904, 0x22BA86, 0x000815, 0x080102,
+ 0x101204, 0x22DA06, 0x000575, 0x081204,
+ 0x000007, 0x100102, 0x000575, 0x000425,
+ 0x021124, 0x100102, 0x000820, 0x031060,
+ 0x010040, 0x001924, 0x287886, 0x00008D,
+ 0x000464, 0x009D04, 0x278886, 0x180102,
+ 0x000575, 0x010042, 0x28040A, 0x00018D,
+ 0x000924, 0x280D02, 0x00000D, 0x000924,
+ 0x281502, 0x10000D, 0x000820, 0x0002F5,
+ 0x010040, 0x200007, 0x001175, 0x0002FD,
+ 0x018042, 0x08000A, 0x000904, 0x23C286,
+ 0x000007, 0x000100, 0x080B20, 0x130B60,
+ 0x1B0B60, 0x030A60, 0x010040, 0x050042,
+ 0x3D004A, 0x35004A, 0x2D004A, 0x20000A,
+ 0x0006F5, 0x010042, 0x28140A, 0x0004F5,
+ 0x010042, 0x08000A, 0x000315, 0x010D04,
+ 0x24CA86, 0x004015, 0x000095, 0x010D04,
+ 0x24B886, 0x100022, 0x10002A, 0x24E206,
+ 0x000007, 0x333104, 0x2AA904, 0x000007,
+ 0x032124, 0x280502, 0x001124, 0x000424,
+ 0x000424, 0x003224, 0x00292C, 0x00636C,
+ 0x25F386, 0x000007, 0x02B164, 0x000464,
+ 0x000464, 0x00008D, 0x000A64, 0x280D02,
+ 0x10008D, 0x000820, 0x0002F5, 0x010040,
+ 0x220007, 0x00008D, 0x38B904, 0x000007,
+ 0x03296C, 0x30010A, 0x0002F5, 0x010042,
+ 0x08000A, 0x000904, 0x25BA86, 0x000007,
+ 0x02312C, 0x28050A, 0x00008D, 0x01096C,
+ 0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+ 0x010040, 0x220007, 0x001124, 0x000424,
+ 0x000424, 0x003224, 0x300102, 0x032944,
+ 0x267A86, 0x000007, 0x300002, 0x0004F5,
+ 0x010042, 0x08000A, 0x000315, 0x010D04,
+ 0x26C086, 0x003124, 0x000464, 0x300102,
+ 0x0002F5, 0x010042, 0x08000A, 0x000904,
+ 0x26CA86, 0x000007, 0x003124, 0x300502,
+ 0x003924, 0x300583, 0x000883, 0x0005F5,
+ 0x010042, 0x28040A, 0x00008D, 0x008124,
+ 0x280D02, 0x00008D, 0x008124, 0x281502,
+ 0x10018D, 0x000820, 0x0002F5, 0x010040,
+ 0x220007, 0x001025, 0x000575, 0x030042,
+ 0x09004A, 0x10000A, 0x0A0904, 0x121104,
+ 0x000007, 0x001020, 0x050860, 0x050040,
+ 0x0006FD, 0x018042, 0x09004A, 0x10000A,
+ 0x0000A5, 0x0A0904, 0x121104, 0x000007,
+ 0x000820, 0x019060, 0x010040, 0x0002F5,
+ 0x010042, 0x08000A, 0x000904, 0x284286,
+ 0x000007, 0x230A06, 0x000007, 0x000606,
+ 0x000007, 0x0002F5, 0x010042, 0x08000A,
+ 0x000904, 0x289286, 0x000007, 0x000100,
+ 0x080B20, 0x138B60, 0x1B8B60, 0x238B60,
+ 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60,
+ 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60,
+ 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60,
+ 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60,
+ 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60,
+ 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60,
+ 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60,
+ 0x000606, 0x018040, 0x00008D, 0x000A64,
+ 0x280D02, 0x000A24, 0x00027D, 0x018042,
+ 0x10000A, 0x001224, 0x0003FD, 0x018042,
+ 0x08000A, 0x000904, 0x2A8286, 0x000007,
+ 0x00018D, 0x000A24, 0x000464, 0x000464,
+ 0x080102, 0x000924, 0x000424, 0x000424,
+ 0x100102, 0x02000D, 0x009144, 0x2AD986,
+ 0x000007, 0x0001FD, 0x018042, 0x08000A,
+ 0x000A44, 0x2ABB86, 0x018042, 0x0A000D,
+ 0x000820, 0x0002FD, 0x018040, 0x200007,
+ 0x00027D, 0x001020, 0x000606, 0x018040,
+ 0x0002F5, 0x010042, 0x08000A, 0x000904,
+ 0x2B2A86, 0x000007, 0x00037D, 0x018042,
+ 0x08000A, 0x000904, 0x2B5A86, 0x000007,
+ 0x000075, 0x002E7D, 0x010042, 0x0B804A,
+ 0x000020, 0x000904, 0x000686, 0x010040,
+ 0x31844A, 0x30048B, 0x000883, 0x00008D,
+ 0x000810, 0x28143A, 0x00008D, 0x000810,
+ 0x280C3A, 0x000675, 0x010042, 0x08000A,
+ 0x003815, 0x010924, 0x280502, 0x0B000D,
+ 0x000820, 0x0002F5, 0x010040, 0x000606,
+ 0x220007, 0x000464, 0x000464, 0x000606,
+ 0x000007, 0x000134, 0x007F8D, 0x00093C,
+ 0x281D12, 0x282512, 0x001F32, 0x0E0007,
+ 0x00010D, 0x00037D, 0x000820, 0x018040,
+ 0x05D2F4, 0x000007, 0x080007, 0x00037D,
+ 0x018042, 0x08000A, 0x000904, 0x2D0286,
+ 0x000007, 0x000606, 0x000007, 0x000007,
+ 0x000012, 0x100007, 0x320007, 0x600007,
+ 0x100080, 0x48001A, 0x004904, 0x2D6186,
+ 0x000007, 0x001210, 0x58003A, 0x000145,
+ 0x5C5D04, 0x000007, 0x000080, 0x48001A,
+ 0x004904, 0x2DB186, 0x000007, 0x001210,
+ 0x50003A, 0x005904, 0x2E0886, 0x000045,
+ 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524,
+ 0x004224, 0x500102, 0x200502, 0x000082,
+ 0x40001A, 0x004104, 0x2E3986, 0x000007,
+ 0x003865, 0x40001A, 0x004020, 0x00104D,
+ 0x04C184, 0x301B86, 0x000040, 0x040007,
+ 0x000165, 0x000145, 0x004020, 0x000040,
+ 0x000765, 0x080080, 0x40001A, 0x004104,
+ 0x2EC986, 0x000007, 0x001210, 0x40003A,
+ 0x004104, 0x2F2286, 0x00004D, 0x0000CD,
+ 0x004810, 0x20043A, 0x000882, 0x40001A,
+ 0x004104, 0x2F3186, 0x000007, 0x004820,
+ 0x005904, 0x300886, 0x000040, 0x0007E5,
+ 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0,
+ 0x4216E0, 0x021260, 0x000040, 0x000032,
+ 0x400075, 0x00007D, 0x07D574, 0x200512,
+ 0x000082, 0x40001A, 0x004104, 0x2FE186,
+ 0x000007, 0x037206, 0x640007, 0x060007,
+ 0x0000E5, 0x000020, 0x000040, 0x000A65,
+ 0x000020, 0x020040, 0x020040, 0x000040,
+ 0x000165, 0x000042, 0x70000A, 0x007104,
+ 0x30A286, 0x000007, 0x018206, 0x640007,
+ 0x050000, 0x007020, 0x000040, 0x037206,
+ 0x640007, 0x000007, 0x00306D, 0x028860,
+ 0x029060, 0x08000A, 0x028860, 0x008040,
+ 0x100012, 0x00100D, 0x009184, 0x314186,
+ 0x000E0D, 0x009184, 0x325186, 0x000007,
+ 0x300007, 0x001020, 0x003B6D, 0x008040,
+ 0x000080, 0x08001A, 0x000904, 0x316186,
+ 0x000007, 0x001220, 0x000DED, 0x008040,
+ 0x008042, 0x10000A, 0x40000D, 0x109544,
+ 0x000007, 0x001020, 0x000DED, 0x008040,
+ 0x008042, 0x20040A, 0x000082, 0x08001A,
+ 0x000904, 0x31F186, 0x000007, 0x003B6D,
+ 0x008042, 0x08000A, 0x000E15, 0x010984,
+ 0x329B86, 0x600007, 0x08001A, 0x000C15,
+ 0x010984, 0x328386, 0x000020, 0x1A0007,
+ 0x0002ED, 0x008040, 0x620007, 0x00306D,
+ 0x028042, 0x0A804A, 0x000820, 0x0A804A,
+ 0x000606, 0x10804A, 0x000007, 0x282512,
+ 0x001F32, 0x05D2F4, 0x54D104, 0x00735C,
+ 0x000786, 0x000007, 0x0C0007, 0x0A0007,
+ 0x1C0007, 0x003465, 0x020040, 0x004820,
+ 0x025060, 0x40000A, 0x024060, 0x000040,
+ 0x454944, 0x000007, 0x004020, 0x003AE5,
+ 0x000040, 0x0028E5, 0x000042, 0x48000A,
+ 0x004904, 0x386886, 0x002C65, 0x000042,
+ 0x40000A, 0x0000D5, 0x454104, 0x000007,
+ 0x000655, 0x054504, 0x34F286, 0x0001D5,
+ 0x054504, 0x34F086, 0x002B65, 0x000042,
+ 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4,
+ 0x000007, 0x454504, 0x000007, 0x0000CD,
+ 0x444944, 0x000007, 0x454504, 0x000007,
+ 0x00014D, 0x554944, 0x000007, 0x045144,
+ 0x34E986, 0x002C65, 0x000042, 0x48000A,
+ 0x4CD104, 0x000007, 0x04C144, 0x34F386,
+ 0x000007, 0x160007, 0x002CE5, 0x040042,
+ 0x40000A, 0x004020, 0x000040, 0x002965,
+ 0x000042, 0x40000A, 0x004104, 0x356086,
+ 0x000007, 0x002402, 0x36A206, 0x005C02,
+ 0x0025E5, 0x000042, 0x40000A, 0x004274,
+ 0x002AE5, 0x000042, 0x40000A, 0x004274,
+ 0x500112, 0x0029E5, 0x000042, 0x40000A,
+ 0x004234, 0x454104, 0x000007, 0x004020,
+ 0x000040, 0x003EE5, 0x000020, 0x000040,
+ 0x002DE5, 0x400152, 0x50000A, 0x045144,
+ 0x364A86, 0x0000C5, 0x003EE5, 0x004020,
+ 0x000040, 0x002BE5, 0x000042, 0x40000A,
+ 0x404254, 0x000007, 0x002AE5, 0x004020,
+ 0x000040, 0x500132, 0x040134, 0x005674,
+ 0x0029E5, 0x020042, 0x42000A, 0x000042,
+ 0x50000A, 0x05417C, 0x0028E5, 0x000042,
+ 0x48000A, 0x0000C5, 0x4CC144, 0x371086,
+ 0x0026E5, 0x0027E5, 0x020042, 0x40004A,
+ 0x50000A, 0x00423C, 0x00567C, 0x0028E5,
+ 0x004820, 0x000040, 0x281D12, 0x282512,
+ 0x001F72, 0x002965, 0x000042, 0x40000A,
+ 0x004104, 0x37AA86, 0x0E0007, 0x160007,
+ 0x1E0007, 0x003EE5, 0x000042, 0x40000A,
+ 0x004104, 0x37E886, 0x002D65, 0x000042,
+ 0x28340A, 0x003465, 0x020042, 0x42004A,
+ 0x004020, 0x4A004A, 0x50004A, 0x05D2F4,
+ 0x54D104, 0x00735C, 0x385186, 0x000007,
+ 0x000606, 0x080007, 0x0C0007, 0x080007,
+ 0x0A0007, 0x0001E5, 0x020045, 0x004020,
+ 0x000060, 0x000365, 0x000040, 0x002E65,
+ 0x001A20, 0x0A1A60, 0x000040, 0x003465,
+ 0x020042, 0x42004A, 0x004020, 0x4A004A,
+ 0x000606, 0x50004A, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000
+};
+
+// --------------------------------------------
+// DS-1E Controller InstructionRAM Code
+// 1999/06/21
+// Buf441 slot is Enabled.
+// --------------------------------------------
+// 04/09 creat
+// 04/12 stop nise fix
+// 06/21 WorkingOff timming
+static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = {
+ 0x000007, 0x240007, 0x0C0007, 0x1C0007,
+ 0x060007, 0x700002, 0x000020, 0x030040,
+ 0x007104, 0x004286, 0x030040, 0x000F0D,
+ 0x000810, 0x20043A, 0x000282, 0x00020D,
+ 0x000810, 0x20043A, 0x001282, 0x200E82,
+ 0x00800D, 0x000810, 0x20043A, 0x001A82,
+ 0x03460D, 0x000810, 0x10043A, 0x02EC0D,
+ 0x000810, 0x18043A, 0x00010D, 0x020015,
+ 0x0000FD, 0x000020, 0x038860, 0x039060,
+ 0x038060, 0x038040, 0x038040, 0x038040,
+ 0x018040, 0x000A7D, 0x038040, 0x038040,
+ 0x018040, 0x200402, 0x000882, 0x08001A,
+ 0x000904, 0x017186, 0x000007, 0x260007,
+ 0x400007, 0x000007, 0x03258D, 0x000810,
+ 0x18043A, 0x260007, 0x284402, 0x00087D,
+ 0x018042, 0x00160A, 0x05A206, 0x000007,
+ 0x440007, 0x00230D, 0x000810, 0x08043A,
+ 0x22FA06, 0x000007, 0x0007FD, 0x018042,
+ 0x08000A, 0x000904, 0x02AB86, 0x000195,
+ 0x090D04, 0x000007, 0x000820, 0x0000F5,
+ 0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
+ 0x018040, 0x000A7D, 0x038042, 0x13804A,
+ 0x18000A, 0x001820, 0x059060, 0x058860,
+ 0x018040, 0x0000FD, 0x018042, 0x70000A,
+ 0x000115, 0x071144, 0x033B86, 0x030000,
+ 0x007020, 0x036206, 0x018040, 0x00360D,
+ 0x000810, 0x08043A, 0x232206, 0x000007,
+ 0x02EC0D, 0x000810, 0x18043A, 0x019A06,
+ 0x000007, 0x240007, 0x000F8D, 0x000810,
+ 0x00163A, 0x002402, 0x005C02, 0x0028FD,
+ 0x000020, 0x018040, 0x08000D, 0x000815,
+ 0x510984, 0x000007, 0x00004D, 0x000E5D,
+ 0x000E02, 0x00430D, 0x000810, 0x08043A,
+ 0x2E1206, 0x000007, 0x00008D, 0x000924,
+ 0x000F02, 0x00470D, 0x000810, 0x08043A,
+ 0x2E1206, 0x000007, 0x480480, 0x001210,
+ 0x28043A, 0x00778D, 0x000810, 0x280C3A,
+ 0x00068D, 0x000810, 0x28143A, 0x284402,
+ 0x03258D, 0x000810, 0x18043A, 0x07FF8D,
+ 0x000820, 0x0002FD, 0x018040, 0x260007,
+ 0x200007, 0x0002FD, 0x018042, 0x08000A,
+ 0x000904, 0x051286, 0x000007, 0x240007,
+ 0x02EC0D, 0x000810, 0x18043A, 0x00387D,
+ 0x018042, 0x08000A, 0x001015, 0x010984,
+ 0x019B86, 0x000007, 0x01B206, 0x000007,
+ 0x0008FD, 0x018042, 0x18000A, 0x001904,
+ 0x22B886, 0x280007, 0x001810, 0x28043A,
+ 0x280C02, 0x00000D, 0x000810, 0x28143A,
+ 0x08808D, 0x000820, 0x0002FD, 0x018040,
+ 0x200007, 0x00020D, 0x189904, 0x000007,
+ 0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x065A86, 0x000007,
+ 0x000100, 0x000A20, 0x00047D, 0x018040,
+ 0x018042, 0x20000A, 0x003015, 0x012144,
+ 0x036186, 0x000007, 0x002104, 0x036186,
+ 0x000007, 0x000F8D, 0x000810, 0x280C3A,
+ 0x023944, 0x07C986, 0x000007, 0x001810,
+ 0x28043A, 0x08810D, 0x000820, 0x0002FD,
+ 0x018040, 0x200007, 0x002810, 0x78003A,
+ 0x00788D, 0x000810, 0x08043A, 0x2A1206,
+ 0x000007, 0x00400D, 0x001015, 0x189904,
+ 0x292904, 0x393904, 0x000007, 0x070206,
+ 0x000007, 0x0004F5, 0x00007D, 0x000020,
+ 0x00008D, 0x010860, 0x018040, 0x00047D,
+ 0x038042, 0x21804A, 0x18000A, 0x021944,
+ 0x229086, 0x000007, 0x004075, 0x71F104,
+ 0x000007, 0x010042, 0x28000A, 0x002904,
+ 0x225886, 0x000007, 0x003C0D, 0x30A904,
+ 0x000007, 0x00077D, 0x018042, 0x08000A,
+ 0x000904, 0x08DA86, 0x00057D, 0x002820,
+ 0x03B060, 0x08F206, 0x018040, 0x003020,
+ 0x03A860, 0x018040, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x08FA86, 0x000007,
+ 0x00057D, 0x018042, 0x28040A, 0x000E8D,
+ 0x000810, 0x280C3A, 0x00000D, 0x000810,
+ 0x28143A, 0x09000D, 0x000820, 0x0002FD,
+ 0x018040, 0x200007, 0x003DFD, 0x000020,
+ 0x018040, 0x00107D, 0x009D8D, 0x000810,
+ 0x08043A, 0x2A1206, 0x000007, 0x000815,
+ 0x08001A, 0x010984, 0x0A5186, 0x00137D,
+ 0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+ 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+ 0x038A60, 0x018040, 0x00107D, 0x018042,
+ 0x08000A, 0x000215, 0x010984, 0x3A8186,
+ 0x000007, 0x007FBD, 0x383DC4, 0x000007,
+ 0x001A7D, 0x001375, 0x018042, 0x09004A,
+ 0x10000A, 0x0B8D04, 0x139504, 0x000007,
+ 0x000820, 0x019060, 0x001104, 0x225886,
+ 0x010040, 0x0017FD, 0x018042, 0x08000A,
+ 0x000904, 0x225A86, 0x000007, 0x00197D,
+ 0x038042, 0x09804A, 0x10000A, 0x000924,
+ 0x001664, 0x0011FD, 0x038042, 0x2B804A,
+ 0x19804A, 0x00008D, 0x218944, 0x000007,
+ 0x002244, 0x0C1986, 0x000007, 0x001A64,
+ 0x002A24, 0x00197D, 0x080102, 0x100122,
+ 0x000820, 0x039060, 0x018040, 0x003DFD,
+ 0x00008D, 0x000820, 0x018040, 0x001375,
+ 0x001A7D, 0x010042, 0x09804A, 0x10000A,
+ 0x00021D, 0x0189E4, 0x2992E4, 0x309144,
+ 0x000007, 0x00060D, 0x000A15, 0x000C1D,
+ 0x001025, 0x00A9E4, 0x012BE4, 0x000464,
+ 0x01B3E4, 0x0232E4, 0x000464, 0x000464,
+ 0x000464, 0x000464, 0x00040D, 0x08B1C4,
+ 0x000007, 0x000820, 0x000BF5, 0x030040,
+ 0x00197D, 0x038042, 0x09804A, 0x000A24,
+ 0x08000A, 0x080E64, 0x000007, 0x100122,
+ 0x000820, 0x031060, 0x010040, 0x0064AC,
+ 0x00027D, 0x000020, 0x018040, 0x00107D,
+ 0x018042, 0x0011FD, 0x3B804A, 0x09804A,
+ 0x20000A, 0x000095, 0x1A1144, 0x00A144,
+ 0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
+ 0x0018FD, 0x018042, 0x0010FD, 0x09804A,
+ 0x28000A, 0x000095, 0x010924, 0x002A64,
+ 0x0E4986, 0x000007, 0x002904, 0x0E5A86,
+ 0x000007, 0x0E6206, 0x080002, 0x00008D,
+ 0x00387D, 0x000820, 0x018040, 0x00127D,
+ 0x018042, 0x10000A, 0x003904, 0x0F0986,
+ 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
+ 0x000025, 0x0FB206, 0x00002D, 0x000015,
+ 0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
+ 0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
+ 0x400025, 0x00008D, 0x110944, 0x000007,
+ 0x00018D, 0x109504, 0x000007, 0x009164,
+ 0x000424, 0x000424, 0x000424, 0x100102,
+ 0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
+ 0x00018D, 0x00042D, 0x00008D, 0x109504,
+ 0x000007, 0x00020D, 0x109184, 0x000007,
+ 0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
+ 0x018040, 0x003BFD, 0x001020, 0x03A860,
+ 0x000815, 0x313184, 0x212184, 0x000007,
+ 0x03B060, 0x03A060, 0x018040, 0x0022FD,
+ 0x000095, 0x010924, 0x000424, 0x000424,
+ 0x001264, 0x100102, 0x000820, 0x039060,
+ 0x018040, 0x001924, 0x010F0D, 0x00397D,
+ 0x000820, 0x058040, 0x038042, 0x09844A,
+ 0x000606, 0x08040A, 0x000424, 0x000424,
+ 0x00117D, 0x018042, 0x08000A, 0x000A24,
+ 0x280502, 0x280C02, 0x09800D, 0x000820,
+ 0x0002FD, 0x018040, 0x200007, 0x0022FD,
+ 0x018042, 0x08000A, 0x000095, 0x280DC4,
+ 0x011924, 0x00197D, 0x018042, 0x0011FD,
+ 0x09804A, 0x10000A, 0x0000B5, 0x113144,
+ 0x0A8D04, 0x000007, 0x080A44, 0x129504,
+ 0x000007, 0x0023FD, 0x001020, 0x038040,
+ 0x101244, 0x000007, 0x000820, 0x039060,
+ 0x018040, 0x0002FD, 0x018042, 0x08000A,
+ 0x000904, 0x123286, 0x000007, 0x003BFD,
+ 0x000100, 0x000A10, 0x0B807A, 0x13804A,
+ 0x090984, 0x000007, 0x000095, 0x013D04,
+ 0x12B886, 0x10000A, 0x100002, 0x090984,
+ 0x000007, 0x038042, 0x11804A, 0x090D04,
+ 0x000007, 0x10000A, 0x090D84, 0x000007,
+ 0x00257D, 0x000820, 0x018040, 0x00010D,
+ 0x000810, 0x28143A, 0x00127D, 0x018042,
+ 0x20000A, 0x00197D, 0x018042, 0x00117D,
+ 0x31804A, 0x10000A, 0x003124, 0x013B8D,
+ 0x00397D, 0x000820, 0x058040, 0x038042,
+ 0x09844A, 0x000606, 0x08040A, 0x300102,
+ 0x003124, 0x000424, 0x000424, 0x001224,
+ 0x280502, 0x001A4C, 0x143986, 0x700002,
+ 0x00002D, 0x030000, 0x00387D, 0x018042,
+ 0x10000A, 0x146206, 0x002124, 0x0000AD,
+ 0x100002, 0x00010D, 0x000924, 0x006B24,
+ 0x014A0D, 0x00397D, 0x000820, 0x058040,
+ 0x038042, 0x09844A, 0x000606, 0x08040A,
+ 0x003264, 0x00008D, 0x000A24, 0x001020,
+ 0x00227D, 0x018040, 0x014F8D, 0x000810,
+ 0x08043A, 0x2B5A06, 0x000007, 0x002820,
+ 0x00207D, 0x018040, 0x00117D, 0x038042,
+ 0x13804A, 0x33800A, 0x00387D, 0x018042,
+ 0x08000A, 0x000904, 0x177286, 0x000007,
+ 0x00008D, 0x030964, 0x015B0D, 0x00397D,
+ 0x000820, 0x058040, 0x038042, 0x09844A,
+ 0x000606, 0x08040A, 0x380102, 0x000424,
+ 0x000424, 0x001224, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x15DA86, 0x000007,
+ 0x280502, 0x001A4C, 0x177186, 0x000007,
+ 0x032164, 0x00632C, 0x003DFD, 0x018042,
+ 0x08000A, 0x000095, 0x090904, 0x000007,
+ 0x000820, 0x001A4C, 0x169986, 0x018040,
+ 0x030000, 0x16B206, 0x002124, 0x00010D,
+ 0x000924, 0x006B24, 0x016F0D, 0x00397D,
+ 0x000820, 0x058040, 0x038042, 0x09844A,
+ 0x000606, 0x08040A, 0x003A64, 0x000095,
+ 0x001224, 0x0002FD, 0x018042, 0x08000A,
+ 0x000904, 0x171286, 0x000007, 0x01760D,
+ 0x000810, 0x08043A, 0x2B5A06, 0x000007,
+ 0x160A06, 0x000007, 0x007020, 0x08010A,
+ 0x10012A, 0x0020FD, 0x038860, 0x039060,
+ 0x018040, 0x00227D, 0x018042, 0x003DFD,
+ 0x08000A, 0x31844A, 0x000904, 0x181086,
+ 0x18008B, 0x00008D, 0x189904, 0x00312C,
+ 0x18E206, 0x000007, 0x00324C, 0x186B86,
+ 0x000007, 0x001904, 0x186886, 0x000007,
+ 0x000095, 0x199144, 0x00222C, 0x003124,
+ 0x00636C, 0x000E3D, 0x001375, 0x000BFD,
+ 0x010042, 0x09804A, 0x10000A, 0x038AEC,
+ 0x0393EC, 0x00224C, 0x18E186, 0x000007,
+ 0x00008D, 0x189904, 0x00226C, 0x00322C,
+ 0x30050A, 0x301DAB, 0x002083, 0x0018FD,
+ 0x018042, 0x08000A, 0x018924, 0x300502,
+ 0x001083, 0x001875, 0x010042, 0x10000A,
+ 0x00008D, 0x010924, 0x001375, 0x330542,
+ 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
+ 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
+ 0x006083, 0x0002F5, 0x010042, 0x08000A,
+ 0x000904, 0x19B286, 0x000007, 0x001E2D,
+ 0x0005FD, 0x018042, 0x08000A, 0x028924,
+ 0x280502, 0x00060D, 0x000810, 0x280C3A,
+ 0x00008D, 0x000810, 0x28143A, 0x0A808D,
+ 0x000820, 0x0002F5, 0x010040, 0x220007,
+ 0x001275, 0x030042, 0x21004A, 0x00008D,
+ 0x1A0944, 0x000007, 0x01AB8D, 0x000810,
+ 0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
+ 0x030042, 0x0D004A, 0x10000A, 0x089144,
+ 0x000007, 0x000820, 0x010040, 0x0025F5,
+ 0x0A3144, 0x000007, 0x000820, 0x032860,
+ 0x030040, 0x00217D, 0x038042, 0x0B804A,
+ 0x10000A, 0x000820, 0x031060, 0x030040,
+ 0x00008D, 0x000124, 0x00012C, 0x000E64,
+ 0x001A64, 0x00636C, 0x08010A, 0x10012A,
+ 0x000820, 0x031060, 0x030040, 0x0020FD,
+ 0x018042, 0x08000A, 0x00227D, 0x018042,
+ 0x10000A, 0x000820, 0x031060, 0x030040,
+ 0x00197D, 0x018042, 0x08000A, 0x0022FD,
+ 0x038042, 0x10000A, 0x000820, 0x031060,
+ 0x030040, 0x090D04, 0x000007, 0x000820,
+ 0x030040, 0x038042, 0x0B804A, 0x10000A,
+ 0x000820, 0x031060, 0x030040, 0x038042,
+ 0x13804A, 0x19804A, 0x110D04, 0x198D04,
+ 0x000007, 0x08000A, 0x001020, 0x031860,
+ 0x030860, 0x030040, 0x00008D, 0x0B0944,
+ 0x000007, 0x000820, 0x010040, 0x0005F5,
+ 0x030042, 0x08000A, 0x000820, 0x010040,
+ 0x0000F5, 0x010042, 0x08000A, 0x000904,
+ 0x1D9886, 0x001E75, 0x030042, 0x01044A,
+ 0x000C0A, 0x1DAA06, 0x000007, 0x000402,
+ 0x000C02, 0x00177D, 0x001AF5, 0x018042,
+ 0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
+ 0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
+ 0x00043D, 0x0013F5, 0x001AFD, 0x030042,
+ 0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+ 0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+ 0x005502, 0x005D0A, 0x030042, 0x0B004A,
+ 0x1B804A, 0x13804A, 0x20000A, 0x089144,
+ 0x19A144, 0x0389E4, 0x0399EC, 0x006502,
+ 0x006D0A, 0x030042, 0x0B004A, 0x19004A,
+ 0x2B804A, 0x13804A, 0x21804A, 0x30000A,
+ 0x089144, 0x19A144, 0x2AB144, 0x0389E4,
+ 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
+ 0x000702, 0x00107D, 0x000415, 0x018042,
+ 0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
+ 0x0019FD, 0x010042, 0x09804A, 0x10000A,
+ 0x000934, 0x001674, 0x0029F5, 0x010042,
+ 0x10000A, 0x00917C, 0x002075, 0x010042,
+ 0x08000A, 0x000904, 0x200A86, 0x0026F5,
+ 0x0027F5, 0x030042, 0x09004A, 0x10000A,
+ 0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
+ 0x010042, 0x51804A, 0x48000A, 0x160007,
+ 0x001075, 0x010042, 0x282C0A, 0x281D12,
+ 0x282512, 0x001F32, 0x1E0007, 0x0E0007,
+ 0x001975, 0x010042, 0x002DF5, 0x0D004A,
+ 0x10000A, 0x009144, 0x20EA86, 0x010042,
+ 0x28340A, 0x000E5D, 0x00008D, 0x000375,
+ 0x000820, 0x010040, 0x05D2F4, 0x54D104,
+ 0x00735C, 0x218B86, 0x000007, 0x0C0007,
+ 0x080007, 0x0A0007, 0x02178D, 0x000810,
+ 0x08043A, 0x34B206, 0x000007, 0x219206,
+ 0x000007, 0x080007, 0x002275, 0x010042,
+ 0x20000A, 0x002104, 0x225886, 0x001E2D,
+ 0x0002F5, 0x010042, 0x08000A, 0x000904,
+ 0x21CA86, 0x000007, 0x002010, 0x30043A,
+ 0x00057D, 0x0180C3, 0x08000A, 0x028924,
+ 0x280502, 0x280C02, 0x0A810D, 0x000820,
+ 0x0002F5, 0x010040, 0x220007, 0x0004FD,
+ 0x018042, 0x70000A, 0x030000, 0x007020,
+ 0x07FA06, 0x018040, 0x022B8D, 0x000810,
+ 0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
+ 0x018042, 0x08000A, 0x000904, 0x22C286,
+ 0x000007, 0x020206, 0x000007, 0x000875,
+ 0x0009FD, 0x00010D, 0x234206, 0x000295,
+ 0x000B75, 0x00097D, 0x00000D, 0x000515,
+ 0x010042, 0x18000A, 0x001904, 0x2A0086,
+ 0x0006F5, 0x001020, 0x010040, 0x0004F5,
+ 0x000820, 0x010040, 0x000775, 0x010042,
+ 0x09804A, 0x10000A, 0x001124, 0x000904,
+ 0x23F286, 0x000815, 0x080102, 0x101204,
+ 0x241206, 0x000575, 0x081204, 0x000007,
+ 0x100102, 0x000575, 0x000425, 0x021124,
+ 0x100102, 0x000820, 0x031060, 0x010040,
+ 0x001924, 0x2A0086, 0x00008D, 0x000464,
+ 0x009D04, 0x291086, 0x180102, 0x000575,
+ 0x010042, 0x28040A, 0x00018D, 0x000924,
+ 0x280D02, 0x00000D, 0x000924, 0x281502,
+ 0x10000D, 0x000820, 0x0002F5, 0x010040,
+ 0x200007, 0x001175, 0x0002FD, 0x018042,
+ 0x08000A, 0x000904, 0x24FA86, 0x000007,
+ 0x000100, 0x080B20, 0x130B60, 0x1B0B60,
+ 0x030A60, 0x010040, 0x050042, 0x3D004A,
+ 0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
+ 0x010042, 0x28140A, 0x0004F5, 0x010042,
+ 0x08000A, 0x000315, 0x010D04, 0x260286,
+ 0x004015, 0x000095, 0x010D04, 0x25F086,
+ 0x100022, 0x10002A, 0x261A06, 0x000007,
+ 0x333104, 0x2AA904, 0x000007, 0x032124,
+ 0x280502, 0x284402, 0x001124, 0x400102,
+ 0x000424, 0x000424, 0x003224, 0x00292C,
+ 0x00636C, 0x277386, 0x000007, 0x02B164,
+ 0x000464, 0x000464, 0x00008D, 0x000A64,
+ 0x280D02, 0x10008D, 0x000820, 0x0002F5,
+ 0x010040, 0x220007, 0x00008D, 0x38B904,
+ 0x000007, 0x03296C, 0x30010A, 0x0002F5,
+ 0x010042, 0x08000A, 0x000904, 0x270286,
+ 0x000007, 0x00212C, 0x28050A, 0x00316C,
+ 0x00046C, 0x00046C, 0x28450A, 0x001124,
+ 0x006B64, 0x100102, 0x00008D, 0x01096C,
+ 0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+ 0x010040, 0x220007, 0x004124, 0x000424,
+ 0x000424, 0x003224, 0x300102, 0x032944,
+ 0x27FA86, 0x000007, 0x300002, 0x0004F5,
+ 0x010042, 0x08000A, 0x000315, 0x010D04,
+ 0x284086, 0x003124, 0x000464, 0x300102,
+ 0x0002F5, 0x010042, 0x08000A, 0x000904,
+ 0x284A86, 0x000007, 0x284402, 0x003124,
+ 0x300502, 0x003924, 0x300583, 0x000883,
+ 0x0005F5, 0x010042, 0x28040A, 0x00008D,
+ 0x008124, 0x280D02, 0x00008D, 0x008124,
+ 0x281502, 0x10018D, 0x000820, 0x0002F5,
+ 0x010040, 0x220007, 0x001025, 0x000575,
+ 0x030042, 0x09004A, 0x10000A, 0x0A0904,
+ 0x121104, 0x000007, 0x001020, 0x050860,
+ 0x050040, 0x0006FD, 0x018042, 0x09004A,
+ 0x10000A, 0x0000A5, 0x0A0904, 0x121104,
+ 0x000007, 0x000820, 0x019060, 0x010040,
+ 0x0002F5, 0x010042, 0x08000A, 0x000904,
+ 0x29CA86, 0x000007, 0x244206, 0x000007,
+ 0x000606, 0x000007, 0x0002F5, 0x010042,
+ 0x08000A, 0x000904, 0x2A1A86, 0x000007,
+ 0x000100, 0x080B20, 0x138B60, 0x1B8B60,
+ 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
+ 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
+ 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
+ 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
+ 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
+ 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+ 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
+ 0x038A60, 0x000606, 0x018040, 0x00008D,
+ 0x000A64, 0x280D02, 0x000A24, 0x00027D,
+ 0x018042, 0x10000A, 0x001224, 0x0003FD,
+ 0x018042, 0x08000A, 0x000904, 0x2C0A86,
+ 0x000007, 0x00018D, 0x000A24, 0x000464,
+ 0x000464, 0x080102, 0x000924, 0x000424,
+ 0x000424, 0x100102, 0x02000D, 0x009144,
+ 0x2C6186, 0x000007, 0x0001FD, 0x018042,
+ 0x08000A, 0x000A44, 0x2C4386, 0x018042,
+ 0x0A000D, 0x000820, 0x0002FD, 0x018040,
+ 0x200007, 0x00027D, 0x001020, 0x000606,
+ 0x018040, 0x0002F5, 0x010042, 0x08000A,
+ 0x000904, 0x2CB286, 0x000007, 0x00037D,
+ 0x018042, 0x08000A, 0x000904, 0x2CE286,
+ 0x000007, 0x000075, 0x002E7D, 0x010042,
+ 0x0B804A, 0x000020, 0x000904, 0x000686,
+ 0x010040, 0x31844A, 0x30048B, 0x000883,
+ 0x00008D, 0x000810, 0x28143A, 0x00008D,
+ 0x000810, 0x280C3A, 0x000675, 0x010042,
+ 0x08000A, 0x003815, 0x010924, 0x280502,
+ 0x0B000D, 0x000820, 0x0002F5, 0x010040,
+ 0x000606, 0x220007, 0x000464, 0x000464,
+ 0x000606, 0x000007, 0x000134, 0x007F8D,
+ 0x00093C, 0x281D12, 0x282512, 0x001F32,
+ 0x0E0007, 0x00010D, 0x00037D, 0x000820,
+ 0x018040, 0x05D2F4, 0x000007, 0x080007,
+ 0x00037D, 0x018042, 0x08000A, 0x000904,
+ 0x2E8A86, 0x000007, 0x000606, 0x000007,
+ 0x000007, 0x000012, 0x100007, 0x320007,
+ 0x600007, 0x460007, 0x100080, 0x48001A,
+ 0x004904, 0x2EF186, 0x000007, 0x001210,
+ 0x58003A, 0x000145, 0x5C5D04, 0x000007,
+ 0x000080, 0x48001A, 0x004904, 0x2F4186,
+ 0x000007, 0x001210, 0x50003A, 0x005904,
+ 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
+ 0x7FFF7D, 0x07D524, 0x004224, 0x500102,
+ 0x200502, 0x000082, 0x40001A, 0x004104,
+ 0x2FC986, 0x000007, 0x003865, 0x40001A,
+ 0x004020, 0x00104D, 0x04C184, 0x31AB86,
+ 0x000040, 0x040007, 0x000165, 0x000145,
+ 0x004020, 0x000040, 0x000765, 0x080080,
+ 0x40001A, 0x004104, 0x305986, 0x000007,
+ 0x001210, 0x40003A, 0x004104, 0x30B286,
+ 0x00004D, 0x0000CD, 0x004810, 0x20043A,
+ 0x000882, 0x40001A, 0x004104, 0x30C186,
+ 0x000007, 0x004820, 0x005904, 0x319886,
+ 0x000040, 0x0007E5, 0x200480, 0x2816A0,
+ 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
+ 0x000040, 0x000032, 0x400075, 0x00007D,
+ 0x07D574, 0x200512, 0x000082, 0x40001A,
+ 0x004104, 0x317186, 0x000007, 0x038A06,
+ 0x640007, 0x0000E5, 0x000020, 0x000040,
+ 0x000A65, 0x000020, 0x020040, 0x020040,
+ 0x000040, 0x000165, 0x000042, 0x70000A,
+ 0x007104, 0x323286, 0x000007, 0x060007,
+ 0x019A06, 0x640007, 0x050000, 0x007020,
+ 0x000040, 0x038A06, 0x640007, 0x000007,
+ 0x00306D, 0x028860, 0x029060, 0x08000A,
+ 0x028860, 0x008040, 0x100012, 0x00100D,
+ 0x009184, 0x32D186, 0x000E0D, 0x009184,
+ 0x33E186, 0x000007, 0x300007, 0x001020,
+ 0x003B6D, 0x008040, 0x000080, 0x08001A,
+ 0x000904, 0x32F186, 0x000007, 0x001220,
+ 0x000DED, 0x008040, 0x008042, 0x10000A,
+ 0x40000D, 0x109544, 0x000007, 0x001020,
+ 0x000DED, 0x008040, 0x008042, 0x20040A,
+ 0x000082, 0x08001A, 0x000904, 0x338186,
+ 0x000007, 0x003B6D, 0x008042, 0x08000A,
+ 0x000E15, 0x010984, 0x342B86, 0x600007,
+ 0x08001A, 0x000C15, 0x010984, 0x341386,
+ 0x000020, 0x1A0007, 0x0002ED, 0x008040,
+ 0x620007, 0x00306D, 0x028042, 0x0A804A,
+ 0x000820, 0x0A804A, 0x000606, 0x10804A,
+ 0x000007, 0x282512, 0x001F32, 0x05D2F4,
+ 0x54D104, 0x00735C, 0x000786, 0x000007,
+ 0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
+ 0x020040, 0x004820, 0x025060, 0x40000A,
+ 0x024060, 0x000040, 0x454944, 0x000007,
+ 0x004020, 0x003AE5, 0x000040, 0x0028E5,
+ 0x000042, 0x48000A, 0x004904, 0x39F886,
+ 0x002C65, 0x000042, 0x40000A, 0x0000D5,
+ 0x454104, 0x000007, 0x000655, 0x054504,
+ 0x368286, 0x0001D5, 0x054504, 0x368086,
+ 0x002B65, 0x000042, 0x003AE5, 0x50004A,
+ 0x40000A, 0x45C3D4, 0x000007, 0x454504,
+ 0x000007, 0x0000CD, 0x444944, 0x000007,
+ 0x454504, 0x000007, 0x00014D, 0x554944,
+ 0x000007, 0x045144, 0x367986, 0x002C65,
+ 0x000042, 0x48000A, 0x4CD104, 0x000007,
+ 0x04C144, 0x368386, 0x000007, 0x160007,
+ 0x002CE5, 0x040042, 0x40000A, 0x004020,
+ 0x000040, 0x002965, 0x000042, 0x40000A,
+ 0x004104, 0x36F086, 0x000007, 0x002402,
+ 0x383206, 0x005C02, 0x0025E5, 0x000042,
+ 0x40000A, 0x004274, 0x002AE5, 0x000042,
+ 0x40000A, 0x004274, 0x500112, 0x0029E5,
+ 0x000042, 0x40000A, 0x004234, 0x454104,
+ 0x000007, 0x004020, 0x000040, 0x003EE5,
+ 0x000020, 0x000040, 0x002DE5, 0x400152,
+ 0x50000A, 0x045144, 0x37DA86, 0x0000C5,
+ 0x003EE5, 0x004020, 0x000040, 0x002BE5,
+ 0x000042, 0x40000A, 0x404254, 0x000007,
+ 0x002AE5, 0x004020, 0x000040, 0x500132,
+ 0x040134, 0x005674, 0x0029E5, 0x020042,
+ 0x42000A, 0x000042, 0x50000A, 0x05417C,
+ 0x0028E5, 0x000042, 0x48000A, 0x0000C5,
+ 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
+ 0x020042, 0x40004A, 0x50000A, 0x00423C,
+ 0x00567C, 0x0028E5, 0x004820, 0x000040,
+ 0x281D12, 0x282512, 0x001F72, 0x002965,
+ 0x000042, 0x40000A, 0x004104, 0x393A86,
+ 0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
+ 0x000042, 0x40000A, 0x004104, 0x397886,
+ 0x002D65, 0x000042, 0x28340A, 0x003465,
+ 0x020042, 0x42004A, 0x004020, 0x4A004A,
+ 0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
+ 0x39E186, 0x000007, 0x000606, 0x080007,
+ 0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
+ 0x020045, 0x004020, 0x000060, 0x000365,
+ 0x000040, 0x002E65, 0x001A20, 0x0A1A60,
+ 0x000040, 0x003465, 0x020042, 0x42004A,
+ 0x004020, 0x4A004A, 0x000606, 0x50004A,
+ 0x0017FD, 0x018042, 0x08000A, 0x000904,
+ 0x225A86, 0x000007, 0x00107D, 0x018042,
+ 0x0011FD, 0x33804A, 0x19804A, 0x20000A,
+ 0x000095, 0x2A1144, 0x01A144, 0x3B9086,
+ 0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
+ 0x018042, 0x0010FD, 0x09804A, 0x38000A,
+ 0x000095, 0x010924, 0x003A64, 0x3B8186,
+ 0x000007, 0x003904, 0x3B9286, 0x000007,
+ 0x3B9A06, 0x00000D, 0x00008D, 0x000820,
+ 0x00387D, 0x018040, 0x700002, 0x00117D,
+ 0x018042, 0x00197D, 0x29804A, 0x30000A,
+ 0x380002, 0x003124, 0x000424, 0x000424,
+ 0x002A24, 0x280502, 0x00068D, 0x000810,
+ 0x28143A, 0x00750D, 0x00B124, 0x002264,
+ 0x3D0386, 0x284402, 0x000810, 0x280C3A,
+ 0x0B800D, 0x000820, 0x0002FD, 0x018040,
+ 0x200007, 0x00758D, 0x00B124, 0x100102,
+ 0x012144, 0x3E4986, 0x001810, 0x10003A,
+ 0x00387D, 0x018042, 0x08000A, 0x000904,
+ 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
+ 0x00008D, 0x023164, 0x000A64, 0x280D02,
+ 0x0B808D, 0x000820, 0x0002FD, 0x018040,
+ 0x200007, 0x00387D, 0x018042, 0x08000A,
+ 0x000904, 0x3E3286, 0x030000, 0x0002FD,
+ 0x018042, 0x08000A, 0x000904, 0x3D8286,
+ 0x000007, 0x002810, 0x28043A, 0x00750D,
+ 0x030924, 0x002264, 0x280D02, 0x02316C,
+ 0x28450A, 0x0B810D, 0x000820, 0x0002FD,
+ 0x018040, 0x200007, 0x00008D, 0x000A24,
+ 0x3E4A06, 0x100102, 0x001810, 0x10003A,
+ 0x0000BD, 0x003810, 0x30043A, 0x00187D,
+ 0x018042, 0x0018FD, 0x09804A, 0x20000A,
+ 0x0000AD, 0x028924, 0x07212C, 0x001010,
+ 0x300583, 0x300D8B, 0x3014BB, 0x301C83,
+ 0x002083, 0x00137D, 0x038042, 0x33844A,
+ 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
+ 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
+ 0x001E0D, 0x0005FD, 0x018042, 0x20000A,
+ 0x020924, 0x00068D, 0x00A96C, 0x00009D,
+ 0x0002FD, 0x018042, 0x08000A, 0x000904,
+ 0x3F6A86, 0x000007, 0x280502, 0x280D0A,
+ 0x284402, 0x001810, 0x28143A, 0x0C008D,
+ 0x000820, 0x0002FD, 0x018040, 0x220007,
+ 0x003904, 0x225886, 0x001E0D, 0x00057D,
+ 0x018042, 0x20000A, 0x020924, 0x0000A5,
+ 0x0002FD, 0x018042, 0x08000A, 0x000904,
+ 0x402A86, 0x000007, 0x280502, 0x280C02,
+ 0x002010, 0x28143A, 0x0C010D, 0x000820,
+ 0x0002FD, 0x018040, 0x225A06, 0x220007,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000
+};
+
+#endif //_HWMCODE_
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
new file mode 100644
index 00000000000..05f1629760b
--- /dev/null
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -0,0 +1,2273 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Routines for control of YMF724/740/744/754 chips
+ *
+ * BUGS:
+ * --
+ *
+ * TODO:
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/ymfpci.h>
+#include <sound/asoundef.h>
+#include <sound/mpu401.h>
+
+#include <asm/io.h>
+
+/*
+ * constants
+ */
+
+/*
+ * common I/O routines
+ */
+
+static void snd_ymfpci_irq_wait(ymfpci_t *chip);
+
+static inline u8 snd_ymfpci_readb(ymfpci_t *chip, u32 offset)
+{
+ return readb(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writeb(ymfpci_t *chip, u32 offset, u8 val)
+{
+ writeb(val, chip->reg_area_virt + offset);
+}
+
+static inline u16 snd_ymfpci_readw(ymfpci_t *chip, u32 offset)
+{
+ return readw(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writew(ymfpci_t *chip, u32 offset, u16 val)
+{
+ writew(val, chip->reg_area_virt + offset);
+}
+
+static inline u32 snd_ymfpci_readl(ymfpci_t *chip, u32 offset)
+{
+ return readl(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val)
+{
+ writel(val, chip->reg_area_virt + offset);
+}
+
+static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary)
+{
+ signed long end_time;
+ u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
+
+ end_time = (jiffies + ((3 * HZ) / 4)) + 1;
+ do {
+ if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)
+ return 0;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (end_time - (signed long)jiffies >= 0);
+ snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));
+ return -EBUSY;
+}
+
+static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val)
+{
+ ymfpci_t *chip = ac97->private_data;
+ u32 cmd;
+
+ snd_ymfpci_codec_ready(chip, 0);
+ cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;
+ snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd);
+}
+
+static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg)
+{
+ ymfpci_t *chip = ac97->private_data;
+
+ if (snd_ymfpci_codec_ready(chip, 0))
+ return ~0;
+ snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);
+ if (snd_ymfpci_codec_ready(chip, 0))
+ return ~0;
+ if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) {
+ int i;
+ for (i = 0; i < 600; i++)
+ snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
+ }
+ return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
+}
+
+/*
+ * Misc routines
+ */
+
+static u32 snd_ymfpci_calc_delta(u32 rate)
+{
+ switch (rate) {
+ case 8000: return 0x02aaab00;
+ case 11025: return 0x03accd00;
+ case 16000: return 0x05555500;
+ case 22050: return 0x07599a00;
+ case 32000: return 0x0aaaab00;
+ case 44100: return 0x0eb33300;
+ default: return ((rate << 16) / 375) << 5;
+ }
+}
+
+static u32 def_rate[8] = {
+ 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
+};
+
+static u32 snd_ymfpci_calc_lpfK(u32 rate)
+{
+ u32 i;
+ static u32 val[8] = {
+ 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,
+ 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000
+ };
+
+ if (rate == 44100)
+ return 0x40000000; /* FIXME: What's the right value? */
+ for (i = 0; i < 8; i++)
+ if (rate <= def_rate[i])
+ return val[i];
+ return val[0];
+}
+
+static u32 snd_ymfpci_calc_lpfQ(u32 rate)
+{
+ u32 i;
+ static u32 val[8] = {
+ 0x35280000, 0x34A70000, 0x32020000, 0x31770000,
+ 0x31390000, 0x31C90000, 0x33D00000, 0x40000000
+ };
+
+ if (rate == 44100)
+ return 0x370A0000;
+ for (i = 0; i < 8; i++)
+ if (rate <= def_rate[i])
+ return val[i];
+ return val[0];
+}
+
+/*
+ * Hardware start management
+ */
+
+static void snd_ymfpci_hw_start(ymfpci_t *chip)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->start_count++ > 0)
+ goto __end;
+ snd_ymfpci_writel(chip, YDSXGR_MODE,
+ snd_ymfpci_readl(chip, YDSXGR_MODE) | 3);
+ chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
+ __end:
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_ymfpci_hw_stop(ymfpci_t *chip)
+{
+ unsigned long flags;
+ long timeout = 1000;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (--chip->start_count > 0)
+ goto __end;
+ snd_ymfpci_writel(chip, YDSXGR_MODE,
+ snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3);
+ while (timeout-- > 0) {
+ if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0)
+ break;
+ }
+ if (atomic_read(&chip->interrupt_sleep_count)) {
+ atomic_set(&chip->interrupt_sleep_count, 0);
+ wake_up(&chip->interrupt_sleep);
+ }
+ __end:
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ * Playback voice management
+ */
+
+static int voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+{
+ ymfpci_voice_t *voice, *voice2;
+ int idx;
+
+ *rvoice = NULL;
+ for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) {
+ voice = &chip->voices[idx];
+ voice2 = pair ? &chip->voices[idx+1] : NULL;
+ if (voice->use || (voice2 && voice2->use))
+ continue;
+ voice->use = 1;
+ if (voice2)
+ voice2->use = 1;
+ switch (type) {
+ case YMFPCI_PCM:
+ voice->pcm = 1;
+ if (voice2)
+ voice2->pcm = 1;
+ break;
+ case YMFPCI_SYNTH:
+ voice->synth = 1;
+ break;
+ case YMFPCI_MIDI:
+ voice->midi = 1;
+ break;
+ }
+ snd_ymfpci_hw_start(chip);
+ if (voice2)
+ snd_ymfpci_hw_start(chip);
+ *rvoice = voice;
+ return 0;
+ }
+ return -ENOMEM;
+}
+
+static int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+{
+ unsigned long flags;
+ int result;
+
+ snd_assert(rvoice != NULL, return -EINVAL);
+ snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL);
+
+ spin_lock_irqsave(&chip->voice_lock, flags);
+ for (;;) {
+ result = voice_alloc(chip, type, pair, rvoice);
+ if (result == 0 || type != YMFPCI_PCM)
+ break;
+ /* TODO: synth/midi voice deallocation */
+ break;
+ }
+ spin_unlock_irqrestore(&chip->voice_lock, flags);
+ return result;
+}
+
+static int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice)
+{
+ unsigned long flags;
+
+ snd_assert(pvoice != NULL, return -EINVAL);
+ snd_ymfpci_hw_stop(chip);
+ spin_lock_irqsave(&chip->voice_lock, flags);
+ pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
+ pvoice->ypcm = NULL;
+ pvoice->interrupt = NULL;
+ spin_unlock_irqrestore(&chip->voice_lock, flags);
+ return 0;
+}
+
+/*
+ * PCM part
+ */
+
+static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice)
+{
+ ymfpci_pcm_t *ypcm;
+ u32 pos, delta;
+
+ if ((ypcm = voice->ypcm) == NULL)
+ return;
+ if (ypcm->substream == NULL)
+ return;
+ spin_lock(&chip->reg_lock);
+ if (ypcm->running) {
+ pos = le32_to_cpu(voice->bank[chip->active_bank].start);
+ if (pos < ypcm->last_pos)
+ delta = pos + (ypcm->buffer_size - ypcm->last_pos);
+ else
+ delta = pos - ypcm->last_pos;
+ ypcm->period_pos += delta;
+ ypcm->last_pos = pos;
+ if (ypcm->period_pos >= ypcm->period_size) {
+ // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+ ypcm->period_pos %= ypcm->period_size;
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(ypcm->substream);
+ spin_lock(&chip->reg_lock);
+ }
+ }
+ spin_unlock(&chip->reg_lock);
+}
+
+static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+ ymfpci_t *chip = ypcm->chip;
+ u32 pos, delta;
+
+ spin_lock(&chip->reg_lock);
+ if (ypcm->running) {
+ pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
+ if (pos < ypcm->last_pos)
+ delta = pos + (ypcm->buffer_size - ypcm->last_pos);
+ else
+ delta = pos - ypcm->last_pos;
+ ypcm->period_pos += delta;
+ ypcm->last_pos = pos;
+ if (ypcm->period_pos >= ypcm->period_size) {
+ ypcm->period_pos %= ypcm->period_size;
+ // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(substream);
+ spin_lock(&chip->reg_lock);
+ }
+ }
+ spin_unlock(&chip->reg_lock);
+}
+
+static int snd_ymfpci_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ ymfpci_pcm_t *ypcm = substream->runtime->private_data;
+ int result = 0;
+
+ spin_lock(&chip->reg_lock);
+ if (ypcm->voices[0] == NULL) {
+ result = -EINVAL;
+ goto __unlock;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr);
+ if (ypcm->voices[1] != NULL)
+ chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr);
+ ypcm->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
+ if (ypcm->voices[1] != NULL)
+ chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
+ ypcm->running = 0;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ __unlock:
+ spin_unlock(&chip->reg_lock);
+ return result;
+}
+static int snd_ymfpci_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ ymfpci_pcm_t *ypcm = substream->runtime->private_data;
+ int result = 0;
+ u32 tmp;
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
+ snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
+ ypcm->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
+ snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
+ ypcm->running = 0;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ spin_unlock(&chip->reg_lock);
+ return result;
+}
+
+static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
+{
+ int err;
+
+ if (ypcm->voices[1] != NULL && voices < 2) {
+ snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]);
+ ypcm->voices[1] = NULL;
+ }
+ if (voices == 1 && ypcm->voices[0] != NULL)
+ return 0; /* already allocated */
+ if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)
+ return 0; /* already allocated */
+ if (voices > 1) {
+ if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
+ snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]);
+ ypcm->voices[0] = NULL;
+ }
+ }
+ err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);
+ if (err < 0)
+ return err;
+ ypcm->voices[0]->ypcm = ypcm;
+ ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt;
+ if (voices > 1) {
+ ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1];
+ ypcm->voices[1]->ypcm = ypcm;
+ }
+ return 0;
+}
+
+static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo,
+ int rate, int w_16, unsigned long addr,
+ unsigned int end,
+ int output_front, int output_rear)
+{
+ u32 format;
+ u32 delta = snd_ymfpci_calc_delta(rate);
+ u32 lpfQ = snd_ymfpci_calc_lpfQ(rate);
+ u32 lpfK = snd_ymfpci_calc_lpfK(rate);
+ snd_ymfpci_playback_bank_t *bank;
+ unsigned int nbank;
+
+ snd_assert(voice != NULL, return);
+ format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000);
+ for (nbank = 0; nbank < 2; nbank++) {
+ bank = &voice->bank[nbank];
+ bank->format = cpu_to_le32(format);
+ bank->loop_default = 0;
+ bank->base = cpu_to_le32(addr);
+ bank->loop_start = 0;
+ bank->loop_end = cpu_to_le32(end);
+ bank->loop_frac = 0;
+ bank->eg_gain_end = cpu_to_le32(0x40000000);
+ bank->lpfQ = cpu_to_le32(lpfQ);
+ bank->status = 0;
+ bank->num_of_frames = 0;
+ bank->loop_count = 0;
+ bank->start = 0;
+ bank->start_frac = 0;
+ bank->delta =
+ bank->delta_end = cpu_to_le32(delta);
+ bank->lpfK =
+ bank->lpfK_end = cpu_to_le32(lpfK);
+ bank->eg_gain = cpu_to_le32(0x40000000);
+ bank->lpfD1 =
+ bank->lpfD2 = 0;
+
+ bank->left_gain =
+ bank->right_gain =
+ bank->left_gain_end =
+ bank->right_gain_end =
+ bank->eff1_gain =
+ bank->eff2_gain =
+ bank->eff3_gain =
+ bank->eff1_gain_end =
+ bank->eff2_gain_end =
+ bank->eff3_gain_end = 0;
+
+ if (!stereo) {
+ if (output_front) {
+ bank->left_gain =
+ bank->right_gain =
+ bank->left_gain_end =
+ bank->right_gain_end = cpu_to_le32(0x40000000);
+ }
+ if (output_rear) {
+ bank->eff2_gain =
+ bank->eff2_gain_end =
+ bank->eff3_gain =
+ bank->eff3_gain_end = cpu_to_le32(0x40000000);
+ }
+ } else {
+ if (output_front) {
+ if ((voice->number & 1) == 0) {
+ bank->left_gain =
+ bank->left_gain_end = cpu_to_le32(0x40000000);
+ } else {
+ bank->format |= cpu_to_le32(1);
+ bank->right_gain =
+ bank->right_gain_end = cpu_to_le32(0x40000000);
+ }
+ }
+ if (output_rear) {
+ if ((voice->number & 1) == 0) {
+ bank->eff3_gain =
+ bank->eff3_gain_end = cpu_to_le32(0x40000000);
+ } else {
+ bank->format |= cpu_to_le32(1);
+ bank->eff2_gain =
+ bank->eff2_gain_end = cpu_to_le32(0x40000000);
+ }
+ }
+ }
+ }
+}
+
+static int __devinit snd_ymfpci_ac3_init(ymfpci_t *chip)
+{
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ 4096, &chip->ac3_tmp_base) < 0)
+ return -ENOMEM;
+
+ chip->bank_effect[3][0]->base =
+ chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr);
+ chip->bank_effect[3][0]->loop_end =
+ chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024);
+ chip->bank_effect[4][0]->base =
+ chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr + 2048);
+ chip->bank_effect[4][0]->loop_end =
+ chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024);
+
+ spin_lock_irq(&chip->reg_lock);
+ snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
+ snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_ymfpci_ac3_done(ymfpci_t *chip)
+{
+ spin_lock_irq(&chip->reg_lock);
+ snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
+ snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3));
+ spin_unlock_irq(&chip->reg_lock);
+ // snd_ymfpci_irq_wait(chip);
+ if (chip->ac3_tmp_base.area) {
+ snd_dma_free_pages(&chip->ac3_tmp_base);
+ chip->ac3_tmp_base.area = NULL;
+ }
+ return 0;
+}
+
+static int snd_ymfpci_playback_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+ int err;
+
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0)
+ return err;
+ return 0;
+}
+
+static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm;
+
+ if (runtime->private_data == NULL)
+ return 0;
+ ypcm = runtime->private_data;
+
+ /* wait, until the PCI operations are not finished */
+ snd_ymfpci_irq_wait(chip);
+ snd_pcm_lib_free_pages(substream);
+ if (ypcm->voices[1]) {
+ snd_ymfpci_voice_free(chip, ypcm->voices[1]);
+ ypcm->voices[1] = NULL;
+ }
+ if (ypcm->voices[0]) {
+ snd_ymfpci_voice_free(chip, ypcm->voices[0]);
+ ypcm->voices[0] = NULL;
+ }
+ return 0;
+}
+
+static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream)
+{
+ // ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+ unsigned int nvoice;
+
+ ypcm->period_size = runtime->period_size;
+ ypcm->buffer_size = runtime->buffer_size;
+ ypcm->period_pos = 0;
+ ypcm->last_pos = 0;
+ for (nvoice = 0; nvoice < runtime->channels; nvoice++)
+ snd_ymfpci_pcm_init_voice(ypcm->voices[nvoice],
+ runtime->channels == 2,
+ runtime->rate,
+ snd_pcm_format_width(runtime->format) == 16,
+ runtime->dma_addr,
+ ypcm->buffer_size,
+ ypcm->output_front,
+ ypcm->output_rear);
+ return 0;
+}
+
+static int snd_ymfpci_capture_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ymfpci_capture_hw_free(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+
+ /* wait, until the PCI operations are not finished */
+ snd_ymfpci_irq_wait(chip);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ymfpci_capture_prepare(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+ snd_ymfpci_capture_bank_t * bank;
+ int nbank;
+ u32 rate, format;
+
+ ypcm->period_size = runtime->period_size;
+ ypcm->buffer_size = runtime->buffer_size;
+ ypcm->period_pos = 0;
+ ypcm->last_pos = 0;
+ ypcm->shift = 0;
+ rate = ((48000 * 4096) / runtime->rate) - 1;
+ format = 0;
+ if (runtime->channels == 2) {
+ format |= 2;
+ ypcm->shift++;
+ }
+ if (snd_pcm_format_width(runtime->format) == 8)
+ format |= 1;
+ else
+ ypcm->shift++;
+ switch (ypcm->capture_bank_number) {
+ case 0:
+ snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format);
+ snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate);
+ break;
+ case 1:
+ snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format);
+ snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate);
+ break;
+ }
+ for (nbank = 0; nbank < 2; nbank++) {
+ bank = chip->bank_capture[ypcm->capture_bank_number][nbank];
+ bank->base = cpu_to_le32(runtime->dma_addr);
+ bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift);
+ bank->start = 0;
+ bank->num_of_loops = 0;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_ymfpci_playback_pointer(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+ ymfpci_voice_t *voice = ypcm->voices[0];
+
+ if (!(ypcm->running && voice))
+ return 0;
+ return le32_to_cpu(voice->bank[chip->active_bank].start);
+}
+
+static snd_pcm_uframes_t snd_ymfpci_capture_pointer(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+
+ if (!ypcm->running)
+ return 0;
+ return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
+}
+
+static void snd_ymfpci_irq_wait(ymfpci_t *chip)
+{
+ wait_queue_t wait;
+ int loops = 4;
+
+ while (loops-- > 0) {
+ if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0)
+ continue;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&chip->interrupt_sleep, &wait);
+ atomic_inc(&chip->interrupt_sleep_count);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/20);
+ remove_wait_queue(&chip->interrupt_sleep, &wait);
+ }
+}
+
+static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ymfpci_t *chip = dev_id;
+ u32 status, nvoice, mode;
+ ymfpci_voice_t *voice;
+
+ status = snd_ymfpci_readl(chip, YDSXGR_STATUS);
+ if (status & 0x80000000) {
+ chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
+ spin_lock(&chip->voice_lock);
+ for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) {
+ voice = &chip->voices[nvoice];
+ if (voice->interrupt)
+ voice->interrupt(chip, voice);
+ }
+ for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) {
+ if (chip->capture_substream[nvoice])
+ snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]);
+ }
+#if 0
+ for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) {
+ if (chip->effect_substream[nvoice])
+ snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]);
+ }
+#endif
+ spin_unlock(&chip->voice_lock);
+ spin_lock(&chip->reg_lock);
+ snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000);
+ mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2;
+ snd_ymfpci_writel(chip, YDSXGR_MODE, mode);
+ spin_unlock(&chip->reg_lock);
+
+ if (atomic_read(&chip->interrupt_sleep_count)) {
+ atomic_set(&chip->interrupt_sleep_count, 0);
+ wake_up(&chip->interrupt_sleep);
+ }
+ }
+
+ status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG);
+ if (status & 1) {
+ if (chip->timer)
+ snd_timer_interrupt(chip->timer, chip->timer->sticks);
+ }
+ snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status);
+
+ if (chip->rawmidi)
+ snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data, regs);
+ return IRQ_HANDLED;
+}
+
+static snd_pcm_hardware_t snd_ymfpci_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 256 * 1024, /* FIXME: enough? */
+ .period_bytes_min = 64,
+ .period_bytes_max = 256 * 1024, /* FIXME: enough? */
+ .periods_min = 3,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_ymfpci_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 256 * 1024, /* FIXME: enough? */
+ .period_bytes_min = 64,
+ .period_bytes_max = 256 * 1024, /* FIXME: enough? */
+ .periods_min = 3,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+
+ kfree(ypcm);
+}
+
+static int snd_ymfpci_playback_open_1(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm;
+
+ ypcm = kcalloc(1, sizeof(*ypcm), GFP_KERNEL);
+ if (ypcm == NULL)
+ return -ENOMEM;
+ ypcm->chip = chip;
+ ypcm->type = PLAYBACK_VOICE;
+ ypcm->substream = substream;
+ runtime->hw = snd_ymfpci_playback;
+ runtime->private_data = ypcm;
+ runtime->private_free = snd_ymfpci_pcm_free_substream;
+ /* FIXME? True value is 256/48 = 5.33333 ms */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+ return 0;
+}
+
+/* call with spinlock held */
+static void ymfpci_open_extension(ymfpci_t *chip)
+{
+ if (! chip->rear_opened) {
+ if (! chip->spdif_opened) /* set AC3 */
+ snd_ymfpci_writel(chip, YDSXGR_MODE,
+ snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30));
+ /* enable second codec (4CHEN) */
+ snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
+ (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010);
+ }
+}
+
+/* call with spinlock held */
+static void ymfpci_close_extension(ymfpci_t *chip)
+{
+ if (! chip->rear_opened) {
+ if (! chip->spdif_opened)
+ snd_ymfpci_writel(chip, YDSXGR_MODE,
+ snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30));
+ snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
+ (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) & ~0x0010);
+ }
+}
+
+static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm;
+ int err;
+
+ if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+ return err;
+ ypcm = runtime->private_data;
+ ypcm->output_front = 1;
+ ypcm->output_rear = chip->mode_dup4ch ? 1 : 0;
+ spin_lock_irq(&chip->reg_lock);
+ if (ypcm->output_rear) {
+ ymfpci_open_extension(chip);
+ chip->rear_opened++;
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_ymfpci_playback_spdif_open(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm;
+ int err;
+
+ if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+ return err;
+ ypcm = runtime->private_data;
+ ypcm->output_front = 0;
+ ypcm->output_rear = 1;
+ spin_lock_irq(&chip->reg_lock);
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+ snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2);
+ ymfpci_open_extension(chip);
+ chip->spdif_pcm_bits = chip->spdif_bits;
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
+ chip->spdif_opened++;
+ spin_unlock_irq(&chip->reg_lock);
+
+ chip->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+ return 0;
+}
+
+static int snd_ymfpci_playback_4ch_open(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm;
+ int err;
+
+ if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+ return err;
+ ypcm = runtime->private_data;
+ ypcm->output_front = 0;
+ ypcm->output_rear = 1;
+ spin_lock_irq(&chip->reg_lock);
+ ymfpci_open_extension(chip);
+ chip->rear_opened++;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_ymfpci_capture_open(snd_pcm_substream_t * substream,
+ u32 capture_bank_number)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm;
+
+ ypcm = kcalloc(1, sizeof(*ypcm), GFP_KERNEL);
+ if (ypcm == NULL)
+ return -ENOMEM;
+ ypcm->chip = chip;
+ ypcm->type = capture_bank_number + CAPTURE_REC;
+ ypcm->substream = substream;
+ ypcm->capture_bank_number = capture_bank_number;
+ chip->capture_substream[capture_bank_number] = substream;
+ runtime->hw = snd_ymfpci_capture;
+ /* FIXME? True value is 256/48 = 5.33333 ms */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+ runtime->private_data = ypcm;
+ runtime->private_free = snd_ymfpci_pcm_free_substream;
+ snd_ymfpci_hw_start(chip);
+ return 0;
+}
+
+static int snd_ymfpci_capture_rec_open(snd_pcm_substream_t * substream)
+{
+ return snd_ymfpci_capture_open(substream, 0);
+}
+
+static int snd_ymfpci_capture_ac97_open(snd_pcm_substream_t * substream)
+{
+ return snd_ymfpci_capture_open(substream, 1);
+}
+
+static int snd_ymfpci_playback_close_1(snd_pcm_substream_t * substream)
+{
+ return 0;
+}
+
+static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ ymfpci_pcm_t *ypcm = substream->runtime->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ if (ypcm->output_rear && chip->rear_opened > 0) {
+ chip->rear_opened--;
+ ymfpci_close_extension(chip);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return snd_ymfpci_playback_close_1(substream);
+}
+
+static int snd_ymfpci_playback_spdif_close(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->spdif_opened = 0;
+ ymfpci_close_extension(chip);
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+ snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2);
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+ spin_unlock_irq(&chip->reg_lock);
+ chip->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+ return snd_ymfpci_playback_close_1(substream);
+}
+
+static int snd_ymfpci_playback_4ch_close(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ if (chip->rear_opened > 0) {
+ chip->rear_opened--;
+ ymfpci_close_extension(chip);
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return snd_ymfpci_playback_close_1(substream);
+}
+
+static int snd_ymfpci_capture_close(snd_pcm_substream_t * substream)
+{
+ ymfpci_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ymfpci_pcm_t *ypcm = runtime->private_data;
+
+ if (ypcm != NULL) {
+ chip->capture_substream[ypcm->capture_bank_number] = NULL;
+ snd_ymfpci_hw_stop(chip);
+ }
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_ops = {
+ .open = snd_ymfpci_playback_open,
+ .close = snd_ymfpci_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ymfpci_playback_hw_params,
+ .hw_free = snd_ymfpci_playback_hw_free,
+ .prepare = snd_ymfpci_playback_prepare,
+ .trigger = snd_ymfpci_playback_trigger,
+ .pointer = snd_ymfpci_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ymfpci_capture_rec_ops = {
+ .open = snd_ymfpci_capture_rec_open,
+ .close = snd_ymfpci_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ymfpci_capture_hw_params,
+ .hw_free = snd_ymfpci_capture_hw_free,
+ .prepare = snd_ymfpci_capture_prepare,
+ .trigger = snd_ymfpci_capture_trigger,
+ .pointer = snd_ymfpci_capture_pointer,
+};
+
+static void snd_ymfpci_pcm_free(snd_pcm_t *pcm)
+{
+ ymfpci_t *chip = pcm->private_data;
+ chip->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ pcm->private_free = snd_ymfpci_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "YMFPCI");
+ chip->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_capture_ac97_ops = {
+ .open = snd_ymfpci_capture_ac97_open,
+ .close = snd_ymfpci_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ymfpci_capture_hw_params,
+ .hw_free = snd_ymfpci_capture_hw_free,
+ .prepare = snd_ymfpci_capture_prepare,
+ .trigger = snd_ymfpci_capture_trigger,
+ .pointer = snd_ymfpci_capture_pointer,
+};
+
+static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm)
+{
+ ymfpci_t *chip = pcm->private_data;
+ chip->pcm2 = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(chip->card, "YMFPCI - PCM2", device, 0, 1, &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ pcm->private_free = snd_ymfpci_pcm2_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ sprintf(pcm->name, "YMFPCI - %s",
+ chip->device_id == PCI_DEVICE_ID_YAMAHA_754 ? "Direct Recording" : "AC'97");
+ chip->pcm2 = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_spdif_ops = {
+ .open = snd_ymfpci_playback_spdif_open,
+ .close = snd_ymfpci_playback_spdif_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ymfpci_playback_hw_params,
+ .hw_free = snd_ymfpci_playback_hw_free,
+ .prepare = snd_ymfpci_playback_prepare,
+ .trigger = snd_ymfpci_playback_trigger,
+ .pointer = snd_ymfpci_playback_pointer,
+};
+
+static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm)
+{
+ ymfpci_t *chip = pcm->private_data;
+ chip->pcm_spdif = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ pcm->private_free = snd_ymfpci_pcm_spdif_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "YMFPCI - IEC958");
+ chip->pcm_spdif = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_4ch_ops = {
+ .open = snd_ymfpci_playback_4ch_open,
+ .close = snd_ymfpci_playback_4ch_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ymfpci_playback_hw_params,
+ .hw_free = snd_ymfpci_playback_hw_free,
+ .prepare = snd_ymfpci_playback_prepare,
+ .trigger = snd_ymfpci_playback_trigger,
+ .pointer = snd_ymfpci_playback_pointer,
+};
+
+static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm)
+{
+ ymfpci_t *chip = pcm->private_data;
+ chip->pcm_4ch = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ pcm->private_free = snd_ymfpci_pcm_4ch_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops);
+
+ /* global setup */
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "YMFPCI - Rear PCM");
+ chip->pcm_4ch = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+static int snd_ymfpci_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ymfpci_spdif_default_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&chip->reg_lock);
+ ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_ymfpci_spdif_default_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
+ (ucontrol->value.iec958.status[1] << 8);
+ spin_lock_irq(&chip->reg_lock);
+ change = chip->spdif_bits != val;
+ chip->spdif_bits = val;
+ if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL)
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+ spin_unlock_irq(&chip->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_spdif_default __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_ymfpci_spdif_default_info,
+ .get = snd_ymfpci_spdif_default_get,
+ .put = snd_ymfpci_spdif_default_put
+};
+
+static int snd_ymfpci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ymfpci_spdif_mask_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&chip->reg_lock);
+ ucontrol->value.iec958.status[0] = 0x3e;
+ ucontrol->value.iec958.status[1] = 0xff;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_spdif_mask __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_ymfpci_spdif_mask_info,
+ .get = snd_ymfpci_spdif_mask_get,
+};
+
+static int snd_ymfpci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ymfpci_spdif_stream_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&chip->reg_lock);
+ ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff;
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int snd_ymfpci_spdif_stream_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change;
+
+ val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
+ (ucontrol->value.iec958.status[1] << 8);
+ spin_lock_irq(&chip->reg_lock);
+ change = chip->spdif_pcm_bits != val;
+ chip->spdif_pcm_bits = val;
+ if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2))
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
+ spin_unlock_irq(&chip->reg_lock);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_spdif_stream __devinitdata =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .info = snd_ymfpci_spdif_stream_info,
+ .get = snd_ymfpci_spdif_stream_get,
+ .put = snd_ymfpci_spdif_stream_put
+};
+
+static int snd_ymfpci_drec_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+ static char *texts[3] = {"AC'97", "IEC958", "ZV Port"};
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 3;
+ if (info->value.enumerated.item > 2)
+ info->value.enumerated.item = 2;
+ strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ymfpci_drec_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ u16 reg;
+
+ spin_lock_irq(&chip->reg_lock);
+ reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+ spin_unlock_irq(&chip->reg_lock);
+ if (!(reg & 0x100))
+ value->value.enumerated.item[0] = 0;
+ else
+ value->value.enumerated.item[0] = 1 + ((reg & 0x200) != 0);
+ return 0;
+}
+
+static int snd_ymfpci_drec_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ u16 reg, old_reg;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+ if (value->value.enumerated.item[0] == 0)
+ reg = old_reg & ~0x100;
+ else
+ reg = (old_reg & ~0x300) | 0x100 | ((value->value.enumerated.item[0] == 2) << 9);
+ snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, reg);
+ spin_unlock_irq(&chip->reg_lock);
+ return reg != old_reg;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_drec_source __devinitdata = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Direct Recording Source",
+ .info = snd_ymfpci_drec_source_info,
+ .get = snd_ymfpci_drec_source_get,
+ .put = snd_ymfpci_drec_source_put
+};
+
+/*
+ * Mixer controls
+ */
+
+#define YMFPCI_SINGLE(xname, xindex, reg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_ymfpci_info_single, \
+ .get = snd_ymfpci_get_single, .put = snd_ymfpci_put_single, \
+ .private_value = reg }
+
+static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ unsigned int mask = 1;
+
+ switch (kcontrol->private_value) {
+ case YDSXGR_SPDIFOUTCTRL: break;
+ case YDSXGR_SPDIFINCTRL: break;
+ default: return -EINVAL;
+ }
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value;
+ unsigned int shift = 0, mask = 1, invert = 0;
+
+ switch (kcontrol->private_value) {
+ case YDSXGR_SPDIFOUTCTRL: break;
+ case YDSXGR_SPDIFINCTRL: break;
+ default: return -EINVAL;
+ }
+ ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value;
+ unsigned int shift = 0, mask = 1, invert = 0;
+ int change;
+ unsigned int val, oval;
+
+ switch (kcontrol->private_value) {
+ case YDSXGR_SPDIFOUTCTRL: break;
+ case YDSXGR_SPDIFINCTRL: break;
+ default: return -EINVAL;
+ }
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = mask - val;
+ val <<= shift;
+ spin_lock_irq(&chip->reg_lock);
+ oval = snd_ymfpci_readl(chip, reg);
+ val = (oval & ~(mask << shift)) | val;
+ change = val != oval;
+ snd_ymfpci_writel(chip, reg, val);
+ spin_unlock_irq(&chip->reg_lock);
+ return change;
+}
+
+#define YMFPCI_DOUBLE(xname, xindex, reg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_ymfpci_info_double, \
+ .get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
+ .private_value = reg }
+
+static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ unsigned int reg = kcontrol->private_value;
+ unsigned int mask = 16383;
+
+ if (reg < 0x80 || reg >= 0xc0)
+ return -EINVAL;
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = kcontrol->private_value;
+ unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+ unsigned int val;
+
+ if (reg < 0x80 || reg >= 0xc0)
+ return -EINVAL;
+ spin_lock_irq(&chip->reg_lock);
+ val = snd_ymfpci_readl(chip, reg);
+ spin_unlock_irq(&chip->reg_lock);
+ ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
+ ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
+ if (invert) {
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ }
+ return 0;
+}
+
+static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = kcontrol->private_value;
+ unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+ int change;
+ unsigned int val1, val2, oval;
+
+ if (reg < 0x80 || reg >= 0xc0)
+ return -EINVAL;
+ val1 = ucontrol->value.integer.value[0] & mask;
+ val2 = ucontrol->value.integer.value[1] & mask;
+ if (invert) {
+ val1 = mask - val1;
+ val2 = mask - val2;
+ }
+ val1 <<= shift_left;
+ val2 <<= shift_right;
+ spin_lock_irq(&chip->reg_lock);
+ oval = snd_ymfpci_readl(chip, reg);
+ val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+ change = val1 != oval;
+ snd_ymfpci_writel(chip, reg, val1);
+ spin_unlock_irq(&chip->reg_lock);
+ return change;
+}
+
+/*
+ * 4ch duplication
+ */
+static int snd_ymfpci_info_dup4ch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ymfpci_get_dup4ch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = chip->mode_dup4ch;
+ return 0;
+}
+
+static int snd_ymfpci_put_dup4ch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ int change;
+ change = (ucontrol->value.integer.value[0] != chip->mode_dup4ch);
+ if (change)
+ chip->mode_dup4ch = !!ucontrol->value.integer.value[0];
+ return change;
+}
+
+
+static snd_kcontrol_new_t snd_ymfpci_controls[] __devinitdata = {
+YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL),
+YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL),
+YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL),
+YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL),
+YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL),
+YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL),
+YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL),
+YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL),
+YMFPCI_DOUBLE("FM Legacy Volume", 0, YDSXGR_LEGACYOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL),
+YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL),
+YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "4ch Duplication",
+ .info = snd_ymfpci_info_dup4ch,
+ .get = snd_ymfpci_get_dup4ch,
+ .put = snd_ymfpci_put_dup4ch,
+},
+};
+
+
+/*
+ * GPIO
+ */
+
+static int snd_ymfpci_get_gpio_out(ymfpci_t *chip, int pin)
+{
+ u16 reg, mode;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
+ reg &= ~(1 << (pin + 8));
+ reg |= (1 << pin);
+ snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg);
+ /* set the level mode for input line */
+ mode = snd_ymfpci_readw(chip, YDSXGR_GPIOTYPECONFIG);
+ mode &= ~(3 << (pin * 2));
+ snd_ymfpci_writew(chip, YDSXGR_GPIOTYPECONFIG, mode);
+ snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
+ mode = snd_ymfpci_readw(chip, YDSXGR_GPIOINSTATUS);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return (mode >> pin) & 1;
+}
+
+static int snd_ymfpci_set_gpio_out(ymfpci_t *chip, int pin, int enable)
+{
+ u16 reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
+ reg &= ~(1 << pin);
+ reg &= ~(1 << (pin + 8));
+ snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg);
+ snd_ymfpci_writew(chip, YDSXGR_GPIOOUTCTRL, enable << pin);
+ snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ return 0;
+}
+
+static int snd_ymfpci_gpio_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ymfpci_gpio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ int pin = (int)kcontrol->private_value;
+ ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
+ return 0;
+}
+
+static int snd_ymfpci_gpio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+ int pin = (int)kcontrol->private_value;
+
+ if (snd_ymfpci_get_gpio_out(chip, pin) != ucontrol->value.integer.value[0]) {
+ snd_ymfpci_set_gpio_out(chip, pin, !!ucontrol->value.integer.value[0]);
+ ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
+ return 1;
+ }
+ return 0;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_rear_shared __devinitdata = {
+ .name = "Shared Rear/Line-In Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ymfpci_gpio_sw_info,
+ .get = snd_ymfpci_gpio_sw_get,
+ .put = snd_ymfpci_gpio_sw_put,
+ .private_value = 2,
+};
+
+
+/*
+ * Mixer routines
+ */
+
+static void snd_ymfpci_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+ ymfpci_t *chip = bus->private_data;
+ chip->ac97_bus = NULL;
+}
+
+static void snd_ymfpci_mixer_free_ac97(ac97_t *ac97)
+{
+ ymfpci_t *chip = ac97->private_data;
+ chip->ac97 = NULL;
+}
+
+int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch)
+{
+ ac97_template_t ac97;
+ snd_kcontrol_t *kctl;
+ unsigned int idx;
+ int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_ymfpci_codec_write,
+ .read = snd_ymfpci_codec_read,
+ };
+
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ return err;
+ chip->ac97_bus->private_free = snd_ymfpci_mixer_free_ac97_bus;
+ chip->ac97_bus->no_vra = 1; /* YMFPCI doesn't need VRA */
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ ac97.private_free = snd_ymfpci_mixer_free_ac97;
+ if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ return err;
+
+ /* to be sure */
+ snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_VRA|AC97_EA_VRM, 0);
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_ymfpci_controls); idx++) {
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0)
+ return err;
+ }
+
+ /* add S/PDIF control */
+ snd_assert(chip->pcm_spdif != NULL, return -EIO);
+ if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0)
+ return err;
+ kctl->id.device = chip->pcm_spdif->device;
+ if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0)
+ return err;
+ kctl->id.device = chip->pcm_spdif->device;
+ if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0)
+ return err;
+ kctl->id.device = chip->pcm_spdif->device;
+ chip->spdif_pcm_ctl = kctl;
+
+ /* direct recording source */
+ if (chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
+ (err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_drec_source, chip))) < 0)
+ return err;
+
+ /*
+ * shared rear/line-in
+ */
+ if (rear_switch) {
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip))) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+
+/*
+ * timer
+ */
+
+static int snd_ymfpci_timer_start(snd_timer_t *timer)
+{
+ ymfpci_t *chip;
+ unsigned long flags;
+ unsigned int count;
+
+ chip = snd_timer_chip(timer);
+ count = timer->sticks - 1;
+ if (count == 0) /* minimum time is 20.8 us */
+ count = 1;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count);
+ snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static int snd_ymfpci_timer_stop(snd_timer_t *timer)
+{
+ ymfpci_t *chip;
+ unsigned long flags;
+
+ chip = snd_timer_chip(timer);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x00);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static int snd_ymfpci_timer_precise_resolution(snd_timer_t *timer,
+ unsigned long *num, unsigned long *den)
+{
+ *num = 1;
+ *den = 96000;
+ return 0;
+}
+
+static struct _snd_timer_hardware snd_ymfpci_timer_hw = {
+ .flags = SNDRV_TIMER_HW_AUTO,
+ .resolution = 10417, /* 1/2fs = 10.41666...us */
+ .ticks = 65536,
+ .start = snd_ymfpci_timer_start,
+ .stop = snd_ymfpci_timer_stop,
+ .precise_resolution = snd_ymfpci_timer_precise_resolution,
+};
+
+int __devinit snd_ymfpci_timer(ymfpci_t *chip, int device)
+{
+ snd_timer_t *timer = NULL;
+ snd_timer_id_t tid;
+ int err;
+
+ tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+ tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+ tid.card = chip->card->number;
+ tid.device = device;
+ tid.subdevice = 0;
+ if ((err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer)) >= 0) {
+ strcpy(timer->name, "YMFPCI timer");
+ timer->private_data = chip;
+ timer->hw = snd_ymfpci_timer_hw;
+ }
+ chip->timer = timer;
+ return err;
+}
+
+
+/*
+ * proc interface
+ */
+
+static void snd_ymfpci_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ ymfpci_t *chip = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "YMFPCI\n\n");
+ for (i = 0; i <= YDSXGR_WORKBASE; i += 4)
+ snd_iprintf(buffer, "%04x: %04x\n", i, snd_ymfpci_readl(chip, i));
+}
+
+static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip)
+{
+ snd_info_entry_t *entry;
+
+ if (! snd_card_proc_new(card, "ymfpci", &entry))
+ snd_info_set_text_ops(entry, chip, 1024, snd_ymfpci_proc_read);
+ return 0;
+}
+
+/*
+ * initialization routines
+ */
+
+static void snd_ymfpci_aclink_reset(struct pci_dev * pci)
+{
+ u8 cmd;
+
+ pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd);
+#if 0 // force to reset
+ if (cmd & 0x03) {
+#endif
+ pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
+ pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03);
+ pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
+ pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0);
+ pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0);
+#if 0
+ }
+#endif
+}
+
+static void snd_ymfpci_enable_dsp(ymfpci_t *chip)
+{
+ snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001);
+}
+
+static void snd_ymfpci_disable_dsp(ymfpci_t *chip)
+{
+ u32 val;
+ int timeout = 1000;
+
+ val = snd_ymfpci_readl(chip, YDSXGR_CONFIG);
+ if (val)
+ snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000);
+ while (timeout-- > 0) {
+ val = snd_ymfpci_readl(chip, YDSXGR_STATUS);
+ if ((val & 0x00000002) == 0)
+ break;
+ }
+}
+
+#include "ymfpci_image.h"
+
+static void snd_ymfpci_download_image(ymfpci_t *chip)
+{
+ int i;
+ u16 ctrl;
+ unsigned long *inst;
+
+ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
+ snd_ymfpci_disable_dsp(chip);
+ snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000);
+ snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000);
+ snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000);
+ snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000);
+ snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000);
+ snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000);
+ snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000);
+ ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+ snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+
+ /* setup DSP instruction code */
+ for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
+ snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]);
+
+ /* setup control instruction code */
+ switch (chip->device_id) {
+ case PCI_DEVICE_ID_YAMAHA_724F:
+ case PCI_DEVICE_ID_YAMAHA_740C:
+ case PCI_DEVICE_ID_YAMAHA_744:
+ case PCI_DEVICE_ID_YAMAHA_754:
+ inst = CntrlInst1E;
+ break;
+ default:
+ inst = CntrlInst;
+ break;
+ }
+ for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
+ snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
+
+ snd_ymfpci_enable_dsp(chip);
+}
+
+static int __devinit snd_ymfpci_memalloc(ymfpci_t *chip)
+{
+ long size, playback_ctrl_size;
+ int voice, bank, reg;
+ u8 *ptr;
+ dma_addr_t ptr_addr;
+
+ playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES;
+ chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2;
+ chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2;
+ chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2;
+ chip->work_size = YDSXG_DEFAULT_WORK_SIZE;
+
+ size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) +
+ ((chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0x00ff) & ~0x00ff) +
+ ((chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0x00ff) & ~0x00ff) +
+ ((chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0x00ff) & ~0x00ff) +
+ chip->work_size;
+ /* work_ptr must be aligned to 256 bytes, but it's already
+ covered with the kernel page allocation mechanism */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ size, &chip->work_ptr) < 0)
+ return -ENOMEM;
+ ptr = chip->work_ptr.area;
+ ptr_addr = chip->work_ptr.addr;
+ memset(ptr, 0, size); /* for sure */
+
+ chip->bank_base_playback = ptr;
+ chip->bank_base_playback_addr = ptr_addr;
+ chip->ctrl_playback = (u32 *)ptr;
+ chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES);
+ ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+ ptr_addr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+ for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
+ chip->voices[voice].number = voice;
+ chip->voices[voice].bank = (snd_ymfpci_playback_bank_t *)ptr;
+ chip->voices[voice].bank_addr = ptr_addr;
+ for (bank = 0; bank < 2; bank++) {
+ chip->bank_playback[voice][bank] = (snd_ymfpci_playback_bank_t *)ptr;
+ ptr += chip->bank_size_playback;
+ ptr_addr += chip->bank_size_playback;
+ }
+ }
+ ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+ ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+ chip->bank_base_capture = ptr;
+ chip->bank_base_capture_addr = ptr_addr;
+ for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++)
+ for (bank = 0; bank < 2; bank++) {
+ chip->bank_capture[voice][bank] = (snd_ymfpci_capture_bank_t *)ptr;
+ ptr += chip->bank_size_capture;
+ ptr_addr += chip->bank_size_capture;
+ }
+ ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+ ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+ chip->bank_base_effect = ptr;
+ chip->bank_base_effect_addr = ptr_addr;
+ for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++)
+ for (bank = 0; bank < 2; bank++) {
+ chip->bank_effect[voice][bank] = (snd_ymfpci_effect_bank_t *)ptr;
+ ptr += chip->bank_size_effect;
+ ptr_addr += chip->bank_size_effect;
+ }
+ ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+ ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+ chip->work_base = ptr;
+ chip->work_base_addr = ptr_addr;
+
+ snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, );
+
+ snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr);
+ snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr);
+ snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr);
+ snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr);
+ snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2);
+
+ /* S/PDIF output initialization */
+ chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff;
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0);
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+
+ /* S/PDIF input initialization */
+ snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0);
+
+ /* digital mixer setup */
+ for (reg = 0x80; reg < 0xc0; reg += 4)
+ snd_ymfpci_writel(chip, reg, 0);
+ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+ snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff);
+ snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff);
+ snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
+ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
+ snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff);
+ snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0x3fff3fff);
+
+ return 0;
+}
+
+static int snd_ymfpci_free(ymfpci_t *chip)
+{
+ u16 ctrl;
+
+ snd_assert(chip != NULL, return -EINVAL);
+
+ if (chip->res_reg_area) { /* don't touch busy hardware */
+ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+ snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
+ snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0);
+ snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0);
+ snd_ymfpci_disable_dsp(chip);
+ snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0);
+ ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+ snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+ }
+
+ snd_ymfpci_ac3_done(chip);
+
+ /* Set PCI device to D3 state */
+#if 0
+ /* FIXME: temporarily disabled, otherwise we cannot fire up
+ * the chip again unless reboot. ACPI bug?
+ */
+ pci_set_power_state(chip->pci, 3);
+#endif
+
+#ifdef CONFIG_PM
+ vfree(chip->saved_regs);
+#endif
+ if (chip->mpu_res) {
+ release_resource(chip->mpu_res);
+ kfree_nocheck(chip->mpu_res);
+ }
+ if (chip->fm_res) {
+ release_resource(chip->fm_res);
+ kfree_nocheck(chip->fm_res);
+ }
+ snd_ymfpci_free_gameport(chip);
+ if (chip->reg_area_virt)
+ iounmap(chip->reg_area_virt);
+ if (chip->work_ptr.area)
+ snd_dma_free_pages(&chip->work_ptr);
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->res_reg_area) {
+ release_resource(chip->res_reg_area);
+ kfree_nocheck(chip->res_reg_area);
+ }
+
+ pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
+
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_ymfpci_dev_free(snd_device_t *device)
+{
+ ymfpci_t *chip = device->device_data;
+ return snd_ymfpci_free(chip);
+}
+
+#ifdef CONFIG_PM
+static int saved_regs_index[] = {
+ /* spdif */
+ YDSXGR_SPDIFOUTCTRL,
+ YDSXGR_SPDIFOUTSTATUS,
+ YDSXGR_SPDIFINCTRL,
+ /* volumes */
+ YDSXGR_PRIADCLOOPVOL,
+ YDSXGR_NATIVEDACINVOL,
+ YDSXGR_NATIVEDACOUTVOL,
+ // YDSXGR_BUF441OUTVOL,
+ YDSXGR_NATIVEADCINVOL,
+ YDSXGR_SPDIFLOOPVOL,
+ YDSXGR_SPDIFOUTVOL,
+ YDSXGR_ZVOUTVOL,
+ YDSXGR_LEGACYOUTVOL,
+ /* address bases */
+ YDSXGR_PLAYCTRLBASE,
+ YDSXGR_RECCTRLBASE,
+ YDSXGR_EFFCTRLBASE,
+ YDSXGR_WORKBASE,
+ /* capture set up */
+ YDSXGR_MAPOFREC,
+ YDSXGR_RECFORMAT,
+ YDSXGR_RECSLOTSR,
+ YDSXGR_ADCFORMAT,
+ YDSXGR_ADCSLOTSR,
+};
+#define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index)
+
+static int snd_ymfpci_suspend(snd_card_t *card, pm_message_t state)
+{
+ ymfpci_t *chip = card->pm_private_data;
+ unsigned int i;
+
+ snd_pcm_suspend_all(chip->pcm);
+ snd_pcm_suspend_all(chip->pcm2);
+ snd_pcm_suspend_all(chip->pcm_spdif);
+ snd_pcm_suspend_all(chip->pcm_4ch);
+ snd_ac97_suspend(chip->ac97);
+ for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
+ chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
+ chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
+ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+ snd_ymfpci_disable_dsp(chip);
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+static int snd_ymfpci_resume(snd_card_t *card)
+{
+ ymfpci_t *chip = card->pm_private_data;
+ unsigned int i;
+
+ pci_enable_device(chip->pci);
+ pci_set_master(chip->pci);
+ snd_ymfpci_aclink_reset(chip->pci);
+ snd_ymfpci_codec_ready(chip, 0);
+ snd_ymfpci_download_image(chip);
+ udelay(100);
+
+ for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
+ snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]);
+
+ snd_ac97_resume(chip->ac97);
+
+ /* start hw again */
+ if (chip->start_count > 0) {
+ spin_lock_irq(&chip->reg_lock);
+ snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode);
+ chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+int __devinit snd_ymfpci_create(snd_card_t * card,
+ struct pci_dev * pci,
+ unsigned short old_legacy_ctrl,
+ ymfpci_t ** rchip)
+{
+ ymfpci_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_ymfpci_dev_free,
+ };
+
+ *rchip = NULL;
+
+ /* enable PCI device */
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ chip->old_legacy_ctrl = old_legacy_ctrl;
+ spin_lock_init(&chip->reg_lock);
+ spin_lock_init(&chip->voice_lock);
+ init_waitqueue_head(&chip->interrupt_sleep);
+ atomic_set(&chip->interrupt_sleep_count, 0);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ chip->device_id = pci->device;
+ pci_read_config_byte(pci, PCI_REVISION_ID, (u8 *)&chip->rev);
+ chip->reg_area_phys = pci_resource_start(pci, 0);
+ chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000);
+ pci_set_master(pci);
+
+ if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
+ snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
+ snd_ymfpci_free(chip);
+ return -EBUSY;
+ }
+ if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) {
+ snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_ymfpci_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ snd_ymfpci_aclink_reset(pci);
+ if (snd_ymfpci_codec_ready(chip, 0) < 0) {
+ snd_ymfpci_free(chip);
+ return -EIO;
+ }
+
+ snd_ymfpci_download_image(chip);
+
+ udelay(100); /* seems we need a delay after downloading image.. */
+
+ if (snd_ymfpci_memalloc(chip) < 0) {
+ snd_ymfpci_free(chip);
+ return -EIO;
+ }
+
+ if ((err = snd_ymfpci_ac3_init(chip)) < 0) {
+ snd_ymfpci_free(chip);
+ return err;
+ }
+
+#ifdef CONFIG_PM
+ chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32));
+ if (chip->saved_regs == NULL) {
+ snd_ymfpci_free(chip);
+ return -ENOMEM;
+ }
+ snd_card_set_pm_callback(card, snd_ymfpci_suspend, snd_ymfpci_resume, chip);
+#endif
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_ymfpci_free(chip);
+ return err;
+ }
+
+ snd_ymfpci_proc_init(card, chip);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = chip;
+ return 0;
+}