summaryrefslogtreecommitdiff
path: root/sound/oss
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/oss
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/oss')
-rw-r--r--sound/oss/CHANGELOG369
-rw-r--r--sound/oss/COPYING339
-rw-r--r--sound/oss/Kconfig1120
-rw-r--r--sound/oss/Makefile187
-rw-r--r--sound/oss/README.FIRST6
-rw-r--r--sound/oss/ac97.c452
-rw-r--r--sound/oss/ac97.h204
-rw-r--r--sound/oss/ac97_codec.c1576
-rw-r--r--sound/oss/ac97_plugin_ad1980.c126
-rw-r--r--sound/oss/aci.c711
-rw-r--r--sound/oss/aci.h57
-rw-r--r--sound/oss/ad1816.c1369
-rw-r--r--sound/oss/ad1848.c3159
-rw-r--r--sound/oss/ad1848.h25
-rw-r--r--sound/oss/ad1848_mixer.h253
-rw-r--r--sound/oss/ad1889.c1103
-rw-r--r--sound/oss/ad1889.h134
-rw-r--r--sound/oss/adlib_card.c73
-rw-r--r--sound/oss/aedsp16.c1381
-rw-r--r--sound/oss/ali5455.c3733
-rw-r--r--sound/oss/au1000.c2214
-rw-r--r--sound/oss/au1550_ac97.c2119
-rw-r--r--sound/oss/audio.c983
-rw-r--r--sound/oss/audio_syms.c16
-rw-r--r--sound/oss/awe_hw.h99
-rw-r--r--sound/oss/awe_wave.c6147
-rw-r--r--sound/oss/awe_wave.h77
-rw-r--r--sound/oss/bin2hex.c39
-rw-r--r--sound/oss/btaudio.c1136
-rw-r--r--sound/oss/cmpci.c3378
-rw-r--r--sound/oss/coproc.h12
-rw-r--r--sound/oss/cs4232.c520
-rw-r--r--sound/oss/cs4281/Makefile6
-rw-r--r--sound/oss/cs4281/cs4281_hwdefs.h1234
-rw-r--r--sound/oss/cs4281/cs4281_wrapper-24.c41
-rw-r--r--sound/oss/cs4281/cs4281m.c4505
-rw-r--r--sound/oss/cs4281/cs4281pm-24.c84
-rw-r--r--sound/oss/cs4281/cs4281pm.h74
-rw-r--r--sound/oss/cs461x.h1691
-rw-r--r--sound/oss/cs461x_image.h322
-rw-r--r--sound/oss/cs46xx.c5794
-rw-r--r--sound/oss/cs46xx_wrapper-24.h56
-rw-r--r--sound/oss/cs46xxpm-24.h52
-rw-r--r--sound/oss/cs46xxpm.h70
-rw-r--r--sound/oss/dev_table.c214
-rw-r--r--sound/oss/dev_table.h405
-rw-r--r--sound/oss/dm.h79
-rw-r--r--sound/oss/dmabuf.c1298
-rw-r--r--sound/oss/dmasound/Kconfig58
-rw-r--r--sound/oss/dmasound/Makefile13
-rw-r--r--sound/oss/dmasound/awacs_defs.h251
-rw-r--r--sound/oss/dmasound/dac3550a.c210
-rw-r--r--sound/oss/dmasound/dmasound.h277
-rw-r--r--sound/oss/dmasound/dmasound_atari.c1600
-rw-r--r--sound/oss/dmasound/dmasound_awacs.c3176
-rw-r--r--sound/oss/dmasound/dmasound_core.c1829
-rw-r--r--sound/oss/dmasound/dmasound_paula.c743
-rw-r--r--sound/oss/dmasound/dmasound_q40.c634
-rw-r--r--sound/oss/dmasound/tas3001c.c850
-rw-r--r--sound/oss/dmasound/tas3001c.h64
-rw-r--r--sound/oss/dmasound/tas3001c_tables.c375
-rw-r--r--sound/oss/dmasound/tas3004.c1140
-rw-r--r--sound/oss/dmasound/tas3004.h77
-rw-r--r--sound/oss/dmasound/tas3004_tables.c301
-rw-r--r--sound/oss/dmasound/tas_common.c214
-rw-r--r--sound/oss/dmasound/tas_common.h284
-rw-r--r--sound/oss/dmasound/tas_eq_prefs.h24
-rw-r--r--sound/oss/dmasound/tas_ioctl.h24
-rw-r--r--sound/oss/dmasound/trans_16.c897
-rw-r--r--sound/oss/emu10k1/8010.h737
-rw-r--r--sound/oss/emu10k1/Makefile17
-rw-r--r--sound/oss/emu10k1/audio.c1588
-rw-r--r--sound/oss/emu10k1/audio.h44
-rw-r--r--sound/oss/emu10k1/cardmi.c832
-rw-r--r--sound/oss/emu10k1/cardmi.h97
-rw-r--r--sound/oss/emu10k1/cardmo.c229
-rw-r--r--sound/oss/emu10k1/cardmo.h62
-rw-r--r--sound/oss/emu10k1/cardwi.c373
-rw-r--r--sound/oss/emu10k1/cardwi.h91
-rw-r--r--sound/oss/emu10k1/cardwo.c643
-rw-r--r--sound/oss/emu10k1/cardwo.h90
-rw-r--r--sound/oss/emu10k1/ecard.c157
-rw-r--r--sound/oss/emu10k1/ecard.h113
-rw-r--r--sound/oss/emu10k1/efxmgr.c220
-rw-r--r--sound/oss/emu10k1/efxmgr.h270
-rw-r--r--sound/oss/emu10k1/emuadxmg.c104
-rw-r--r--sound/oss/emu10k1/hwaccess.c507
-rw-r--r--sound/oss/emu10k1/hwaccess.h247
-rw-r--r--sound/oss/emu10k1/icardmid.h163
-rw-r--r--sound/oss/emu10k1/icardwav.h53
-rw-r--r--sound/oss/emu10k1/irqmgr.c113
-rw-r--r--sound/oss/emu10k1/irqmgr.h52
-rw-r--r--sound/oss/emu10k1/main.c1475
-rw-r--r--sound/oss/emu10k1/midi.c613
-rw-r--r--sound/oss/emu10k1/midi.h78
-rw-r--r--sound/oss/emu10k1/mixer.c690
-rw-r--r--sound/oss/emu10k1/passthrough.c236
-rw-r--r--sound/oss/emu10k1/passthrough.h99
-rw-r--r--sound/oss/emu10k1/recmgr.c147
-rw-r--r--sound/oss/emu10k1/recmgr.h48
-rw-r--r--sound/oss/emu10k1/timer.c176
-rw-r--r--sound/oss/emu10k1/timer.h54
-rw-r--r--sound/oss/emu10k1/voicemgr.c398
-rw-r--r--sound/oss/emu10k1/voicemgr.h103
-rw-r--r--sound/oss/es1370.c2789
-rw-r--r--sound/oss/es1371.c3097
-rw-r--r--sound/oss/esssolo1.c2497
-rw-r--r--sound/oss/forte.c2137
-rw-r--r--sound/oss/gus.h24
-rw-r--r--sound/oss/gus_card.c293
-rw-r--r--sound/oss/gus_hw.h50
-rw-r--r--sound/oss/gus_linearvol.h18
-rw-r--r--sound/oss/gus_midi.c256
-rw-r--r--sound/oss/gus_vol.c153
-rw-r--r--sound/oss/gus_wave.c3464
-rw-r--r--sound/oss/hal2.c1557
-rw-r--r--sound/oss/hal2.h248
-rw-r--r--sound/oss/harmony.c1330
-rw-r--r--sound/oss/hex2hex.c101
-rw-r--r--sound/oss/i810_audio.c3658
-rw-r--r--sound/oss/ics2101.c247
-rw-r--r--sound/oss/ite8172.c2259
-rw-r--r--sound/oss/iwmem.h36
-rw-r--r--sound/oss/kahlua.c232
-rw-r--r--sound/oss/mad16.c1097
-rw-r--r--sound/oss/maestro.c3832
-rw-r--r--sound/oss/maestro.h60
-rw-r--r--sound/oss/maestro3.c2973
-rw-r--r--sound/oss/maestro3.h821
-rw-r--r--sound/oss/maui.c478
-rw-r--r--sound/oss/midi_ctrl.h22
-rw-r--r--sound/oss/midi_syms.c29
-rw-r--r--sound/oss/midi_synth.c697
-rw-r--r--sound/oss/midi_synth.h47
-rw-r--r--sound/oss/midibuf.c431
-rw-r--r--sound/oss/mpu401.c1826
-rw-r--r--sound/oss/mpu401.h14
-rw-r--r--sound/oss/msnd.c419
-rw-r--r--sound/oss/msnd.h280
-rw-r--r--sound/oss/msnd_classic.c3
-rw-r--r--sound/oss/msnd_classic.h188
-rw-r--r--sound/oss/msnd_pinnacle.c1922
-rw-r--r--sound/oss/msnd_pinnacle.h249
-rw-r--r--sound/oss/nec_vrc5477.c2059
-rw-r--r--sound/oss/nm256.h295
-rw-r--r--sound/oss/nm256_audio.c1707
-rw-r--r--sound/oss/nm256_coeff.h4697
-rw-r--r--sound/oss/opl3.c1257
-rw-r--r--sound/oss/opl3.h5
-rw-r--r--sound/oss/opl3_hw.h246
-rw-r--r--sound/oss/opl3sa.c329
-rw-r--r--sound/oss/opl3sa2.c1129
-rw-r--r--sound/oss/os.h51
-rw-r--r--sound/oss/pas2.h17
-rw-r--r--sound/oss/pas2_card.c458
-rw-r--r--sound/oss/pas2_midi.c262
-rw-r--r--sound/oss/pas2_mixer.c336
-rw-r--r--sound/oss/pas2_pcm.c437
-rw-r--r--sound/oss/pss.c1283
-rw-r--r--sound/oss/rme96xx.c1861
-rw-r--r--sound/oss/rme96xx.h78
-rw-r--r--sound/oss/sb.h185
-rw-r--r--sound/oss/sb_audio.c1098
-rw-r--r--sound/oss/sb_card.c347
-rw-r--r--sound/oss/sb_card.h149
-rw-r--r--sound/oss/sb_common.c1291
-rw-r--r--sound/oss/sb_ess.c1832
-rw-r--r--sound/oss/sb_ess.h34
-rw-r--r--sound/oss/sb_midi.c205
-rw-r--r--sound/oss/sb_mixer.c768
-rw-r--r--sound/oss/sb_mixer.h105
-rw-r--r--sound/oss/sequencer.c1684
-rw-r--r--sound/oss/sequencer_syms.c30
-rw-r--r--sound/oss/sgalaxy.c207
-rw-r--r--sound/oss/sh_dac_audio.c325
-rw-r--r--sound/oss/skeleton.c219
-rw-r--r--sound/oss/sonicvibes.c2792
-rw-r--r--sound/oss/sound_calls.h90
-rw-r--r--sound/oss/sound_config.h154
-rw-r--r--sound/oss/sound_firmware.h2
-rw-r--r--sound/oss/sound_syms.c50
-rw-r--r--sound/oss/sound_timer.c323
-rw-r--r--sound/oss/soundcard.c751
-rw-r--r--sound/oss/soundvers.h2
-rw-r--r--sound/oss/sscape.c1485
-rw-r--r--sound/oss/swarm_cs4297a.c2742
-rw-r--r--sound/oss/sys_timer.c289
-rw-r--r--sound/oss/trident.c4628
-rw-r--r--sound/oss/trident.h358
-rw-r--r--sound/oss/trix.c525
-rw-r--r--sound/oss/tuning.h29
-rw-r--r--sound/oss/uart401.c481
-rw-r--r--sound/oss/uart6850.c362
-rw-r--r--sound/oss/ulaw.h69
-rw-r--r--sound/oss/v_midi.c291
-rw-r--r--sound/oss/v_midi.h15
-rw-r--r--sound/oss/via82cxxx_audio.c3615
-rw-r--r--sound/oss/vidc.c561
-rw-r--r--sound/oss/vidc.h67
-rw-r--r--sound/oss/vidc_fill.S218
-rw-r--r--sound/oss/vwsnd.c3486
-rw-r--r--sound/oss/waveartist.c2035
-rw-r--r--sound/oss/waveartist.h92
-rw-r--r--sound/oss/wavfront.c3538
-rw-r--r--sound/oss/wf_midi.c880
-rw-r--r--sound/oss/ymfpci.c2691
-rw-r--r--sound/oss/ymfpci.h360
-rw-r--r--sound/oss/ymfpci_image.h1565
-rw-r--r--sound/oss/yss225.c319
-rw-r--r--sound/oss/yss225.h24
210 files changed, 172711 insertions, 0 deletions
diff --git a/sound/oss/CHANGELOG b/sound/oss/CHANGELOG
new file mode 100644
index 000000000000..8706cd66ca1f
--- /dev/null
+++ b/sound/oss/CHANGELOG
@@ -0,0 +1,369 @@
+Note these changes relate to Hannu's code and don't include the changes
+made outside of this for modularising the sound
+
+Changelog for version 3.8o
+--------------------------
+
+Since 3.8h
+- Included support for OPL3-SA1 and SoftOSS
+
+Since 3.8
+- Fixed SNDCTL_DSP_GETOSPACE
+- Compatibility fixes for Linux 2.1.47
+
+Since 3.8-beta21
+- Fixed all known bugs (I think).
+
+Since 3.8-beta8
+- Lot of fixes to audio playback code in dmabuf.c
+
+Since 3.8-beta6
+- Fixed the famous Quake delay bug.
+
+Since 3.8-beta5
+- Fixed many bugs in audio playback.
+
+Since 3.8-beta4
+- Just minor changes.
+
+Since 3.8-beta1
+- Major rewrite of audio playback handling.
+- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
+
+Since 3.7-beta#
+- Passing of ioctl() parameters between soundcard.c and other modules has been
+changed so that arg always points to kernel space.
+- Some bugfixes.
+
+Since 3.7-beta5
+- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
+stream of received 0x00 bytes when the MIDI receiver is enabled.
+
+Since 3.5
+- Changes almost everywhere.
+- Support for OPTi 82C924-based sound cards.
+
+Since 3.5.4-beta8
+- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode
+ with GUS.
+- Limited minimum fragment size with some audio devices (GUS=512 and
+ SB=32). These devices require more time to "recover" from processing
+ of each fragment.
+
+Since 3.5.4-beta6/7
+- There seems to be problems in the OPTi 82C930 so cards based on this
+ chip don't necessarily work yet. There are problems in detecting the
+ MIDI interface. Also mixer volumes may be seriously wrong on some systems.
+ You can safely use this driver version with C930 if it looks to work.
+ However please don't complain if you have problems with it. C930 support
+ should be fixed in future releases.
+- Got initialization of GUS PnP to work. With this version GUS PnP should
+ work in GUS compatible mode after initialization using isapnptools.
+- Fixed a bug in handling of full duplex cards in write only mode. This has
+ been causing "audio device opening" errors with RealAudio player.
+
+Since 3.5.4.beta5
+- Changes to OPTi 82C930 driver.
+- Major changes to the Soundscape driver. The driver requires now just one
+ DMA channel. The extra audio/dsp device (the "Not functional" one) used
+ for code download in the earlier versions has been eliminated. There is now
+ just one /dev/dsp# device which is used both for code download and audio.
+
+Since 3.5.4.beta4
+- Minor changes.
+
+Since 3.5.4-beta2
+- Fixed silent playback with ESS 688/1688.
+- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one
+ is required for 8 and 16 bit modes).
+- Added the "lowlevel" subdirectory for additional low level drivers that
+ are not part of USS core. See lowlevel/README for more info.
+- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in
+ miroPCM sound cards. See lowlevel/aci.readme for more info.
+- Support for Aztech Washington chipset (AZT2316 ASIC).
+
+Since 3.5.4-beta1
+- Reduced clicking with AD1848.
+- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback
+ is sometimes just white noise (occurs randomly).
+
+Since 3.5.2
+- Major changes to the SB/Jazz16/ESS driver (most parts rewritten).
+ The most noticeable new feature is support for multiple SB cards at the same
+ time.
+- Renamed sb16_midi.c to uart401.c. Also modified it to work also with
+ other MPU401 UART compatible cards than SB16/ESS/Jazz.
+- Some changes which reduce clicking in audio playback.
+- Copying policy is now GPL.
+
+Since 3.5.1
+- TB Maui initialization support
+Since 3.5
+- Improved handling of playback underrun situations.
+
+Since 3.5-beta10
+- Bug fixing
+
+Since 3.5-beta9
+- Fixed for compatibility with Linux 1.3.70 and later.
+- Changed boot time passing of 16 bit DMA channel number to SB driver.
+
+Since 3.5-beta8
+- Minor changes
+
+Since 3.5-beta7
+- enhancements to configure program (by Jeff Tranter):
+ - prompts are in same format as 1.3.x Linux kernel config program
+ - on-line help for each question
+ - fixed some compile warnings detected by gcc/g++ -Wall
+ - minor grammatical changes to prompts
+
+Since 3.5-beta6
+- Fixed bugs in mmap() support.
+- Minor changes to Maui driver.
+
+Since 3.5-beta5
+- Fixed crash after recording with ESS688. It's generally a good
+ idea to stop inbound DMA transfers before freeing the memory
+ buffer.
+- Fixed handling of AD1845 codec (for example Shuttle Sound System).
+- Few other fixes.
+
+Since 3.5-beta4
+- Fixed bug in handling of uninitialized instruments with GUS.
+
+Since 3.5-beta3
+- Few changes which decrease popping at end/beginning of audio playback.
+
+Since 3.5-beta2
+- Removed MAD16+CS4231 hack made in previous version since it didn't
+ help.
+- Fixed the above bug in proper way and in proper place. Many thanks
+ to James Hightower.
+
+Since 3.5-beta1
+- Bug fixes.
+- Full duplex audio with MAD16+CS4231 may work now. The driver configures
+ SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels.
+ The side effect is that all 8 bit DMA channels (0,1,3) are populated in
+ duplex mode.
+
+Since 3.5-alpha9
+- Bug fixes (mostly in Jazz16 and ESS1688/688 supports).
+- Temporarily disabled recording with ESS1688/688 since it causes crash.
+- Changed audio buffer partitioning algorithm so that it selects
+ smaller fragment size than earlier. This improves real time capabilities
+ of the driver and makes recording to disk to work better. Unfortunately
+ this change breaks some programs which assume that fragments cannot be
+ shorter than 4096 bytes.
+
+Since 3.5-alpha8
+- Bug fixes
+
+Since 3.5-alpha7
+- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable
+ using command "cd /linux/drivers/sound;make script" and then
+ just run kernel's make config normally.
+- Minor fixes to the SB support. Hopefully the driver works with
+ all SB models now.
+- Added support for ESS ES1688 "AudioDrive" based cards.
+
+Since 3.5-alpha6
+- SB Pro and SB16 supports are no longer separately selectable options.
+ Enabling SB enables them too.
+- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified
+configure to handle this.
+- Removed initialization messages from the
+modularized version. They can be enabled by using init_trace=1 in
+the insmod command line (insmod sound init_trace=1).
+- More AIX stuff.
+- Added support for synchronizing dsp/audio devices with /dev/sequencer.
+- mmap() support for dsp/audio devices.
+
+Since 3.5-alpha5
+- AIX port.
+- Changed some xxx_PATCH macros in soundcard.h to work with
+ big endian machines.
+
+Since 3.5-alpha4
+- Removed the 'setfx' stuff from the version distributed with kernel
+ sources. Running 'setfx' is required again.
+
+Since 3.5-alpha3
+- Moved stuff from the 'setfx' program to the AudioTrix Pro driver.
+
+Since 3.5-alpha2
+- Modifications to makefile and configure.c. Unnecessary sources
+ are no longer compiled. Newly created local.h is also copied to
+ /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces
+ new local.h which is compatible with current version of the driver.
+- Some fixes to the SB16 support.
+- Fixed random protection fault in gus_wave.c
+
+Since 3.5-alpha1
+- Modified to work with Linux-1.3.33 and later
+- Some minor changes
+
+Since 3.0.2
+- Support for CS4232 based PnP cards (AcerMagic S23 etc).
+- Full duplex support for some CS4231, CS4232 and AD1845 based cards
+(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards
+having a codec mentioned above).
+- Almost fully rewritten loadable modules support.
+- Fixed some bugs.
+- Huge amount of testing (more testing is still required).
+- mmap() support (works with some cards). Requires much more testing.
+- Sample/patch/program loading for TB Maui/Tropez. No initialization
+since TB doesn't allow me to release that code.
+- Using CS4231 compatible codecs as timer for /dev/music.
+
+Since 3.0.1
+- Added allocation of I/O ports, DMA channels and interrupts
+to the initialization code. This may break modules support since
+the driver may not free some resources on unload. Should be fixed soon.
+
+Since 3.0
+- Some important bug fixes.
+- select() for /dev/dsp and /dev/audio (Linux only).
+(To use select() with read, you have to call read() to start
+the recording. Calling write() kills recording immediately so
+use select() carefully when you are writing a half duplex app.
+Full duplex mode is not implemented yet.) Select works also with
+/dev/sequencer and /dev/music. Maybe with /dev/midi## too.
+
+Since 3.0-beta2
+- Minor fixes.
+- Added Readme.cards
+
+Since 3.0-beta1
+- Minor fixes to the modules support.
+- Eliminated call to sb_free_irq() in ad1848.c
+- Rewritten MAD16&Mozart support (not tested with MAD16 Pro).
+- Fix to DMA initialization of PSS cards.
+- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.)
+- Fixed some bugs in the PSS driver which caused I/O errors with
+ the MSS mode (/dev/dsp).
+
+Since 3.0-950506
+- Recording with GUS MAX fixed. It works when the driver is configured
+ to use two DMA channels with GUS MAX (16 bit ones recommended).
+
+Since 3.0-94xxxx
+- Too many changes
+
+Since 3.0-940818
+- Fixes for Linux 1.1.4x.
+- Disables Disney Sound System with SG NX Pro 16 (less noise).
+
+Since 2.90-2
+- Fixes to soundcard.h
+- Non blocking mode to /dev/sequencer
+- Experimental detection code for Ensoniq Soundscape.
+
+Since 2.90
+- Minor and major bug fixes
+
+Since pre-3.0-940712
+- GUS MAX support
+- Partially working MSS/WSS support (could work with some cards).
+- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
+ (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
+ GUS MAX, but it doesn't work yet.
+Since pre-3.0-940426
+- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
+This codec chip is used in various sound cards. This version is developed
+for the 16 bit daughtercard of GUS. It should work with other cards also
+if the following requirements are met:
+ - The I/O, IRQ and DMA settings are jumper selectable or
+ the card is initialized by booting DOS before booting Linux (etc.).
+ - You add the IO, IRQ and DMA settings manually to the local.h.
+ (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
+ the base address bust be the base address of the codec chip not the
+ card itself. For the GUS16 these are the same but most MSS compatible
+ cards have the codec located at card_base+4.
+- Some minor changes
+
+Since 2.5 (******* MAJOR REWRITE ***********)
+
+This version is based on v2.3. I have tried to maintain two versions
+together so that this one should have the same features than v2.5.
+Something may still be missing. If you notice such things, please let me
+know.
+
+The Readme.v30 contains more details.
+
+- /dev/midi## devices.
+- /dev/sequencer2
+
+Since 2.5-beta2
+- Some fine tuning to the GUS v3.7 mixer code.
+- Fixed speed limits for the plain SB (1.0 to 2.0).
+
+Since 2.5-beta
+- Fixed OPL-3 detection with SB. Caused problems with PAS16.
+- GUS v3.7 mixer support.
+
+Since 2.4
+- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
+- Fixed truncated sound on /dev/dsp when the device is closed.
+- Linear volume mode for GUS
+- Pitch bends larger than +/- 2 octaves.
+- MIDI recording for SB and SB Pro. (Untested).
+- Some other fixes.
+- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
+- Implemented better detection for OPL-3. This should be useful if you
+ have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
+- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
+
+Since 2.3b
+- Fixed bug which made it impossible to make long recordings to disk.
+ Recording was not restarted after a buffer overflow situation.
+- Limited mixer support for GUS.
+- Numerous improvements to the GUS driver by Andrew Robinson. Including
+ some click removal etc.
+
+Since 2.3
+- Fixed some minor bugs in the SB16 driver.
+
+Since 2.2b
+- Full SB16 DSP support. 8/16 bit, mono/stereo
+- The SCO and FreeBSD versions should be in sync now. There are some
+ problems with SB16 and GUS in the FreeBSD versions.
+ The DMA buffer allocation of the SCO version has been polished but
+ there could still be some problems. At least it hogs memory.
+ The DMA channel
+ configuration method used in the SCO/System is a hack.
+- Support for the MPU emulation of the SB16.
+- Some big arrays are now allocated boot time. This makes the BSS segment
+ smaller which makes it possible to use the full driver with
+ NetBSD. These arrays are not allocated if no suitable sound card is available.
+- Fixed a bug in the compute_and_set_volume in gus_wave.c
+- Fixed the too fast mono playback problem of SB Pro and PAS16.
+
+Since 2.2
+- Stereo recording for SB Pro. Somehow it was missing and nobody
+ had noticed it earlier.
+- Minor polishing.
+- Interpreting of boot time arguments (sound=) for Linux.
+- Breakup of sb_dsp.c. Parts of the code has been moved to
+ sb_mixer.c and sb_midi.c
+
+Since 2.1
+- Preliminary support for SB16.
+ - The SB16 mixer is supported in its native mode.
+ - Digitized voice capability up to 44.1 kHz/8 bit/mono
+ (16 bit and stereo support coming in the next release).
+- Fixed some bugs in the digitized voice driver for PAS16.
+- Proper initialization of the SB emulation of latest PAS16 models.
+
+- Significantly improved /dev/dsp and /dev/audio support.
+ - Now supports half duplex mode. It's now possible to record and
+ playback without closing and reopening the device.
+ - It's possible to use smaller buffers than earlier. There is a new
+ ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
+ This call instructs the driver to use smaller buffers. The default
+ buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
+ immediately after opening the device.
+
+Since 2.0
+Just cosmetic changes.
diff --git a/sound/oss/COPYING b/sound/oss/COPYING
new file mode 100644
index 000000000000..916d1f0f2842
--- /dev/null
+++ b/sound/oss/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
new file mode 100644
index 000000000000..d303c2ed6e5a
--- /dev/null
+++ b/sound/oss/Kconfig
@@ -0,0 +1,1120 @@
+# drivers/sound/Config.in
+#
+# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+# More hacking for modularisation.
+#
+# Prompt user for primary drivers.
+config SOUND_BT878
+ tristate "BT878 audio dma"
+ depends on SOUND_PRIME!=n && SOUND
+ ---help---
+ Audio DMA support for bt878 based grabber boards. As you might have
+ already noticed, bt878 is listed with two functions in /proc/pci.
+ Function 0 does the video stuff (bt848 compatible), function 1 does
+ the same for audio data. This is a driver for the audio part of
+ the chip. If you say 'Y' here you get a oss-compatible dsp device
+ where you can record from. If you want just watch TV you probably
+ don't need this driver as most TV cards handle sound with a short
+ cable from the TV card to your sound card's line-in.
+
+ To compile this driver as a module, choose M here: the module will
+ be called btaudio.
+
+config SOUND_CMPCI
+ tristate "C-Media PCI (CMI8338/8738)"
+ depends on SOUND_PRIME!=n && SOUND && PCI
+ help
+ Say Y or M if you have a PCI sound card using the CMI8338
+ or the CMI8738 chipset. Data on these chips are available at
+ <http://www.cmedia.com.tw/>.
+
+ A userspace utility to control some internal registers of these
+ chips is available at
+ <http://member.nifty.ne.jp/Breeze/softwares/unix/cmictl-e.html>.
+
+config SOUND_CMPCI_FM
+ bool "Enable legacy FM"
+ depends on SOUND_CMPCI && X86
+ help
+ Say Y here to enable the legacy FM (frequency-modulation) synthesizer
+ support on a card using the CMI8338 or CMI8378 chipset. Even it is
+ enabled, you need to set fmio as proper value to enable it.
+ Say N here if you don't need this.
+
+config SOUND_CMPCI_MIDI
+ bool "Enable legacy MPU-401"
+ depends on SOUND_CMPCI && X86
+ help
+ Say Y here to enable the legacy MPU401 MIDI synthesizer support on a
+ card using the CMI8338 or CMI8378 chipset. Even it is enabled,
+ you need to set mpuio as proper value to enable it.
+ Say N here if you don't need this.
+
+config SOUND_CMPCI_JOYSTICK
+ bool "Enable joystick"
+ depends on SOUND_CMPCI && X86
+ help
+ Say Y here in order to enable the joystick port on a sound card using
+ the CMI8338 or the CMI8738 chipset. You need to config the
+ gameport support and set joystick parameter as 1 to use it.
+ Say N here if you don't need this.
+
+config SOUND_EMU10K1
+ tristate "Creative SBLive! (EMU10K1)"
+ depends on SOUND_PRIME!=n && SOUND && PCI
+ ---help---
+ Say Y or M if you have a PCI sound card using the EMU10K1 chipset,
+ such as the Creative SBLive!, SB PCI512 or Emu-APS.
+
+ For more information on this driver and the degree of support for
+ the different card models please check:
+
+ <http://sourceforge.net/projects/emu10k1/>
+
+ It is now possible to load dsp microcode patches into the EMU10K1
+ chip. These patches are used to implement real time sound
+ processing effects which include for example: signal routing,
+ bass/treble control, AC3 passthrough, ...
+ Userspace tools to create new patches and load/unload them can be
+ found in the emu-tools package at the above URL.
+
+config MIDI_EMU10K1
+ bool "Creative SBLive! MIDI (EXPERIMENTAL)"
+ depends on SOUND_EMU10K1 && EXPERIMENTAL
+ help
+ Say Y if you want to be able to use the OSS /dev/sequencer
+ interface. This code is still experimental.
+
+config SOUND_FUSION
+ tristate "Crystal SoundFusion (CS4280/461x)"
+ depends on SOUND_PRIME!=n && SOUND
+ help
+ This module drives the Crystal SoundFusion devices (CS4280/46xx
+ series) when wired as native sound drivers with AC97 codecs. If
+ this driver does not work try the CS4232 driver.
+
+config SOUND_CS4281
+ tristate "Crystal Sound CS4281"
+ depends on SOUND_PRIME!=n && SOUND
+ help
+ Picture and feature list at
+ <http://www.pcbroker.com/crystal4281.html>.
+
+config SOUND_BCM_CS4297A
+ tristate "Crystal Sound CS4297a (for Swarm)"
+ depends on SOUND_PRIME!=n && SIBYTE_SWARM && SOUND
+ help
+ The BCM91250A has a Crystal CS4297a on synchronous serial
+ port B (in addition to the DB-9 serial port). Say Y or M
+ here to enable the sound chip instead of the UART. Also
+ note that CONFIG_KGDB should not be enabled at the same
+ time, since it also attempts to use this UART port.
+
+config SOUND_ES1370
+ tristate "Ensoniq AudioPCI (ES1370)"
+ depends on SOUND_PRIME!=n && SOUND && PCI && SOUND_GAMEPORT
+ help
+ Say Y or M if you have a PCI sound card utilizing the Ensoniq
+ ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find
+ out if your sound card uses an ES1370 without removing your
+ computer's cover, use lspci -n and look for the PCI ID
+ 1274:5000. Since Ensoniq was bought by Creative Labs,
+ Sound Blaster 64/PCI models are either ES1370 or ES1371 based.
+ This driver differs slightly from OSS/Free, so PLEASE READ
+ <file:Documentation/sound/oss/es1370>.
+
+config SOUND_ES1371
+ tristate "Creative Ensoniq AudioPCI 97 (ES1371)"
+ depends on SOUND_PRIME!=n && SOUND && PCI && SOUND_GAMEPORT
+ help
+ Say Y or M if you have a PCI sound card utilizing the Ensoniq
+ ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if
+ your sound card uses an ES1371 without removing your computer's
+ cover, use lspci -n and look for the PCI ID 1274:1371. Since
+ Ensoniq was bought by Creative Labs, Sound Blaster 64/PCI
+ models are either ES1370 or ES1371 based. This driver differs
+ slightly from OSS/Free, so PLEASE READ
+ <file:Documentation/sound/oss/es1371>.
+
+config SOUND_ESSSOLO1
+ tristate "ESS Technology Solo1"
+ depends on SOUND_PRIME!=n && SOUND && SOUND_GAMEPORT && PCI
+ help
+ Say Y or M if you have a PCI sound card utilizing the ESS Technology
+ Solo1 chip. To find out if your sound card uses a
+ Solo1 chip without removing your computer's cover, use
+ lspci -n and look for the PCI ID 125D:1969. This driver
+ differs slightly from OSS/Free, so PLEASE READ
+ <file:Documentation/sound/oss/solo1>.
+
+config SOUND_MAESTRO
+ tristate "ESS Maestro, Maestro2, Maestro2E driver"
+ depends on SOUND_PRIME!=n && SOUND && PCI
+ help
+ Say Y or M if you have a sound system driven by ESS's Maestro line
+ of PCI sound chips. These include the Maestro 1, Maestro 2, and
+ Maestro 2E. See <file:Documentation/sound/oss/Maestro> for more
+ details.
+
+config SOUND_MAESTRO3
+ tristate "ESS Maestro3/Allegro driver (EXPERIMENTAL)"
+ depends on SOUND_PRIME!=n && SOUND && PCI && EXPERIMENTAL
+ help
+ Say Y or M if you have a sound system driven by ESS's Maestro 3
+ PCI sound chip.
+
+config SOUND_ICH
+ tristate "Intel ICH (i8xx) audio support"
+ depends on SOUND_PRIME!=n && PCI
+ help
+ Support for integral audio in Intel's I/O Controller Hub (ICH)
+ chipset, as used on the 810/820/840 motherboards.
+
+config SOUND_HARMONY
+ tristate "PA Harmony audio driver"
+ depends on GSC_LASI && SOUND_PRIME!=n
+ help
+ Say 'Y' or 'M' to include support for Harmony soundchip
+ on HP 712, 715/new and many other GSC based machines.
+
+config SOUND_SONICVIBES
+ tristate "S3 SonicVibes"
+ depends on SOUND_PRIME!=n && SOUND && SOUND_GAMEPORT
+ help
+ Say Y or M if you have a PCI sound card utilizing the S3
+ SonicVibes chipset. To find out if your sound card uses a
+ SonicVibes chip without removing your computer's cover, use
+ lspci -n and look for the PCI ID 5333:CA00. This driver
+ differs slightly from OSS/Free, so PLEASE READ
+ <file:Documentation/sound/oss/sonicvibes>.
+
+config SOUND_VWSND
+ tristate "SGI Visual Workstation Sound"
+ depends on SOUND_PRIME!=n && X86_VISWS && SOUND
+ help
+ Say Y or M if you have an SGI Visual Workstation and you want to be
+ able to use its on-board audio. Read
+ <file:Documentation/sound/oss/vwsnd> for more info on this driver's
+ capabilities.
+
+config SOUND_HAL2
+ tristate "SGI HAL2 sound (EXPERIMENTAL)"
+ depends on SOUND_PRIME!=n && SOUND && SGI_IP22 && EXPERIMENTAL
+ help
+ Say Y or M if you have an SGI Indy system and want to be able to
+ use it's on-board A2 audio system.
+
+config SOUND_IT8172
+ tristate "IT8172G Sound"
+ depends on SOUND_PRIME!=n && (MIPS_ITE8172 || MIPS_IVR) && SOUND
+
+config SOUND_VRC5477
+ tristate "NEC Vrc5477 AC97 sound"
+ depends on SOUND_PRIME!=n && DDB5477 && SOUND
+ help
+ Say Y here to enable sound support for the NEC Vrc5477 chip, an
+ integrated, multi-function controller chip for MIPS CPUs. Works
+ with the AC97 codec.
+
+config SOUND_AU1000
+ tristate "Au1000 Sound"
+ depends on SOUND_PRIME!=n && (SOC_AU1000 || SOC_AU1100 || SOC_AU1500) && SOUND
+
+config SOUND_AU1550_AC97
+ tristate "Au1550 AC97 Sound"
+ depends on SOUND_PRIME!=n && SOC_AU1550 && SOUND
+
+config SOUND_TRIDENT
+ tristate "Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core"
+ depends on SOUND_PRIME!=n && SOUND && SOUND_GAMEPORT
+ ---help---
+ Say Y or M if you have a PCI sound card utilizing the Trident
+ 4DWave-DX/NX chipset or your mother board chipset has SiS 7018
+ or ALi 5451 built-in. The SiS 7018 PCI Audio Core is embedded
+ in SiS960 Super South Bridge and SiS540/630 Single Chipset.
+ The ALi 5451 PCI Audio Core is embedded in ALi M1535, M1535D,
+ M1535+ or M1535D+ South Bridge.
+
+ Use lspci -n to find out if your sound card or chipset uses
+ Trident 4DWave or SiS 7018. PCI ID 1023:2000 or 1023:2001 stands
+ for Trident 4Dwave. PCI ID 1039:7018 stands for SiS7018. PCI ID
+ 10B9:5451 stands for ALi5451.
+
+ This driver supports S/PDIF in/out (record/playback) for ALi 5451
+ embedded in ALi M1535+ and M1535D+. Note that they aren't all
+ enabled by default; you can enable them by saying Y to "/proc file
+ system support" and "Sysctl support", and after the /proc file
+ system has been mounted, executing the command
+
+ command what is enabled
+
+ echo 0>/proc/ALi5451 pcm out is also set to S/PDIF out. (Default).
+
+ echo 1>/proc/ALi5451 use S/PDIF out to output pcm data.
+
+ echo 2>/proc/ALi5451 use S/PDIF out to output non-pcm data.
+ (AC3...).
+
+ echo 3>/proc/ALi5451 record from Ac97 in(MIC, Line in...).
+ (Default).
+
+ echo 4>/proc/ALi5451 no matter Ac97 settings, record from S/PDIF
+ in.
+
+
+ This driver differs slightly from OSS/Free, so PLEASE READ the
+ comments at the top of <file:drivers/sound/trident.c>.
+
+config SOUND_MSNDCLAS
+ tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
+ depends on SOUND_PRIME!=n && SOUND && (m || !STANDALONE)
+ help
+ Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
+ Monterey (not for the Pinnacle or Fiji).
+
+ See <file:Documentation/sound/oss/MultiSound> for important information
+ about this driver. Note that it has been discontinued, but the
+ Voyetra Turtle Beach knowledge base entry for it is still available
+ at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
+
+comment "Compiled-in MSND Classic support requires firmware during compilation."
+ depends on SOUND_PRIME && SOUND_MSNDCLAS=y
+
+config MSNDCLAS_HAVE_BOOT
+ bool
+ depends on SOUND_MSNDCLAS=y && !STANDALONE
+ default y
+
+config MSNDCLAS_INIT_FILE
+ string "Full pathname of MSNDINIT.BIN firmware file"
+ depends on SOUND_MSNDCLAS
+ default "/etc/sound/msndinit.bin"
+ help
+ The MultiSound cards have two firmware files which are required for
+ operation, and are not currently included. These files can be
+ obtained from Turtle Beach. See
+ <file:Documentation/sound/oss/MultiSound> for information on how to
+ obtain this.
+
+config MSNDCLAS_PERM_FILE
+ string "Full pathname of MSNDPERM.BIN firmware file"
+ depends on SOUND_MSNDCLAS
+ default "/etc/sound/msndperm.bin"
+ help
+ The MultiSound cards have two firmware files which are required for
+ operation, and are not currently included. These files can be
+ obtained from Turtle Beach. See
+ <file:Documentation/sound/oss/MultiSound> for information on how to
+ obtain this.
+
+config MSNDCLAS_IRQ
+ int "MSND Classic IRQ 5, 7, 9, 10, 11, 12"
+ depends on SOUND_MSNDCLAS=y
+ default "5"
+ help
+ Interrupt Request line for the MultiSound Classic and related cards.
+
+config MSNDCLAS_MEM
+ hex "MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000"
+ depends on SOUND_MSNDCLAS=y
+ default "D0000"
+ help
+ Memory-mapped I/O base address for the MultiSound Classic and
+ related cards.
+
+config MSNDCLAS_IO
+ hex "MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
+ depends on SOUND_MSNDCLAS=y
+ default "290"
+ help
+ I/O port address for the MultiSound Classic and related cards.
+
+config SOUND_MSNDPIN
+ tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji"
+ depends on SOUND_PRIME!=n && SOUND && (m || !STANDALONE)
+ help
+ Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji.
+ See <file:Documentation/sound/oss/MultiSound> for important information
+ about this driver. Note that it has been discontinued, but the
+ Voyetra Turtle Beach knowledge base entry for it is still available
+ at <http://www.turtlebeach.com/site/kb_ftp/600.asp>.
+
+comment "Compiled-in MSND Pinnacle support requires firmware during compilation."
+ depends on SOUND_PRIME && SOUND_MSNDPIN=y
+
+config MSNDPIN_HAVE_BOOT
+ bool
+ depends on SOUND_MSNDPIN=y
+ default y
+
+config MSNDPIN_INIT_FILE
+ string "Full pathname of PNDSPINI.BIN firmware file"
+ depends on SOUND_MSNDPIN
+ default "/etc/sound/pndspini.bin"
+ help
+ The MultiSound cards have two firmware files which are required
+ for operation, and are not currently included. These files can be
+ obtained from Turtle Beach. See
+ <file:Documentation/sound/oss/MultiSound> for information on how to
+ obtain this.
+
+config MSNDPIN_PERM_FILE
+ string "Full pathname of PNDSPERM.BIN firmware file"
+ depends on SOUND_MSNDPIN
+ default "/etc/sound/pndsperm.bin"
+ help
+ The MultiSound cards have two firmware files which are required for
+ operation, and are not currently included. These files can be
+ obtained from Turtle Beach. See
+ <file:Documentation/sound/oss/MultiSound> for information on how to
+ obtain this.
+
+config MSNDPIN_IRQ
+ int "MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12"
+ depends on SOUND_MSNDPIN=y
+ default "5"
+ help
+ Interrupt request line for the primary synthesizer on MultiSound
+ Pinnacle and Fiji sound cards.
+
+config MSNDPIN_MEM
+ hex "MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000"
+ depends on SOUND_MSNDPIN=y
+ default "D0000"
+ help
+ Memory-mapped I/O base address for the primary synthesizer on
+ MultiSound Pinnacle and Fiji sound cards.
+
+config MSNDPIN_IO
+ hex "MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
+ depends on SOUND_MSNDPIN=y
+ default "290"
+ help
+ Memory-mapped I/O base address for the primary synthesizer on
+ MultiSound Pinnacle and Fiji sound cards.
+
+config MSNDPIN_DIGITAL
+ bool "MSND Pinnacle has S/PDIF I/O"
+ depends on SOUND_MSNDPIN=y
+ help
+ If you have the S/PDIF daughter board for the Pinnacle or Fiji,
+ answer Y here; otherwise, say N. If you have this, you will be able
+ to play and record from the S/PDIF port (digital signal). See
+ <file:Documentation/sound/oss/MultiSound> for information on how to make
+ use of this capability.
+
+config MSNDPIN_NONPNP
+ bool "MSND Pinnacle non-PnP Mode"
+ depends on SOUND_MSNDPIN=y
+ help
+ The Pinnacle and Fiji card resources can be configured either with
+ PnP, or through a configuration port. Say Y here if your card is NOT
+ in PnP mode. For the Pinnacle, configuration in non-PnP mode allows
+ use of the IDE and joystick peripherals on the card as well; these
+ do not show up when the card is in PnP mode. Specifying zero for any
+ resource of a device will disable the device. If you are running the
+ card in PnP mode, you must say N here and use isapnptools to
+ configure the card's resources.
+
+comment "MSND Pinnacle DSP section will be configured to above parameters."
+ depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+
+config MSNDPIN_CFG
+ hex "MSND Pinnacle config port 250,260,270"
+ depends on MSNDPIN_NONPNP
+ default "250"
+ help
+ This is the port which the Pinnacle and Fiji uses to configure the
+ card's resources when not in PnP mode. If your card is in PnP mode,
+ then be sure to say N to the previous option, "MSND Pinnacle Non-PnP
+ Mode".
+
+comment "Pinnacle-specific Device Configuration (0 disables)"
+ depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+
+config MSNDPIN_MPU_IO
+ hex "MSND Pinnacle MPU I/O (e.g. 330)"
+ depends on MSNDPIN_NONPNP
+ default "0"
+ help
+ Memory-mapped I/O base address for the Kurzweil daughterboard
+ synthesizer on MultiSound Pinnacle and Fiji sound cards.
+
+config MSNDPIN_MPU_IRQ
+ int "MSND Pinnacle MPU IRQ (e.g. 9)"
+ depends on MSNDPIN_NONPNP
+ default "0"
+ help
+ Interrupt request number for the Kurzweil daughterboard
+ synthesizer on MultiSound Pinnacle and Fiji sound cards.
+
+config MSNDPIN_IDE_IO0
+ hex "MSND Pinnacle IDE I/O 0 (e.g. 170)"
+ depends on MSNDPIN_NONPNP
+ default "0"
+ help
+ CD-ROM drive 0 memory-mapped I/O base address for the MultiSound
+ Pinnacle and Fiji sound cards.
+
+config MSNDPIN_IDE_IO1
+ hex "MSND Pinnacle IDE I/O 1 (e.g. 376)"
+ depends on MSNDPIN_NONPNP
+ default "0"
+ help
+ CD-ROM drive 1 memory-mapped I/O base address for the MultiSound
+ Pinnacle and Fiji sound cards.
+
+config MSNDPIN_IDE_IRQ
+ int "MSND Pinnacle IDE IRQ (e.g. 15)"
+ depends on MSNDPIN_NONPNP
+ default "0"
+ help
+ Interrupt request number for the IDE CD-ROM interface on the
+ MultiSound Pinnacle and Fiji sound cards.
+
+config MSNDPIN_JOYSTICK_IO
+ hex "MSND Pinnacle joystick I/O (e.g. 200)"
+ depends on MSNDPIN_NONPNP
+ default "0"
+ help
+ Memory-mapped I/O base address for the joystick port on MultiSound
+ Pinnacle and Fiji sound cards.
+
+config MSND_FIFOSIZE
+ int "MSND buffer size (kB)"
+ depends on SOUND_PRIME && (SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y)
+ default "128"
+ help
+ Configures the size of each audio buffer, in kilobytes, for
+ recording and playing in the MultiSound drivers (both the Classic
+ and Pinnacle). Larger values reduce the chance of data overruns at
+ the expense of overall latency. If unsure, use the default.
+
+config SOUND_VIA82CXXX
+ tristate "VIA 82C686 Audio Codec"
+ depends on SOUND_PRIME!=n && PCI
+ help
+ Say Y here to include support for the audio codec found on VIA
+ 82Cxxx-based chips. Typically these are built into a motherboard.
+
+ DO NOT select Sound Blaster or Adlib with this driver, unless
+ you have a Sound Blaster or Adlib card in addition to your VIA
+ audio chip.
+
+config MIDI_VIA82CXXX
+ bool "VIA 82C686 MIDI"
+ depends on SOUND_VIA82CXXX
+ help
+ Answer Y to use the MIDI interface of the Via686. You may need to
+ enable this in the BIOS before it will work. This is for connection
+ to external MIDI hardware, and is not required for software playback
+ of MIDI files.
+
+config SOUND_OSS
+ tristate "OSS sound modules"
+ depends on SOUND_PRIME!=n && SOUND
+ help
+ OSS is the Open Sound System suite of sound card drivers. They make
+ sound programming easier since they provide a common API. Say Y or
+ M here (the module will be called sound) if you haven't found a
+ driver for your sound card above, then pick your driver from the
+ list below.
+
+config SOUND_TRACEINIT
+ bool "Verbose initialisation"
+ depends on SOUND_OSS
+ help
+ Verbose soundcard initialization -- affects the format of autoprobe
+ and initialization messages at boot time.
+
+config SOUND_DMAP
+ bool "Persistent DMA buffers"
+ depends on SOUND_OSS
+ ---help---
+ Linux can often have problems allocating DMA buffers for ISA sound
+ cards on machines with more than 16MB of RAM. This is because ISA
+ DMA buffers must exist below the 16MB boundary and it is quite
+ possible that a large enough free block in this region cannot be
+ found after the machine has been running for a while. If you say Y
+ here the DMA buffers (64Kb) will be allocated at boot time and kept
+ until the shutdown. This option is only useful if you said Y to
+ "OSS sound modules", above. If you said M to "OSS sound modules"
+ then you can get the persistent DMA buffer functionality by passing
+ the command-line argument "dmabuf=1" to the sound module.
+
+ Say Y unless you have 16MB or more RAM or a PCI sound card.
+
+config SOUND_AD1816
+ tristate "AD1816(A) based cards (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && SOUND_OSS
+ help
+ Say M here if you have a sound card based on the Analog Devices
+ AD1816(A) chip.
+
+ If you compile the driver into the kernel, you have to add
+ "ad1816=<io>,<irq>,<dma>,<dma2>" to the kernel command line.
+
+config SOUND_AD1889
+ tristate "AD1889 based cards (AD1819 codec) (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && SOUND_OSS
+ help
+ Say M here if you have a sound card based on the Analog Devices
+ AD1889 chip.
+
+config SOUND_SGALAXY
+ tristate "Aztech Sound Galaxy (non-PnP) cards"
+ depends on SOUND_OSS
+ help
+ This module initializes the older non Plug and Play sound galaxy
+ cards from Aztech. It supports the Waverider Pro 32 - 3D and the
+ Galaxy Washington 16.
+
+ If you compile the driver into the kernel, you have to add
+ "sgalaxy=<io>,<irq>,<dma>,<dma2>,<sgbase>" to the kernel command
+ line.
+
+config SOUND_ADLIB
+ tristate "Adlib Cards"
+ depends on SOUND_OSS
+ help
+ Includes ASB 64 4D. Information on programming AdLib cards is
+ available at <http://www.itsnet.com/home/ldragon/Specs/adlib.html>.
+
+config SOUND_ACI_MIXER
+ tristate "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20)"
+ depends on SOUND_OSS
+ ---help---
+ ACI (Audio Command Interface) is a protocol used to communicate with
+ the microcontroller on some sound cards produced by miro and
+ Cardinal Technologies. The main function of the ACI is to control
+ the mixer and to get a product identification.
+
+ This VoxWare ACI driver currently supports the ACI functions on the
+ miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI
+ also controls the radio tuner. This is supported in the video4linux
+ miropcm20 driver (say M or Y here and go back to "Multimedia
+ devices" -> "Radio Adapters").
+
+ This driver is also available as a module and will be called aci.
+
+config SOUND_CS4232
+ tristate "Crystal CS4232 based (PnP) cards"
+ depends on SOUND_OSS
+ help
+ Say Y here if you have a card based on the Crystal CS4232 chip set,
+ which uses its own Plug and Play protocol.
+
+ If you compile the driver into the kernel, you have to add
+ "cs4232=<io>,<irq>,<dma>,<dma2>,<mpuio>,<mpuirq>" to the kernel
+ command line.
+
+ See <file:Documentation/sound/oss/CS4232> for more information on
+ configuring this card.
+
+config SOUND_SSCAPE
+ tristate "Ensoniq SoundScape support"
+ depends on SOUND_OSS
+ help
+ Answer Y if you have a sound card based on the Ensoniq SoundScape
+ chipset. Such cards are being manufactured at least by Ensoniq, Spea
+ and Reveal (Reveal makes also other cards).
+
+ If you compile the driver into the kernel, you have to add
+ "sscape=<io>,<irq>,<dma>,<mpuio>,<mpuirq>" to the kernel command
+ line.
+
+config SOUND_GUS
+ tristate "Gravis Ultrasound support"
+ depends on SOUND_OSS
+ help
+ Say Y here for any type of Gravis Ultrasound card, including the GUS
+ or GUS MAX. See also <file:Documentation/sound/oss/ultrasound> for more
+ information on configuring this card with modules.
+
+ If you compile the driver into the kernel, you have to add
+ "gus=<io>,<irq>,<dma>,<dma2>" to the kernel command line.
+
+config SOUND_GUS16
+ bool "16 bit sampling option of GUS (_NOT_ GUS MAX)"
+ depends on SOUND_GUS
+ help
+ Support for Gravis Ulstrasound (GUS) cards (other than the GUS),
+ sampling at 16-bit width.
+
+config SOUND_GUSMAX
+ bool "GUS MAX support"
+ depends on SOUND_GUS
+ help
+ Support for Gravis Ulstrasound MAX.
+
+config SOUND_VMIDI
+ tristate "Loopback MIDI device support"
+ depends on SOUND_OSS
+ help
+ Support for MIDI loopback on port 1 or 2.
+
+config SOUND_TRIX
+ tristate "MediaTrix AudioTrix Pro support"
+ depends on SOUND_OSS
+ help
+ Answer Y if you have the AudioTriX Pro sound card manufactured
+ by MediaTrix.
+
+config TRIX_HAVE_BOOT
+ bool "Have TRXPRO.HEX firmware file"
+ depends on SOUND_TRIX=y && !STANDALONE
+ help
+ The MediaTrix AudioTrix Pro has an on-board microcontroller which
+ needs to be initialized by downloading the code from the file
+ TRXPRO.HEX in the DOS driver directory. If you don't have the
+ TRXPRO.HEX file handy you may skip this step. However, the SB and
+ MPU-401 modes of AudioTrix Pro will not work without this file!
+
+config TRIX_BOOT_FILE
+ string "Full pathname of TRXPRO.HEX firmware file"
+ depends on TRIX_HAVE_BOOT
+ default "/etc/sound/trxpro.hex"
+ help
+ Enter the full pathname of your TRXPRO.HEX file, starting from /.
+
+config SOUND_MSS
+ tristate "Microsoft Sound System support"
+ depends on SOUND_OSS
+ ---help---
+ Again think carefully before answering Y to this question. It's
+ safe to answer Y if you have the original Windows Sound System card
+ made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may
+ say Y in case your card is NOT among these:
+
+ ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16,
+ Ensoniq SoundScape (and compatibles made by Reveal and Spea),
+ Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max,
+ Gravis Ultrasound with 16 bit option, Logitech Sound Man 16,
+ Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi
+ 82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft
+ Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid
+ SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro
+ Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface,
+ Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound
+ Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M
+ notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM
+ synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface.
+
+ For cards having native support in VoxWare, consult the card
+ specific instructions in <file:Documentation/sound/oss/README.OSS>.
+ Some drivers have their own MSS support and saying Y to this option
+ will cause a conflict.
+
+ If you compile the driver into the kernel, you have to add
+ "ad1848=<io>,<irq>,<dma>,<dma2>[,<type>]" to the kernel command
+ line.
+
+config SOUND_MPU401
+ tristate "MPU-401 support (NOT for SB16)"
+ depends on SOUND_OSS
+ ---help---
+ Be careful with this question. The MPU401 interface is supported by
+ all sound cards. However, some natively supported cards have their
+ own driver for MPU401. Enabling this MPU401 option with these cards
+ will cause a conflict. Also, enabling MPU401 on a system that
+ doesn't really have a MPU401 could cause some trouble. If your card
+ was in the list of supported cards, look at the card specific
+ instructions in the <file:Documentation/sound/oss/README.OSS> file. It
+ is safe to answer Y if you have a true MPU401 MIDI interface card.
+
+ If you compile the driver into the kernel, you have to add
+ "mpu401=<io>,<irq>" to the kernel command line.
+
+config SOUND_NM256
+ tristate "NM256AV/NM256ZX audio support"
+ depends on SOUND_OSS
+ help
+ Say M here to include audio support for the NeoMagic 256AV/256ZX
+ chipsets. These are the audio chipsets found in the Sony
+ Z505S/SX/DX, some Sony F-series, and the Dell Latitude CPi and CPt
+ laptops. It includes support for an AC97-compatible mixer and an
+ apparently proprietary sound engine.
+
+ See <file:Documentation/sound/oss/NM256> for further information.
+
+config SOUND_MAD16
+ tristate "OPTi MAD16 and/or Mozart based cards"
+ depends on SOUND_OSS && SOUND_GAMEPORT
+ ---help---
+ Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi
+ 82C928 or 82C929 or 82C931) audio interface chip. These chips are
+ quite common so it's possible that many no-name cards have one of
+ them. In addition the MAD16 chip is used in some cards made by known
+ manufacturers such as Turtle Beach (Tropez), Reveal (some models)
+ and Diamond (latest ones). Note however that the Tropez sound cards
+ have their own driver; if you have one of those, say N here and Y or
+ M to "Full support for Turtle Beach WaveFront", below.
+
+ If you compile the driver into the kernel, you have to add
+ "mad16=<io>,<irq>,<dma>,<dma2>,<mpuio>,<mpuirq>" to the
+ kernel command line.
+
+ See also <file:Documentation/sound/oss/Opti> and
+ <file:Documentation/sound/oss/MAD16> for more information on setting
+ these cards up as modules.
+
+config MAD16_OLDCARD
+ bool "Support MIDI in older MAD16 based cards (requires SB)"
+ depends on SOUND_MAD16
+ help
+ Answer Y (or M) if you have an older card based on the C928 or
+ Mozart chipset and you want to have MIDI support. If you enable this
+ option you also need to enable support for Sound Blaster.
+
+config SOUND_PAS
+ tristate "ProAudioSpectrum 16 support"
+ depends on SOUND_OSS
+ ---help---
+ Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
+ 16 or Logitech SoundMan 16 sound card. Answer N if you have some
+ other card made by Media Vision or Logitech since those are not
+ PAS16 compatible. Please read <file:Documentation/sound/oss/PAS16>.
+ It is not necessary to add Sound Blaster support separately; it
+ is included in PAS support.
+
+ If you compile the driver into the kernel, you have to add
+ "pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2>
+ to the kernel command line.
+
+config PAS_JOYSTICK
+ bool "Enable PAS16 joystick port"
+ depends on SOUND_PAS=y
+ help
+ Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick
+ port.
+
+config SOUND_PSS
+ tristate "PSS (AD1848, ADSP-2115, ESC614) support"
+ depends on SOUND_OSS
+ help
+ Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
+ ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
+ ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on
+ how to compile it into the kernel or as a module see the file
+ <file:Documentation/sound/oss/PSS>.
+
+ If you compile the driver into the kernel, you have to add
+ "pss=<io>,<mssio>,<mssirq>,<mssdma>,<mpuio>,<mpuirq>" to the kernel
+ command line.
+
+config PSS_MIXER
+ bool "Enable PSS mixer (Beethoven ADSP-16 and other compatibile)"
+ depends on SOUND_PSS
+ help
+ Answer Y for Beethoven ADSP-16. You may try to say Y also for other
+ cards if they have master volume, bass, treble, and you can't
+ control it under Linux. If you answer N for Beethoven ADSP-16, you
+ can't control master volume, bass, treble and synth volume.
+
+ If you said M to "PSS support" above, you may enable or disable this
+ PSS mixer with the module parameter pss_mixer. For more information
+ see the file <file:Documentation/sound/oss/PSS>.
+
+config PSS_HAVE_BOOT
+ bool "Have DSPxxx.LD firmware file"
+ depends on SOUND_PSS && !STANDALONE
+ help
+ If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y
+ to include this file. Without this file the synth device (OPL) may
+ not work.
+
+config PSS_BOOT_FILE
+ string "Full pathname of DSPxxx.LD firmware file"
+ depends on PSS_HAVE_BOOT
+ default "/etc/sound/dsp001.ld"
+ help
+ Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file,
+ starting from /.
+
+config SOUND_SB
+ tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
+ depends on SOUND_OSS
+ ---help---
+ Answer Y if you have an original Sound Blaster card made by Creative
+ Labs or a 100% hardware compatible clone (like the Thunderboard or
+ SM Games). For an unknown card you may answer Y if the card claims
+ to be Sound Blaster-compatible.
+
+ Please read the file <file:Documentation/sound/oss/Soundblaster>.
+
+ You should also say Y here for cards based on the Avance Logic
+ ALS-007 and ALS-1X0 chips (read <file:Documentation/sound/oss/ALS>) and
+ for cards based on ESS chips (read
+ <file:Documentation/sound/oss/ESS1868> and
+ <file:Documentation/sound/oss/ESS>). If you have an SB AWE 32 or SB AWE
+ 64, say Y here and also to "AWE32 synth" below and read
+ <file:Documentation/sound/oss/INSTALL.awe>. If you have an IBM Mwave
+ card, say Y here and read <file:Documentation/sound/oss/mwave>.
+
+ If you compile the driver into the kernel and don't want to use
+ isapnp, you have to add "sb=<io>,<irq>,<dma>,<dma2>" to the kernel
+ command line.
+
+ You can say M here to compile this driver as a module; the module is
+ called sb.
+
+config SOUND_AWE32_SYNTH
+ tristate "AWE32 synth"
+ depends on SOUND_OSS
+ help
+ Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or
+ similar sound card. See <file:Documentation/sound/oss/README.awe>,
+ <file:Documentation/sound/oss/AWE32> and the Soundblaster-AWE
+ mini-HOWTO, available from <http://www.tldp.org/docs.html#howto>
+ for more info.
+
+config SOUND_WAVEFRONT
+ tristate "Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards"
+ depends on SOUND_OSS && m
+ help
+ Answer Y or M if you have a Tropez Plus, Tropez or Maui sound card
+ and read the files <file:Documentation/sound/oss/Wavefront> and
+ <file:Documentation/sound/oss/Tropez+>.
+
+config SOUND_MAUI
+ tristate "Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers"
+ depends on SOUND_OSS
+ help
+ Say Y here if you have a Turtle Beach Wave Front, Maui, or Tropez
+ sound card.
+
+ If you compile the driver into the kernel, you have to add
+ "maui=<io>,<irq>" to the kernel command line.
+
+config MAUI_HAVE_BOOT
+ bool "Have OSWF.MOT firmware file"
+ depends on SOUND_MAUI=y && !STANDALONE
+ help
+ Turtle Beach Maui and Tropez sound cards have a microcontroller
+ which needs to be initialized prior to use. OSWF.MOT is a file
+ distributed with the card's DOS/Windows drivers. Answer Y if you
+ have this file.
+
+config MAUI_BOOT_FILE
+ string "Full pathname of OSWF.MOT firmware file"
+ depends on MAUI_HAVE_BOOT
+ default "/etc/sound/oswf.mot"
+ help
+ Enter the full pathname of your OSWF.MOT file, starting from /.
+
+config SOUND_YM3812
+ tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
+ depends on SOUND_OSS
+ ---help---
+ Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
+ Answering Y is usually a safe and recommended choice, however some
+ cards may have software (TSR) FM emulation. Enabling FM support with
+ these cards may cause trouble (I don't currently know of any such
+ cards, however). Please read the file
+ <file:Documentation/sound/oss/OPL3> if your card has an OPL3 chip.
+
+ If you compile the driver into the kernel, you have to add
+ "opl3=<io>" to the kernel command line.
+
+ If unsure, say Y.
+
+config SOUND_OPL3SA1
+ tristate "Yamaha OPL3-SA1 audio controller"
+ depends on SOUND_OSS
+ help
+ Say Y or M if you have a Yamaha OPL3-SA1 sound chip, which is
+ usually built into motherboards. Read
+ <file:Documentation/sound/oss/OPL3-SA> for details.
+
+ If you compile the driver into the kernel, you have to add
+ "opl3sa=<io>,<irq>,<dma>,<dma2>,<mpuio>,<mpuirq>" to the kernel
+ command line.
+
+config SOUND_OPL3SA2
+ tristate "Yamaha OPL3-SA2 and SA3 based PnP cards"
+ depends on SOUND_OSS
+ help
+ Say Y or M if you have a card based on one of these Yamaha sound
+ chipsets or the "SAx", which is actually a SA3. Read
+ <file:Documentation/sound/oss/OPL3-SA2> for more information on
+ configuring these cards.
+
+ If you compile the driver into the kernel and do not also
+ configure in the optional ISA PnP support, you will have to add
+ "opl3sa2=<io>,<irq>,<dma>,<dma2>,<mssio>,<mpuio>" to the kernel
+ command line.
+
+config SOUND_YMFPCI
+ tristate "Yamaha YMF7xx PCI audio (native mode)"
+ depends on SOUND_OSS && PCI
+ help
+ Support for Yamaha cards including the YMF711, YMF715, YMF718,
+ YMF719, YMF724, Waveforce 192XG, and Waveforce 192 Digital.
+
+config SOUND_YMFPCI_LEGACY
+ bool "Yamaha PCI legacy ports support"
+ depends on SOUND_YMFPCI
+ help
+ Support for YMF7xx PCI cards emulating an MP401.
+
+config SOUND_UART6850
+ tristate "6850 UART support"
+ depends on SOUND_OSS
+ help
+ This option enables support for MIDI interfaces based on the 6850
+ UART chip. This interface is rarely found on sound cards. It's safe
+ to answer N to this question.
+
+ If you compile the driver into the kernel, you have to add
+ "uart6850=<io>,<irq>" to the kernel command line.
+
+config SOUND_AEDSP16
+ tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)"
+ depends on SOUND_OSS
+ ---help---
+ Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
+ driver supports Audio Excel DSP 16 but not the III nor PnP versions
+ of this card.
+
+ The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or
+ a Microsoft Sound System card, so you should have said Y to either
+ "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
+ or "Microsoft Sound System support", above, and you need to answer
+ the "MSS emulation" and "SBPro emulation" questions below
+ accordingly. You should say Y to one and only one of these two
+ questions.
+
+ Read the <file:Documentation/sound/oss/README.OSS> file and the head of
+ <file:drivers/sound/aedsp16.c> as well as
+ <file:Documentation/sound/oss/AudioExcelDSP16> to get more information
+ about this driver and its configuration.
+
+config SC6600
+ bool "SC-6600 based audio cards (new Audio Excel DSP 16)"
+ depends on SOUND_AEDSP16
+ help
+ The SC6600 is the new version of DSP mounted on the Audio Excel DSP
+ 16 cards. Find in the manual the FCC ID of your audio card and
+ answer Y if you have an SC6600 DSP.
+
+config SC6600_JOY
+ bool "Activate SC-6600 Joystick Interface"
+ depends on SC6600
+ help
+ Say Y here in order to use the joystick interface of the Audio Excel
+ DSP 16 card.
+
+config SC6600_CDROM
+ int "SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)"
+ depends on SC6600
+ default "4"
+ help
+ This is used to activate the CD-ROM interface of the Audio Excel
+ DSP 16 card. Enter: 0 for Sony, 1 for Panasonic, 2 for IDE, 4 for no
+ CD-ROM present.
+
+config SC6600_CDROMBASE
+ hex "SC-6600 CDROM Interface I/O Address"
+ depends on SC6600
+ default "0"
+ help
+ Base I/O port address for the CD-ROM interface of the Audio Excel
+ DSP 16 card.
+
+choice
+ prompt "Audio Excel DSP 16"
+ optional
+ depends on SOUND_AEDSP16
+
+config AEDSP16_MSS
+ bool "MSS emulation"
+ depends on SOUND_MSS
+ help
+ Answer Y if you want your audio card to emulate Microsoft Sound
+ System. You should then say Y to "Microsoft Sound System support"
+ and say N to "Audio Excel DSP 16 (SBPro emulation)".
+
+config AEDSP16_SBPRO
+ bool "SBPro emulation"
+ depends on SOUND_SB
+ help
+ Answer Y if you want your audio card to emulate Sound Blaster Pro.
+ You should then say Y to "100% Sound Blaster compatibles
+ (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS
+ emulation)".
+
+ If you compile the driver into the kernel, you have to add
+ "aedsp16=<io>,<irq>,<dma>,<mssio>,<mpuio>,<mouirq>" to the kernel
+ command line.
+
+endchoice
+
+config AEDSP16_MPU401
+ bool "Audio Excel DSP 16 (MPU401 emulation)"
+ depends on SOUND_AEDSP16 && SOUND_MPU401
+ help
+ Answer Y if you want your audio card to emulate the MPU-401 midi
+ interface. You should then also say Y to "MPU-401 support".
+
+ Note that the I/O base for MPU-401 support of aedsp16 is the same
+ you have selected for "MPU-401 support". If you are using this
+ driver as a module you have to specify the MPU I/O base address with
+ the parameter 'mpu_base=0xNNN'.
+
+config SOUND_VIDC
+ tristate "VIDC 16-bit sound"
+ depends on ARM && (ARCH_ACORN || ARCH_CLPS7500) && SOUND_OSS
+ help
+ 16-bit support for the VIDC onboard sound hardware found on Acorn
+ machines.
+
+config SOUND_WAVEARTIST
+ tristate "Netwinder WaveArtist"
+ depends on ARM && SOUND_OSS && ARCH_NETWINDER
+ help
+ Say Y here to include support for the Rockwell WaveArtist sound
+ system. This driver is mainly for the NetWinder.
+
+config SOUND_TVMIXER
+ tristate "TV card (bt848) mixer support"
+ depends on SOUND_PRIME!=n && SOUND && I2C
+ help
+ Support for audio mixer facilities on the BT848 TV frame-grabber
+ card.
+
+config SOUND_KAHLUA
+ tristate "XpressAudio Sound Blaster emulation"
+ depends on SOUND_SB
+
+config SOUND_ALI5455
+ tristate "ALi5455 audio support"
+ depends on SOUND_PRIME!=n && PCI
+
+config SOUND_FORTE
+ tristate "ForteMedia FM801 driver"
+ depends on SOUND_PRIME!=n && PCI
+ help
+ Say Y or M if you want driver support for the ForteMedia FM801 PCI
+ audio controller (Abit AU10, Genius Sound Maker, HP Workstation
+ zx2000, and others).
+
+config SOUND_RME96XX
+ tristate "RME Hammerfall (RME96XX) support"
+ depends on SOUND_PRIME!=n && PCI
+ help
+ Say Y or M if you have a Hammerfall or Hammerfall light
+ multichannel card from RME. If you want to access advanced
+ features of the card, read <file:Documentation/sound/oss/rme96xx>.
+
+config SOUND_AD1980
+ tristate "AD1980 front/back switch plugin"
+ depends on SOUND_PRIME!=n
+
+config SOUND_SH_DAC_AUDIO
+ tristate "SuperH DAC audio support"
+ depends on SOUND_PRIME!=n && SOUND && CPU_SH3
+
+config SOUND_SH_DAC_AUDIO_CHANNEL
+ int " DAC channel"
+ default "1"
+ depends on SOUND_SH_DAC_AUDIO
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
new file mode 100644
index 000000000000..db9afb61d6ff
--- /dev/null
+++ b/sound/oss/Makefile
@@ -0,0 +1,187 @@
+# Makefile for the Linux sound card driver
+#
+# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+# Rewritten to use lists instead of if-statements.
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_SOUND_OSS) += sound.o
+obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o
+
+# Please leave it as is, cause the link order is significant !
+
+obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
+obj-$(CONFIG_SOUND_HAL2) += hal2.o
+obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
+obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o
+obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o
+obj-$(CONFIG_SOUND_MSS) += ad1848.o
+obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o
+obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o
+obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o
+obj-$(CONFIG_SOUND_MPU401) += mpu401.o
+obj-$(CONFIG_SOUND_UART6850) += uart6850.o
+obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o
+obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o
+obj-$(CONFIG_SOUND_YM3812) += opl3.o
+obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
+obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
+obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
+obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o
+obj-$(CONFIG_SOUND_AD1816) += ad1816.o
+obj-$(CONFIG_SOUND_AD1889) += ad1889.o ac97_codec.o
+obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o
+obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o
+
+obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o
+ifeq ($(CONFIG_MIDI_VIA82CXXX),y)
+ obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o
+endif
+obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o
+ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y)
+ obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o
+endif
+obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
+obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
+obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
+obj-$(CONFIG_SOUND_NM256) += nm256_audio.o ac97.o
+obj-$(CONFIG_SOUND_ICH) += i810_audio.o ac97_codec.o
+obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o
+obj-$(CONFIG_SOUND_CMPCI) += cmpci.o
+ifeq ($(CONFIG_SOUND_CMPCI_FM),y)
+ obj-$(CONFIG_SOUND_CMPCI) += sound.o opl3.o
+endif
+ifeq ($(CONFIG_SOUND_CMPCI_MIDI),y)
+ obj-$(CONFIG_SOUND_CMPCI) += sound.o mpu401.o
+endif
+obj-$(CONFIG_SOUND_ES1370) += es1370.o
+obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o
+obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o
+obj-$(CONFIG_SOUND_AU1000) += au1000.o ac97_codec.o
+obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
+obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o
+obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o
+obj-$(CONFIG_SOUND_MAESTRO) += maestro.o
+obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o
+obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o
+obj-$(CONFIG_SOUND_HARMONY) += harmony.o
+obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o
+obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
+obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o
+obj-$(CONFIG_SOUND_BT878) += btaudio.o
+obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o
+obj-$(CONFIG_SOUND_IT8172) += ite8172.o ac97_codec.o
+obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o
+
+obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o
+obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o
+
+ifeq ($(CONFIG_MIDI_EMU10K1),y)
+ obj-$(CONFIG_SOUND_EMU10K1) += sound.o
+endif
+
+obj-$(CONFIG_SOUND_EMU10K1) += emu10k1/
+obj-$(CONFIG_SOUND_CS4281) += cs4281/
+obj-$(CONFIG_DMASOUND) += dmasound/
+
+# Declare multi-part drivers.
+
+sound-objs := \
+ dev_table.o soundcard.o sound_syms.o \
+ audio.o audio_syms.o dmabuf.o \
+ midi_syms.o midi_synth.o midibuf.o \
+ sequencer.o sequencer_syms.o sound_timer.o sys_timer.o
+
+gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
+pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
+sb-objs := sb_card.o
+sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o
+vidc_mod-objs := vidc.o vidc_fill.o
+wavefront-objs := wavfront.o wf_midi.o yss225.o
+
+hostprogs-y := bin2hex hex2hex
+
+# Files generated that shall be removed upon make clean
+clean-files := maui_boot.h msndperm.c msndinit.c pndsperm.c pndspini.c \
+ pss_boot.h trix_boot.h
+
+# Firmware files that need translation
+#
+# The translated files are protected by a file that keeps track
+# of what name was used to build them. If the name changes, they
+# will be forced to be remade.
+#
+
+# Turtle Beach Maui / Tropez
+
+$(obj)/maui.o: $(obj)/maui_boot.h
+
+ifeq ($(CONFIG_MAUI_HAVE_BOOT),y)
+ $(obj)/maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) $(obj)/bin2hex
+ $(obj)/bin2hex -i maui_os < $< > $@
+else
+ $(obj)/maui_boot.h:
+ ( \
+ echo 'static unsigned char * maui_os = NULL;'; \
+ echo 'static int maui_osLen = 0;'; \
+ ) > $@
+endif
+
+# Turtle Beach MultiSound
+
+ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y)
+ $(obj)/msnd_classic.o: $(obj)/msndperm.c $(obj)/msndinit.c
+
+ $(obj)/msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) $(obj)/bin2hex
+ $(obj)/bin2hex msndperm < $< > $@
+
+ $(obj)/msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) $(obj)/bin2hex
+ $(obj)/bin2hex msndinit < $< > $@
+endif
+
+ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y)
+ $(obj)/msnd_pinnacle.o: $(obj)/pndsperm.c $(obj)/pndspini.c
+
+ $(obj)/pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) $(obj)/bin2hex
+ $(obj)/bin2hex pndsperm < $< > $@
+
+ $(obj)/pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) $(obj)/bin2hex
+ $(obj)/bin2hex pndspini < $< > $@
+endif
+
+# PSS (ECHO-ADI2111)
+
+$(obj)/pss.o: $(obj)/pss_boot.h
+
+ifeq ($(CONFIG_PSS_HAVE_BOOT),y)
+ $(obj)/pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) $(obj)/bin2hex
+ $(obj)/bin2hex pss_synth < $< > $@
+else
+ $(obj)/pss_boot.h:
+ ( \
+ echo 'static unsigned char * pss_synth = NULL;'; \
+ echo 'static int pss_synthLen = 0;'; \
+ ) > $@
+endif
+
+# MediaTrix AudioTrix Pro
+
+$(obj)/trix.o: $(obj)/trix_boot.h
+
+ifeq ($(CONFIG_TRIX_HAVE_BOOT),y)
+ $(obj)/trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) $(obj)/hex2hex
+ $(obj)/hex2hex -i trix_boot < $< > $@
+else
+ $(obj)/trix_boot.h:
+ ( \
+ echo 'static unsigned char * trix_boot = NULL;'; \
+ echo 'static int trix_boot_len = 0;'; \
+ ) > $@
+endif
diff --git a/sound/oss/README.FIRST b/sound/oss/README.FIRST
new file mode 100644
index 000000000000..90fdcf063d2d
--- /dev/null
+++ b/sound/oss/README.FIRST
@@ -0,0 +1,6 @@
+The modular sound driver patches were funded by Red Hat Software
+(www.redhat.com). The sound driver here is thus a modified version of
+Hannu's code. Please bear that in mind when considering the appropriate
+forums for bug reporting.
+
+Alan Cox
diff --git a/sound/oss/ac97.c b/sound/oss/ac97.c
new file mode 100644
index 000000000000..3ba6d91e891d
--- /dev/null
+++ b/sound/oss/ac97.c
@@ -0,0 +1,452 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include "ac97.h"
+
+/* Flag for mono controls. */
+#define MO 0
+/* And for stereo. */
+#define ST 1
+
+/* Whether or not the bits in the channel are inverted. */
+#define INV 1
+#define NINV 0
+
+static struct ac97_chn_desc {
+ int ac97_regnum;
+ int oss_channel;
+ int maxval;
+ int is_stereo;
+ int oss_mask;
+ int recordNum;
+ u16 regmask;
+ int is_inverted;
+} mixerRegs[] = {
+ { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV },
+ { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV },
+ { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV },
+ { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV },
+ { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV },
+ { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV },
+ { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV },
+ { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV },
+ { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV },
+ { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV },
+ { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV },
+ { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV },
+ { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV },
+ { -1, -1, 0xff, 0, 0, -1, 0x0000, 0 },
+};
+
+static struct ac97_chn_desc *
+ac97_find_chndesc (struct ac97_hwint *dev, int oss_channel)
+{
+ int x;
+
+ for (x = 0; mixerRegs[x].oss_channel != -1; x++) {
+ if (mixerRegs[x].oss_channel == oss_channel)
+ return mixerRegs + x;
+ }
+
+ return NULL;
+}
+
+static inline int
+ac97_is_valid_channel (struct ac97_hwint *dev, struct ac97_chn_desc *chn)
+{
+ return (dev->last_written_mixer_values[chn->ac97_regnum / 2]
+ != AC97_REG_UNSUPPORTED);
+}
+
+int
+ac97_init (struct ac97_hwint *dev)
+{
+ int x;
+ int reg0;
+
+ /* Clear out the arrays of cached values. */
+ for (x = 0; x < AC97_REG_CNT; x++)
+ dev->last_written_mixer_values[x] = AC97_REGVAL_UNKNOWN;
+
+ for (x = 0; x < SOUND_MIXER_NRDEVICES; x++)
+ dev->last_written_OSS_values[x] = AC97_REGVAL_UNKNOWN;
+
+ /* Clear the device masks. */
+ dev->mixer_devmask = 0;
+ dev->mixer_stereomask = 0;
+ dev->mixer_recmask = 0;
+
+ /* ??? Do a "standard reset" via register 0? */
+
+ /* Hardware-dependent reset. */
+ if (dev->reset_device (dev))
+ return -1;
+
+ /* Check the mixer device capabilities. */
+ reg0 = dev->read_reg (dev, AC97_RESET);
+
+ if (reg0 < 0)
+ return -1;
+
+ /* Check for support for treble/bass controls. */
+ if (! (reg0 & 4)) {
+ dev->last_written_mixer_values[AC97_MASTER_TONE / 2]
+ = AC97_REG_UNSUPPORTED;
+ }
+
+ /* ??? There may be other tests here? */
+
+ /* Fill in the device masks. */
+ for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) {
+ if (ac97_is_valid_channel (dev, mixerRegs + x)) {
+ dev->mixer_devmask |= mixerRegs[x].oss_mask;
+
+ if (mixerRegs[x].is_stereo)
+ dev->mixer_stereomask |= mixerRegs[x].oss_mask;
+
+ if (mixerRegs[x].recordNum != -1)
+ dev->mixer_recmask |= mixerRegs[x].oss_mask;
+ }
+ }
+
+ return 0;
+}
+
+/* Reset the mixer to the currently saved settings. */
+int
+ac97_reset (struct ac97_hwint *dev)
+{
+ int x;
+
+ if (dev->reset_device (dev))
+ return -1;
+
+ /* Now set the registers back to their last-written values. */
+ for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) {
+ int regnum = mixerRegs[x].ac97_regnum;
+ int value = dev->last_written_mixer_values [regnum / 2];
+ if (value >= 0)
+ ac97_put_register (dev, regnum, value);
+ }
+ return 0;
+}
+
+/* Return the contents of register REG; use the cache if the value in it
+ is valid. Returns a negative error code on failure. */
+static int
+ac97_get_register (struct ac97_hwint *dev, u8 reg)
+{
+ if (reg > 127 || (reg & 1))
+ return -EINVAL;
+
+ /* See if it's in the cache, or if it's just plain invalid. */
+ switch (dev->last_written_mixer_values[reg / 2]) {
+ case AC97_REG_UNSUPPORTED:
+ return -EINVAL;
+ break;
+ case AC97_REGVAL_UNKNOWN:
+ dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg);
+ break;
+ default:
+ break;
+ }
+ return dev->last_written_mixer_values[reg / 2];
+}
+
+/* Write VALUE to AC97 register REG, and cache its value in the last-written
+ cache. Returns a negative error code on failure, or 0 on success. */
+int
+ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value)
+{
+ if (reg > 127 || (reg & 1))
+ return -EINVAL;
+
+ if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED)
+ return -EINVAL;
+ else {
+ int res = dev->write_reg (dev, reg, value);
+ if (res >= 0) {
+ dev->last_written_mixer_values[reg / 2] = value;
+ return 0;
+ }
+ else
+ return res;
+ }
+}
+
+/* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100. If
+ IS_STEREO is set, VALUE is a stereo value; the left channel value
+ is in the lower 8 bits, and the right channel value is in the upper
+ 8 bits.
+
+ A negative error code is returned on failure, or the unsigned
+ scaled value on success. */
+
+static int
+ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv)
+{
+ /* Muted? */
+ if (value & AC97_MUTE)
+ return 0;
+
+ if (is_stereo)
+ return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8)
+ | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
+ else {
+ int i;
+
+ /* Inverted. */
+ if (inv)
+ value = maxval - value;
+
+ i = (value * 100 + (maxval / 2)) / maxval;
+ if (i > 100)
+ i = 100;
+ if (i < 0)
+ i = 0;
+ return i;
+ }
+}
+
+static int
+ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv)
+{
+ if (is_stereo)
+ return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8)
+ | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
+ else {
+ int i = ((value & 255) * maxval + 50) / 100;
+ if (inv)
+ i = maxval - i;
+ if (i < 0)
+ i = 0;
+ if (i > maxval)
+ i = maxval;
+ return i;
+ }
+}
+
+static int
+ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value)
+{
+ int scaled_value;
+ struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel);
+ int result;
+
+ if (channel == NULL)
+ return -ENODEV;
+ if (! ac97_is_valid_channel (dev, channel))
+ return -ENODEV;
+ scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval,
+ channel->is_stereo,
+ channel->is_inverted);
+ if (scaled_value < 0)
+ return scaled_value;
+
+ if (channel->regmask != 0) {
+ int mv;
+
+ int oldval = ac97_get_register (dev, channel->ac97_regnum);
+ if (oldval < 0)
+ return oldval;
+
+ for (mv = channel->regmask; ! (mv & 1); mv >>= 1)
+ scaled_value <<= 1;
+
+ scaled_value &= channel->regmask;
+ scaled_value |= (oldval & ~channel->regmask);
+ }
+ result = ac97_put_register (dev, channel->ac97_regnum, scaled_value);
+ if (result == 0)
+ dev->last_written_OSS_values[oss_channel] = oss_value;
+ return result;
+}
+
+static int
+ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel)
+{
+ struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel);
+ int regval;
+
+ if (channel == NULL)
+ return -ENODEV;
+
+ if (! ac97_is_valid_channel (dev, channel))
+ return -ENODEV;
+
+ regval = ac97_get_register (dev, channel->ac97_regnum);
+
+ if (regval < 0)
+ return regval;
+
+ if (channel->regmask != 0) {
+ int mv;
+
+ regval &= channel->regmask;
+
+ for (mv = channel->regmask; ! (mv & 1); mv >>= 1)
+ regval >>= 1;
+ }
+ return ac97_scale_to_oss_val (regval, channel->maxval,
+ channel->is_stereo,
+ channel->is_inverted);
+}
+
+static int
+ac97_get_recmask (struct ac97_hwint *dev)
+{
+ int recReg = ac97_get_register (dev, AC97_RECORD_SELECT);
+
+ if (recReg < 0)
+ return recReg;
+ else {
+ int x;
+ for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) {
+ if (mixerRegs[x].recordNum == (recReg & 7))
+ return mixerRegs[x].oss_mask;
+ }
+ return -ENODEV;
+ }
+}
+
+static int
+ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask)
+{
+ int x;
+
+ if (oss_recmask == 0)
+ oss_recmask = SOUND_MIXER_MIC;
+
+ for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) {
+ if ((mixerRegs[x].recordNum >= 0)
+ && (oss_recmask & mixerRegs[x].oss_mask))
+ break;
+ }
+ if (mixerRegs[x].ac97_regnum < 0)
+ return -ENODEV;
+ else {
+ int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum;
+ int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval);
+ if (res == 0)
+ return ac97_get_recmask (dev);
+ else
+ return res;
+ }
+}
+
+/* Set the mixer DEV to the list of values in VALUE_LIST. Return 0 on
+ success, or a negative error code. */
+int
+ac97_set_values (struct ac97_hwint *dev,
+ struct ac97_mixer_value_list *value_list)
+{
+ int x;
+
+ for (x = 0; value_list[x].oss_channel != -1; x++) {
+ int chnum = value_list[x].oss_channel;
+ struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum);
+ if (chent != NULL) {
+ u16 val;
+ int res;
+
+ if (chent->is_stereo)
+ val = (value_list[x].value.stereo.right << 8)
+ | value_list[x].value.stereo.left;
+ else {
+ /* We do this so the returned value looks OK in the
+ mixer app. It's not necessary otherwise. */
+ val = (value_list[x].value.mono << 8)
+ | value_list[x].value.mono;
+ }
+ res = ac97_set_mixer (dev, chnum, val);
+ if (res < 0)
+ return res;
+ }
+ else
+ return -ENODEV;
+ }
+ return 0;
+}
+
+int
+ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, void __user *arg)
+{
+ int ret;
+
+ switch (cmd) {
+ case SOUND_MIXER_READ_RECSRC:
+ ret = ac97_get_recmask (dev);
+ break;
+
+ case SOUND_MIXER_WRITE_RECSRC:
+ {
+ if (get_user (ret, (int __user *) arg))
+ ret = -EFAULT;
+ else
+ ret = ac97_set_recmask (dev, ret);
+ }
+ break;
+
+ case SOUND_MIXER_READ_CAPS:
+ ret = SOUND_CAP_EXCL_INPUT;
+ break;
+
+ case SOUND_MIXER_READ_DEVMASK:
+ ret = dev->mixer_devmask;
+ break;
+
+ case SOUND_MIXER_READ_RECMASK:
+ ret = dev->mixer_recmask;
+ break;
+
+ case SOUND_MIXER_READ_STEREODEVS:
+ ret = dev->mixer_stereomask;
+ break;
+
+ default:
+ /* Read or write request. */
+ ret = -EINVAL;
+ if (_IOC_TYPE (cmd) == 'M') {
+ int dir = _SIOC_DIR (cmd);
+ int channel = _IOC_NR (cmd);
+
+ if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) {
+ ret = 0;
+ if (dir & _SIOC_WRITE) {
+ int val;
+ if (get_user (val, (int __user *) arg) == 0)
+ ret = ac97_set_mixer (dev, channel, val);
+ else
+ ret = -EFAULT;
+ }
+ if (ret >= 0 && (dir & _SIOC_READ)) {
+ if (dev->last_written_OSS_values[channel]
+ == AC97_REGVAL_UNKNOWN)
+ dev->last_written_OSS_values[channel]
+ = ac97_get_mixer_scaled (dev, channel);
+ ret = dev->last_written_OSS_values[channel];
+ }
+ }
+ }
+ break;
+ }
+
+ if (ret < 0)
+ return ret;
+ else
+ return put_user(ret, (int __user *) arg);
+}
+
+EXPORT_SYMBOL(ac97_init);
+EXPORT_SYMBOL(ac97_set_values);
+EXPORT_SYMBOL(ac97_put_register);
+EXPORT_SYMBOL(ac97_mixer_ioctl);
+EXPORT_SYMBOL(ac97_reset);
+MODULE_LICENSE("GPL");
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/sound/oss/ac97.h b/sound/oss/ac97.h
new file mode 100644
index 000000000000..77d454ea3202
--- /dev/null
+++ b/sound/oss/ac97.h
@@ -0,0 +1,204 @@
+/*
+ * ac97.h
+ *
+ * definitions for the AC97, Intel's Audio Codec 97 Spec
+ * also includes support for a generic AC97 interface
+ */
+
+#ifndef _AC97_H_
+#define _AC97_H_
+#include "sound_config.h"
+#include "sound_calls.h"
+
+#define AC97_RESET 0x0000 //
+#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out
+#define AC97_HEADPHONE_VOL 0x0004 //
+#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output
+#define AC97_MASTER_TONE 0x0008 //
+#define AC97_PCBEEP_VOL 0x000a // none
+#define AC97_PHONE_VOL 0x000c // TAD Input (mono)
+#define AC97_MIC_VOL 0x000e // MIC Input (mono)
+#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo)
+#define AC97_CD_VOL 0x0012 // CD Input (stereo)
+#define AC97_VIDEO_VOL 0x0014 // none
+#define AC97_AUX_VOL 0x0016 // Aux Input (stereo)
+#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo)
+#define AC97_RECORD_SELECT 0x001a //
+#define AC97_RECORD_GAIN 0x001c
+#define AC97_RECORD_GAIN_MIC 0x001e
+#define AC97_GENERAL_PURPOSE 0x0020
+#define AC97_3D_CONTROL 0x0022
+#define AC97_MODEM_RATE 0x0024
+#define AC97_POWER_CONTROL 0x0026
+
+/* registers 0x0028 - 0x0058 are reserved */
+
+/* AC'97 2.0 */
+#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */
+#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */
+#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */
+#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */
+#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */
+#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR DAC Rate */
+#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */
+#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */
+#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */
+#define AC97_RESERVED_3A 0x003A /* Reserved */
+/* range 0x3c-0x58 - MODEM */
+
+/* registers 0x005a - 0x007a are vendor reserved */
+
+#define AC97_VENDOR_ID1 0x007c
+#define AC97_VENDOR_ID2 0x007e
+
+/* volume control bit defines */
+
+#define AC97_MUTE 0x8000
+#define AC97_MICBOOST 0x0040
+#define AC97_LEFTVOL 0x3f00
+#define AC97_RIGHTVOL 0x003f
+
+/* record mux defines */
+
+#define AC97_RECMUX_MIC 0x0000
+#define AC97_RECMUX_CD 0x0101
+#define AC97_RECMUX_VIDEO 0x0202 /* not used */
+#define AC97_RECMUX_AUX 0x0303
+#define AC97_RECMUX_LINE 0x0404
+#define AC97_RECMUX_STEREO_MIX 0x0505
+#define AC97_RECMUX_MONO_MIX 0x0606
+#define AC97_RECMUX_PHONE 0x0707
+
+
+/* general purpose register bit defines */
+
+#define AC97_GP_LPBK 0x0080 /* Loopback mode */
+#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */
+#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */
+#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */
+#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */
+#define AC97_GP_LD 0x1000 /* Loudness 1=on */
+#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */
+#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */
+#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */
+
+
+/* powerdown control and status bit defines */
+
+/* status */
+#define AC97_PWR_MDM 0x0010 /* Modem section ready */
+#define AC97_PWR_REF 0x0008 /* Vref nominal */
+#define AC97_PWR_ANL 0x0004 /* Analog section ready */
+#define AC97_PWR_DAC 0x0002 /* DAC section ready */
+#define AC97_PWR_ADC 0x0001 /* ADC section ready */
+
+/* control */
+#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */
+#define AC97_PWR_PR1 0x0200 /* DAC powerdown */
+#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */
+#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */
+#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */
+#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */
+#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */
+#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */
+
+/* useful power states */
+#define AC97_PWR_D0 0x0000 /* everything on */
+#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4
+#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
+#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
+#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */
+
+/* Total number of defined registers. */
+#define AC97_REG_CNT 64
+
+/* Generic AC97 mixer interface. */
+
+/* Structure describing access to the hardware. */
+struct ac97_hwint
+{
+ /* Perform any hardware-specific reset and initialization. Returns
+ 0 on success, or a negative error code. */
+ int (*reset_device) (struct ac97_hwint *dev);
+
+ /* Returns the contents of the specified register REG. The caller
+ should check to see if the desired contents are available in
+ the cache first, if applicable. Returns a positive unsigned value
+ representing the contents of the register, or a negative error
+ code. */
+ int (*read_reg) (struct ac97_hwint *dev, u8 reg);
+
+ /* Writes VALUE to register REG. Returns 0 on success, or a
+ negative error code. */
+ int (*write_reg) (struct ac97_hwint *dev, u8 reg, u16 value);
+
+ /* Hardware-specific information. */
+ void *driver_private;
+
+ /* Three OSS masks. */
+ int mixer_devmask;
+ int mixer_stereomask;
+ int mixer_recmask;
+
+ /* The mixer cache. The indices correspond to the AC97 hardware register
+ number / 2, since the register numbers are always an even number.
+
+ Unknown values are set to -1; unsupported registers contain a
+ -2. */
+ int last_written_mixer_values[AC97_REG_CNT];
+
+ /* A cache of values written via OSS; we need these so we can return
+ the values originally written by the user.
+
+ Why the original user values? Because the real-world hardware
+ has less precision, and some existing applications assume that
+ they will get back the exact value that they wrote (aumix).
+
+ A -1 value indicates that no value has been written to this mixer
+ channel via OSS. */
+ int last_written_OSS_values[SOUND_MIXER_NRDEVICES];
+};
+
+/* Values stored in the register cache. */
+#define AC97_REGVAL_UNKNOWN -1
+#define AC97_REG_UNSUPPORTED -2
+
+struct ac97_mixer_value_list
+{
+ /* Mixer channel to set. List is terminated by a value of -1. */
+ int oss_channel;
+ /* The scaled value to set it to; values generally range from 0-100. */
+ union {
+ struct {
+ u8 left, right;
+ } stereo;
+ u8 mono;
+ } value;
+};
+
+/* Initialize the ac97 mixer by resetting it. */
+extern int ac97_init (struct ac97_hwint *dev);
+
+/* Sets the mixer DEV to the values in VALUE_LIST. Returns 0 on success,
+ or a negative error code. */
+extern int ac97_set_values (struct ac97_hwint *dev,
+ struct ac97_mixer_value_list *value_list);
+
+/* Writes the specified VALUE to the AC97 register REG in the mixer.
+ Takes care of setting the last-written cache as well. */
+extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value);
+
+/* Default ioctl. */
+extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd,
+ void __user * arg);
+
+/* Do a complete reset on the AC97 mixer, restoring all mixer registers to
+ the current values. Normally used after an APM resume event. */
+extern int ac97_reset (struct ac97_hwint *dev);
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
new file mode 100644
index 000000000000..124b1e10a13d
--- /dev/null
+++ b/sound/oss/ac97_codec.c
@@ -0,0 +1,1576 @@
+/*
+ * ac97_codec.c: Generic AC97 mixer/modem module
+ *
+ * Derived from ac97 mixer in maestro and trident driver.
+ *
+ * Copyright 2000 Silicon Integrated System Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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.
+ *
+ **************************************************************************
+ *
+ * The Intel Audio Codec '97 specification is available at the Intel
+ * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/
+ *
+ * The specification itself is currently available at:
+ * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf
+ *
+ **************************************************************************
+ *
+ * History
+ * May 02, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Removed non existant WM9700
+ * Added support for WM9705, WM9708, WM9709, WM9710, WM9711
+ * WM9712 and WM9717
+ * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
+ * corrections to support WM9707 in ViewPad 1000
+ * v0.4 Mar 15 2000 Ollie Lho
+ * dual codecs support verified with 4 channels output
+ * v0.3 Feb 22 2000 Ollie Lho
+ * bug fix for record mask setting
+ * v0.2 Feb 10 2000 Ollie Lho
+ * add ac97_read_proc for /proc/driver/{vendor}/ac97
+ * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>
+ * Isolated from trident.c to support multiple ac97 codec
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ac97_codec.h>
+#include <asm/uaccess.h>
+
+#define CODEC_ID_BUFSZ 14
+
+static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
+static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
+ unsigned int left, unsigned int right);
+static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );
+static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
+static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
+
+static int ac97_init_mixer(struct ac97_codec *codec);
+
+static int wolfson_init03(struct ac97_codec * codec);
+static int wolfson_init04(struct ac97_codec * codec);
+static int wolfson_init05(struct ac97_codec * codec);
+static int wolfson_init11(struct ac97_codec * codec);
+static int wolfson_init13(struct ac97_codec * codec);
+static int tritech_init(struct ac97_codec * codec);
+static int tritech_maestro_init(struct ac97_codec * codec);
+static int sigmatel_9708_init(struct ac97_codec *codec);
+static int sigmatel_9721_init(struct ac97_codec *codec);
+static int sigmatel_9744_init(struct ac97_codec *codec);
+static int ad1886_init(struct ac97_codec *codec);
+static int eapd_control(struct ac97_codec *codec, int);
+static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
+static int cmedia_init(struct ac97_codec * codec);
+static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
+static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
+
+
+/*
+ * AC97 operations.
+ *
+ * If you are adding a codec then you should be able to use
+ * eapd_ops - any codec that supports EAPD amp control (most)
+ * null_ops - any ancient codec that supports nothing
+ *
+ * The three functions are
+ * init - used for non AC97 standard initialisation
+ * amplifier - used to do amplifier control (1=on 0=off)
+ * digital - switch to digital modes (0 = analog)
+ *
+ * Not all codecs support all features, not all drivers use all the
+ * operations yet
+ */
+
+static struct ac97_ops null_ops = { NULL, NULL, NULL };
+static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
+static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control};
+static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
+static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
+static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
+static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
+static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
+static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
+static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
+static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
+static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };
+static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
+static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
+static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
+static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
+static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
+
+/* sorted by vendor/device id */
+static const struct {
+ u32 id;
+ char *name;
+ struct ac97_ops *ops;
+ int flags;
+} ac97_codec_ids[] = {
+ {0x41445303, "Analog Devices AD1819", &null_ops},
+ {0x41445340, "Analog Devices AD1881", &null_ops},
+ {0x41445348, "Analog Devices AD1881A", &null_ops},
+ {0x41445360, "Analog Devices AD1885", &default_ops},
+ {0x41445361, "Analog Devices AD1886", &ad1886_ops},
+ {0x41445370, "Analog Devices AD1981", &null_ops},
+ {0x41445372, "Analog Devices AD1981A", &null_ops},
+ {0x41445374, "Analog Devices AD1981B", &null_ops},
+ {0x41445460, "Analog Devices AD1885", &default_ops},
+ {0x41445461, "Analog Devices AD1886", &ad1886_ops},
+ {0x414B4D00, "Asahi Kasei AK4540", &null_ops},
+ {0x414B4D01, "Asahi Kasei AK4542", &null_ops},
+ {0x414B4D02, "Asahi Kasei AK4543", &null_ops},
+ {0x414C4326, "ALC100P", &null_ops},
+ {0x414C4710, "ALC200/200P", &null_ops},
+ {0x414C4720, "ALC650", &default_digital_ops},
+ {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
+ {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
+ {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME },
+ {0x43525900, "Cirrus Logic CS4297", &default_ops},
+ {0x43525903, "Cirrus Logic CS4297", &default_ops},
+ {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
+ {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},
+ {0x43525923, "Cirrus Logic CS4298", &null_ops},
+ {0x4352592B, "Cirrus Logic CS4294", &null_ops},
+ {0x4352592D, "Cirrus Logic CS4294", &null_ops},
+ {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
+ {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
+ {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
+ {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM },
+ {0x44543031, "Diamond Technology DT0893", &default_ops},
+ {0x45838308, "ESS Allegro ES1988", &null_ops},
+ {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */
+ {0x4e534331, "National Semiconductor LM4549", &null_ops},
+ {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
+ {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
+ {0x545200FF, "TriTech TR?????", &tritech_m_ops},
+ {0x54524102, "TriTech TR28022", &null_ops},
+ {0x54524103, "TriTech TR28023", &null_ops},
+ {0x54524106, "TriTech TR28026", &null_ops},
+ {0x54524108, "TriTech TR28028", &tritech_ops},
+ {0x54524123, "TriTech TR A5", &null_ops},
+ {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03},
+ {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04},
+ {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05},
+ {0x574D4C09, "Wolfson WM9709", &null_ops},
+ {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11},
+ {0x574D4C13, "Wolfson WM9713", &wolfson_ops13, AC97_DEFAULT_POWER_OFF},
+ {0x83847600, "SigmaTel STAC????", &null_ops},
+ {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
+ {0x83847605, "SigmaTel STAC9704", &null_ops},
+ {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops},
+ {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops},
+ {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops},
+ {0x83847652, "SigmaTel STAC9752/53", &default_ops},
+ {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops},
+ {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops},
+ {0x83847684, "SigmaTel STAC9783/84?", &null_ops},
+ {0x57454301, "Winbond 83971D", &null_ops},
+};
+
+static const char *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 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 */ "Winbond 3D Stereo Enhancement",
+ /* 28 */ "Rockwell 3D Stereo Enhancement",
+ /* 29 */ "Reserved 29",
+ /* 30 */ "Reserved 30",
+ /* 31 */ "Reserved 31"
+};
+
+/* this table has default mixer values for all OSS mixers. */
+static struct mixer_defaults {
+ int mixer;
+ unsigned int value;
+} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
+ /* all values 0 -> 100 in bytes */
+ {SOUND_MIXER_VOLUME, 0x4343},
+ {SOUND_MIXER_BASS, 0x4343},
+ {SOUND_MIXER_TREBLE, 0x4343},
+ {SOUND_MIXER_PCM, 0x4343},
+ {SOUND_MIXER_SPEAKER, 0x4343},
+ {SOUND_MIXER_LINE, 0x4343},
+ {SOUND_MIXER_MIC, 0x0000},
+ {SOUND_MIXER_CD, 0x4343},
+ {SOUND_MIXER_ALTPCM, 0x4343},
+ {SOUND_MIXER_IGAIN, 0x4343},
+ {SOUND_MIXER_LINE1, 0x4343},
+ {SOUND_MIXER_PHONEIN, 0x4343},
+ {SOUND_MIXER_PHONEOUT, 0x4343},
+ {SOUND_MIXER_VIDEO, 0x4343},
+ {-1,0}
+};
+
+/* table to scale scale from OSS mixer value to AC97 mixer register value */
+static struct ac97_mixer_hw {
+ unsigned char offset;
+ int scale;
+} ac97_hw[SOUND_MIXER_NRDEVICES]= {
+ [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64},
+ [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16},
+ [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16},
+ [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32},
+ [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16},
+ [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32},
+ [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32},
+ [SOUND_MIXER_CD] = {AC97_CD_VOL, 32},
+ [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64},
+ [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16},
+ [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32},
+ [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32},
+ [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64},
+ [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32},
+};
+
+/* the following tables allow us to go from OSS <-> ac97 quickly. */
+enum ac97_recsettings {
+ AC97_REC_MIC=0,
+ AC97_REC_CD,
+ AC97_REC_VIDEO,
+ AC97_REC_AUX,
+ AC97_REC_LINE,
+ AC97_REC_STEREO, /* combination of all enabled outputs.. */
+ AC97_REC_MONO, /*.. or the mono equivalent */
+ AC97_REC_PHONE
+};
+
+static const unsigned int ac97_rm2oss[] = {
+ [AC97_REC_MIC] = SOUND_MIXER_MIC,
+ [AC97_REC_CD] = SOUND_MIXER_CD,
+ [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
+ [AC97_REC_AUX] = SOUND_MIXER_LINE1,
+ [AC97_REC_LINE] = SOUND_MIXER_LINE,
+ [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
+ [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
+};
+
+/* indexed by bit position */
+static const unsigned int ac97_oss_rm[] = {
+ [SOUND_MIXER_MIC] = AC97_REC_MIC,
+ [SOUND_MIXER_CD] = AC97_REC_CD,
+ [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
+ [SOUND_MIXER_LINE1] = AC97_REC_AUX,
+ [SOUND_MIXER_LINE] = AC97_REC_LINE,
+ [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
+ [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
+};
+
+static LIST_HEAD(codecs);
+static LIST_HEAD(codec_drivers);
+static DECLARE_MUTEX(codec_sem);
+
+/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
+ about that given mixer, and should be holding a spinlock for the card */
+static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel)
+{
+ u16 val;
+ int ret = 0;
+ int scale;
+ struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
+
+ val = codec->codec_read(codec , mh->offset);
+
+ if (val & AC97_MUTE) {
+ ret = 0;
+ } else if (AC97_STEREO_MASK & (1 << oss_channel)) {
+ /* nice stereo mixers .. */
+ int left,right;
+
+ left = (val >> 8) & 0x7f;
+ right = val & 0x7f;
+
+ if (oss_channel == SOUND_MIXER_IGAIN) {
+ right = (right * 100) / mh->scale;
+ left = (left * 100) / mh->scale;
+ } else {
+ /* these may have 5 or 6 bit resolution */
+ if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
+ scale = (1 << codec->bit_resolution);
+ else
+ scale = mh->scale;
+
+ right = 100 - ((right * 100) / scale);
+ left = 100 - ((left * 100) / scale);
+ }
+ ret = left | (right << 8);
+ } else if (oss_channel == SOUND_MIXER_SPEAKER) {
+ ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
+ } else if (oss_channel == SOUND_MIXER_PHONEIN) {
+ ret = 100 - (((val & 0x1f) * 100) / mh->scale);
+ } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
+ scale = (1 << codec->bit_resolution);
+ ret = 100 - (((val & 0x1f) * 100) / scale);
+ } else if (oss_channel == SOUND_MIXER_MIC) {
+ ret = 100 - (((val & 0x1f) * 100) / mh->scale);
+ /* the low bit is optional in the tone sliders and masking
+ it lets us avoid the 0xf 'bypass'.. */
+ } else if (oss_channel == SOUND_MIXER_BASS) {
+ ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
+ } else if (oss_channel == SOUND_MIXER_TREBLE) {
+ ret = 100 - (((val & 0xe) * 100) / mh->scale);
+ }
+
+#ifdef DEBUG
+ printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
+ "0x%04x -> 0x%04x\n",
+ oss_channel, codec->id ? "Secondary" : "Primary",
+ mh->offset, val, ret);
+#endif
+
+ return ret;
+}
+
+/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to
+ make sure all is well in arg land, call with spinlock held */
+static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
+ unsigned int left, unsigned int right)
+{
+ u16 val = 0;
+ int scale;
+ struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
+
+#ifdef DEBUG
+ printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "
+ "left vol:%2d, right vol:%2d:",
+ oss_channel, codec->id ? "Secondary" : "Primary",
+ mh->offset, left, right);
+#endif
+
+ if (AC97_STEREO_MASK & (1 << oss_channel)) {
+ /* stereo mixers */
+ if (left == 0 && right == 0) {
+ val = AC97_MUTE;
+ } else {
+ if (oss_channel == SOUND_MIXER_IGAIN) {
+ right = (right * mh->scale) / 100;
+ left = (left * mh->scale) / 100;
+ if (right >= mh->scale)
+ right = mh->scale-1;
+ if (left >= mh->scale)
+ left = mh->scale-1;
+ } else {
+ /* these may have 5 or 6 bit resolution */
+ if (oss_channel == SOUND_MIXER_VOLUME ||
+ oss_channel == SOUND_MIXER_ALTPCM)
+ scale = (1 << codec->bit_resolution);
+ else
+ scale = mh->scale;
+
+ right = ((100 - right) * scale) / 100;
+ left = ((100 - left) * scale) / 100;
+ if (right >= scale)
+ right = scale-1;
+ if (left >= scale)
+ left = scale-1;
+ }
+ val = (left << 8) | right;
+ }
+ } else if (oss_channel == SOUND_MIXER_BASS) {
+ val = codec->codec_read(codec , mh->offset) & ~0x0f00;
+ left = ((100 - left) * mh->scale) / 100;
+ if (left >= mh->scale)
+ left = mh->scale-1;
+ val |= (left << 8) & 0x0e00;
+ } else if (oss_channel == SOUND_MIXER_TREBLE) {
+ val = codec->codec_read(codec , mh->offset) & ~0x000f;
+ left = ((100 - left) * mh->scale) / 100;
+ if (left >= mh->scale)
+ left = mh->scale-1;
+ val |= left & 0x000e;
+ } else if(left == 0) {
+ val = AC97_MUTE;
+ } else if (oss_channel == SOUND_MIXER_SPEAKER) {
+ left = ((100 - left) * mh->scale) / 100;
+ if (left >= mh->scale)
+ left = mh->scale-1;
+ val = left << 1;
+ } else if (oss_channel == SOUND_MIXER_PHONEIN) {
+ left = ((100 - left) * mh->scale) / 100;
+ if (left >= mh->scale)
+ left = mh->scale-1;
+ val = left;
+ } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
+ scale = (1 << codec->bit_resolution);
+ left = ((100 - left) * scale) / 100;
+ if (left >= mh->scale)
+ left = mh->scale-1;
+ val = left;
+ } else if (oss_channel == SOUND_MIXER_MIC) {
+ val = codec->codec_read(codec , mh->offset) & ~0x801f;
+ left = ((100 - left) * mh->scale) / 100;
+ if (left >= mh->scale)
+ left = mh->scale-1;
+ val |= left;
+ /* the low bit is optional in the tone sliders and masking
+ it lets us avoid the 0xf 'bypass'.. */
+ }
+#ifdef DEBUG
+ printk(" 0x%04x", val);
+#endif
+
+ codec->codec_write(codec, mh->offset, val);
+
+#ifdef DEBUG
+ val = codec->codec_read(codec, mh->offset);
+ printk(" -> 0x%04x\n", val);
+#endif
+}
+
+/* a thin wrapper for write_mixer */
+static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val )
+{
+ unsigned int left,right;
+
+ /* cleanse input a little */
+ right = ((val >> 8) & 0xff) ;
+ left = (val & 0xff) ;
+
+ if (right > 100) right = 100;
+ if (left > 100) left = 100;
+
+ codec->mixer_state[oss_mixer] = (right << 8) | left;
+ codec->write_mixer(codec, oss_mixer, left, right);
+}
+
+/* read or write the recmask, the ac97 can really have left and right recording
+ inputs independantly set, but OSS doesn't seem to want us to express that to
+ the user. the caller guarantees that we have a supported bit set, and they
+ must be holding the card's spinlock */
+static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask)
+{
+ unsigned int val;
+
+ if (rw) {
+ /* read it from the card */
+ val = codec->codec_read(codec, AC97_RECORD_SELECT);
+#ifdef DEBUG
+ printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
+#endif
+ return (1 << ac97_rm2oss[val & 0x07]);
+ }
+
+ /* else, write the first set in the mask as the
+ output */
+ /* clear out current set value first (AC97 supports only 1 input!) */
+ val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);
+ if (mask != val)
+ mask &= ~val;
+
+ val = ffs(mask);
+ val = ac97_oss_rm[val-1];
+ val |= val << 8; /* set both channels */
+
+#ifdef DEBUG
+ printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
+#endif
+
+ codec->codec_write(codec, AC97_RECORD_SELECT, val);
+
+ return 0;
+};
+
+static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
+{
+ int i, val = 0;
+
+ if (cmd == SOUND_MIXER_INFO) {
+ mixer_info info;
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.id, codec->name, sizeof(info.id));
+ strlcpy(info.name, codec->name, sizeof(info.name));
+ info.modify_counter = codec->modcnt;
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == SOUND_OLD_MIXER_INFO) {
+ _old_mixer_info info;
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.id, codec->name, sizeof(info.id));
+ strlcpy(info.name, codec->name, sizeof(info.name));
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+ return -EINVAL;
+
+ if (cmd == OSS_GETVERSION)
+ return put_user(SOUND_VERSION, (int __user *)arg);
+
+ if (_SIOC_DIR(cmd) == _SIOC_READ) {
+ switch (_IOC_NR(cmd)) {
+ case SOUND_MIXER_RECSRC: /* give them the current record source */
+ if (!codec->recmask_io) {
+ val = 0;
+ } else {
+ val = codec->recmask_io(codec, 1, 0);
+ }
+ break;
+
+ case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
+ val = codec->supported_mixers;
+ break;
+
+ case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+ val = codec->record_sources;
+ break;
+
+ case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+ val = codec->stereo_mixers;
+ break;
+
+ case SOUND_MIXER_CAPS:
+ val = SOUND_CAP_EXCL_INPUT;
+ break;
+
+ default: /* read a specific mixer */
+ i = _IOC_NR(cmd);
+
+ if (!supported_mixer(codec, i))
+ return -EINVAL;
+
+ /* do we ever want to touch the hardware? */
+ /* val = codec->read_mixer(codec, i); */
+ val = codec->mixer_state[i];
+ break;
+ }
+ return put_user(val, (int __user *)arg);
+ }
+
+ if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
+ codec->modcnt++;
+ if (get_user(val, (int __user *)arg))
+ return -EFAULT;
+
+ switch (_IOC_NR(cmd)) {
+ case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+ if (!codec->recmask_io) return -EINVAL;
+ if (!val) return 0;
+ if (!(val &= codec->record_sources)) return -EINVAL;
+
+ codec->recmask_io(codec, 0, val);
+
+ return 0;
+ default: /* write a specific mixer */
+ i = _IOC_NR(cmd);
+
+ if (!supported_mixer(codec, i))
+ return -EINVAL;
+
+ ac97_set_mixer(codec, i, val);
+
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/* entry point for /proc/driver/controller_vendor/ac97/%d */
+int ac97_read_proc (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0, cap, extid, val, id1, id2;
+ struct ac97_codec *codec;
+ int is_ac97_20 = 0;
+
+ if ((codec = data) == NULL)
+ return -ENODEV;
+
+ id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
+ id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
+ len += sprintf (page+len, "Vendor name : %s\n", codec->name);
+ len += sprintf (page+len, "Vendor id : %04X %04X\n", id1, id2);
+
+ extid = codec->codec_read(codec, AC97_EXTENDED_ID);
+ extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13));
+ len += sprintf (page+len, "AC97 Version : %s\n",
+ extid ? "2.0 or later" : "1.0");
+ if (extid) is_ac97_20 = 1;
+
+ cap = codec->codec_read(codec, AC97_RESET);
+ len += sprintf (page+len, "Capabilities :%s%s%s%s%s%s\n",
+ cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "",
+ cap & 0x0002 ? " -reserved1-" : "",
+ cap & 0x0004 ? " -bass & treble-" : "",
+ cap & 0x0008 ? " -simulated stereo-" : "",
+ cap & 0x0010 ? " -headphone out-" : "",
+ cap & 0x0020 ? " -loudness-" : "");
+ val = cap & 0x00c0;
+ len += sprintf (page+len, "DAC resolutions :%s%s%s\n",
+ " -16-bit-",
+ val & 0x0040 ? " -18-bit-" : "",
+ val & 0x0080 ? " -20-bit-" : "");
+ val = cap & 0x0300;
+ len += sprintf (page+len, "ADC resolutions :%s%s%s\n",
+ " -16-bit-",
+ val & 0x0100 ? " -18-bit-" : "",
+ val & 0x0200 ? " -20-bit-" : "");
+ len += sprintf (page+len, "3D enhancement : %s\n",
+ ac97_stereo_enhancements[(cap >> 10) & 0x1f]);
+
+ val = codec->codec_read(codec, AC97_GENERAL_PURPOSE);
+ len += sprintf (page+len, "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");
+
+ extid = codec->codec_read(codec, AC97_EXTENDED_ID);
+ cap = extid;
+ len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n",
+ cap & 0x0001 ? " -var rate PCM audio-" : "",
+ cap & 0x0002 ? " -2x PCM audio out-" : "",
+ cap & 0x0008 ? " -var rate MIC in-" : "",
+ cap & 0x0040 ? " -PCM center DAC-" : "",
+ cap & 0x0080 ? " -PCM surround DAC-" : "",
+ cap & 0x0100 ? " -PCM LFE DAC-" : "",
+ cap & 0x0200 ? " -slot/DAC mappings-" : "");
+ if (is_ac97_20) {
+ len += sprintf (page+len, "Front DAC rate : %d\n",
+ codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE));
+ }
+
+ return len;
+}
+
+/**
+ * codec_id - Turn id1/id2 into a PnP string
+ * @id1: Vendor ID1
+ * @id2: Vendor ID2
+ * @buf: CODEC_ID_BUFSZ byte buffer
+ *
+ * Fills buf with a zero terminated PnP ident string for the id1/id2
+ * pair. For convenience the return is the passed in buffer pointer.
+ */
+
+static char *codec_id(u16 id1, u16 id2, char *buf)
+{
+ if(id1&0x8080) {
+ snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);
+ } else {
+ buf[0] = (id1 >> 8);
+ buf[1] = (id1 & 0xFF);
+ buf[2] = (id2 >> 8);
+ snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);
+ }
+ return buf;
+}
+
+/**
+ * ac97_check_modem - Check if the Codec is a modem
+ * @codec: codec to check
+ *
+ * Return true if the device is an AC97 1.0 or AC97 2.0 modem
+ */
+
+static int ac97_check_modem(struct ac97_codec *codec)
+{
+ /* Check for an AC97 1.0 soft modem (ID1) */
+ if(codec->codec_read(codec, AC97_RESET) & 2)
+ return 1;
+ /* Check for an AC97 2.x soft modem */
+ codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
+ if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
+ return 1;
+ return 0;
+}
+
+
+/**
+ * ac97_alloc_codec - Allocate an AC97 codec
+ *
+ * Returns a new AC97 codec structure. AC97 codecs may become
+ * refcounted soon so this interface is needed. Returns with
+ * one reference taken.
+ */
+
+struct ac97_codec *ac97_alloc_codec(void)
+{
+ struct ac97_codec *codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL);
+ if(!codec)
+ return NULL;
+
+ memset(codec, 0, sizeof(*codec));
+ spin_lock_init(&codec->lock);
+ INIT_LIST_HEAD(&codec->list);
+ return codec;
+}
+
+EXPORT_SYMBOL(ac97_alloc_codec);
+
+/**
+ * ac97_release_codec - Release an AC97 codec
+ * @codec: codec to release
+ *
+ * Release an allocated AC97 codec. This will be refcounted in
+ * time but for the moment is trivial. Calls the unregister
+ * handler if the codec is now defunct.
+ */
+
+void ac97_release_codec(struct ac97_codec *codec)
+{
+ /* Remove from the list first, we don't want to be
+ "rediscovered" */
+ down(&codec_sem);
+ list_del(&codec->list);
+ up(&codec_sem);
+ /*
+ * The driver needs to deal with internal
+ * locking to avoid accidents here.
+ */
+ if(codec->driver)
+ codec->driver->remove(codec, codec->driver);
+ kfree(codec);
+}
+
+EXPORT_SYMBOL(ac97_release_codec);
+
+/**
+ * ac97_probe_codec - Initialize and setup AC97-compatible codec
+ * @codec: (in/out) Kernel info for a single AC97 codec
+ *
+ * Reset the AC97 codec, then initialize the mixer and
+ * the rest of the @codec structure.
+ *
+ * The codec_read and codec_write fields of @codec are
+ * required to be setup and working when this function
+ * is called. All other fields are set by this function.
+ *
+ * codec_wait field of @codec can optionally be provided
+ * when calling this function. If codec_wait is not %NULL,
+ * this function will call codec_wait any time it is
+ * necessary to wait for the audio chip to reach the
+ * codec-ready state. If codec_wait is %NULL, then
+ * the default behavior is to call schedule_timeout.
+ * Currently codec_wait is used to wait for AC97 codec
+ * reset to complete.
+ *
+ * Some codecs will power down when a register reset is
+ * performed. We now check for such codecs.
+ *
+ * Returns 1 (true) on success, or 0 (false) on failure.
+ */
+
+int ac97_probe_codec(struct ac97_codec *codec)
+{
+ u16 id1, id2;
+ u16 audio;
+ int i;
+ char cidbuf[CODEC_ID_BUFSZ];
+ u16 f;
+ struct list_head *l;
+ struct ac97_driver *d;
+
+ /* wait for codec-ready state */
+ if (codec->codec_wait)
+ codec->codec_wait(codec);
+ else
+ udelay(10);
+
+ /* will the codec power down if register reset ? */
+ id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
+ id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
+ codec->name = NULL;
+ codec->codec_ops = &null_ops;
+ for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
+ if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
+ codec->type = ac97_codec_ids[i].id;
+ codec->name = ac97_codec_ids[i].name;
+ codec->codec_ops = ac97_codec_ids[i].ops;
+ codec->flags = ac97_codec_ids[i].flags;
+ break;
+ }
+ }
+
+ codec->model = (id1 << 16) | id2;
+ if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {
+ /* reset codec and wait for the ready bit before we continue */
+ codec->codec_write(codec, AC97_RESET, 0L);
+ if (codec->codec_wait)
+ codec->codec_wait(codec);
+ else
+ udelay(10);
+ }
+
+ /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
+ * be read zero.
+ *
+ * FIXME: is the following comment outdated? -jgarzik
+ * Probing of AC97 in this way is not reliable, it is not even SAFE !!
+ */
+ if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
+ printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
+ (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary")
+ : (codec->id&1 ? "Secondary": "Primary"));
+ return 0;
+ }
+
+ /* probe for Modem Codec */
+ codec->modem = ac97_check_modem(codec);
+
+ /* enable SPDIF */
+ f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+ if((codec->codec_ops == &null_ops) && (f & 4))
+ codec->codec_ops = &default_digital_ops;
+
+ /* A device which thinks its a modem but isnt */
+ if(codec->flags & AC97_DELUDED_MODEM)
+ codec->modem = 0;
+
+ if (codec->name == NULL)
+ codec->name = "Unknown";
+ printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n",
+ codec->modem ? "Modem" : (audio ? "Audio" : ""),
+ codec_id(id1, id2, cidbuf), codec->name);
+
+ if(!ac97_init_mixer(codec))
+ return 0;
+
+ /*
+ * Attach last so the caller can override the mixer
+ * callbacks.
+ */
+
+ down(&codec_sem);
+ list_add(&codec->list, &codecs);
+
+ list_for_each(l, &codec_drivers) {
+ d = list_entry(l, struct ac97_driver, list);
+ if ((codec->model ^ d->codec_id) & d->codec_mask)
+ continue;
+ if(d->probe(codec, d) == 0)
+ {
+ codec->driver = d;
+ break;
+ }
+ }
+
+ up(&codec_sem);
+ return 1;
+}
+
+static int ac97_init_mixer(struct ac97_codec *codec)
+{
+ u16 cap;
+ int i;
+
+ cap = codec->codec_read(codec, AC97_RESET);
+
+ /* mixer masks */
+ codec->supported_mixers = AC97_SUPPORTED_MASK;
+ codec->stereo_mixers = AC97_STEREO_MASK;
+ codec->record_sources = AC97_RECORD_MASK;
+ if (!(cap & 0x04))
+ codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
+ if (!(cap & 0x10))
+ codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
+
+
+ /* detect bit resolution */
+ codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
+ if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)
+ codec->bit_resolution = 6;
+ else
+ codec->bit_resolution = 5;
+
+ /* generic OSS to AC97 wrapper */
+ codec->read_mixer = ac97_read_mixer;
+ codec->write_mixer = ac97_write_mixer;
+ codec->recmask_io = ac97_recmask_io;
+ codec->mixer_ioctl = ac97_mixer_ioctl;
+
+ /* initialize mixer channel volumes */
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ struct mixer_defaults *md = &mixer_defaults[i];
+ if (md->mixer == -1)
+ break;
+ if (!supported_mixer(codec, md->mixer))
+ continue;
+ ac97_set_mixer(codec, md->mixer, md->value);
+ }
+
+ /* codec specific initialization for 4-6 channel output or secondary codec stuff */
+ if (codec->codec_ops->init != NULL) {
+ codec->codec_ops->init(codec);
+ }
+
+ /*
+ * Volume is MUTE only on this device. We have to initialise
+ * it but its useless beyond that.
+ */
+ if(codec->flags & AC97_NO_PCM_VOLUME)
+ {
+ codec->supported_mixers &= ~SOUND_MASK_PCM;
+ printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");
+ }
+ return 1;
+}
+
+#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */
+#define AC97_SIGMATEL_DAC2INVERT 0x6e
+#define AC97_SIGMATEL_BIAS1 0x70
+#define AC97_SIGMATEL_BIAS2 0x72
+#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */
+#define AC97_SIGMATEL_CIC1 0x76
+#define AC97_SIGMATEL_CIC2 0x78
+
+
+static int sigmatel_9708_init(struct ac97_codec * codec)
+{
+ u16 codec72, codec6c;
+
+ codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;
+ codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);
+
+ if ((codec72==0) && (codec6c==0)) {
+ codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+ codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);
+ codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
+ codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);
+ } else if ((codec72==0x8000) && (codec6c==0)) {
+ codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+ codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);
+ codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);
+ } else if ((codec72==0x8000) && (codec6c==0x0080)) {
+ /* nothing */
+ }
+ codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
+ return 0;
+}
+
+
+static int sigmatel_9721_init(struct ac97_codec * codec)
+{
+ /* Only set up secondary codec */
+ if (codec->id == 0)
+ return 0;
+
+ codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
+
+ /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
+ sloc 3,4 = 0x01, slot 7,8 = 0x00, */
+ codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00);
+
+ /* we don't have the crystal when we are on an AMR card, so use
+ BIT_CLK as our clock source. Write the magic word ABBA and read
+ back to enable register 0x78 */
+ codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+ codec->codec_read(codec, AC97_SIGMATEL_CIC1);
+
+ /* sync all the clocks*/
+ codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802);
+
+ return 0;
+}
+
+
+static int sigmatel_9744_init(struct ac97_codec * codec)
+{
+ // patch for SigmaTel
+ codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+ codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk
+ codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
+ codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002);
+ codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
+ return 0;
+}
+
+static int cmedia_init(struct ac97_codec *codec)
+{
+ /* Initialise the CMedia 9739 */
+ /*
+ We could set various options here
+ Register 0x20 bit 0x100 sets mic as center bass
+ Also do multi_channel_ctrl &=~0x3000 |=0x1000
+
+ For now we set up the GPIO and PC beep
+ */
+
+ u16 v;
+
+ /* MIC */
+ codec->codec_write(codec, 0x64, 0x3000);
+ v = codec->codec_read(codec, 0x64);
+ v &= ~0x8000;
+ codec->codec_write(codec, 0x64, v);
+ codec->codec_write(codec, 0x70, 0x0100);
+ codec->codec_write(codec, 0x72, 0x0020);
+ return 0;
+}
+
+#define AC97_WM97XX_FMIXER_VOL 0x72
+#define AC97_WM97XX_RMIXER_VOL 0x74
+#define AC97_WM97XX_TEST 0x5a
+#define AC97_WM9704_RPCM_VOL 0x70
+#define AC97_WM9711_OUT3VOL 0x16
+
+static int wolfson_init03(struct ac97_codec * codec)
+{
+ /* this is known to work for the ViewSonic ViewPad 1000 */
+ codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
+ codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000);
+ return 0;
+}
+
+static int wolfson_init04(struct ac97_codec * codec)
+{
+ codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
+ codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808);
+
+ // patch for DVD noise
+ codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200);
+
+ // init vol as PCM vol
+ codec->codec_write(codec, AC97_WM9704_RPCM_VOL,
+ codec->codec_read(codec, AC97_PCMOUT_VOL));
+
+ /* set rear surround volume */
+ codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
+ return 0;
+}
+
+/* WM9705, WM9710 */
+static int wolfson_init05(struct ac97_codec * codec)
+{
+ /* set front mixer volume */
+ codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
+ return 0;
+}
+
+/* WM9711, WM9712 */
+static int wolfson_init11(struct ac97_codec * codec)
+{
+ /* stop pop's during suspend/resume */
+ codec->codec_write(codec, AC97_WM97XX_TEST,
+ codec->codec_read(codec, AC97_WM97XX_TEST) & 0xffbf);
+
+ /* set out3 volume */
+ codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808);
+ return 0;
+}
+
+/* WM9713 */
+static int wolfson_init13(struct ac97_codec * codec)
+{
+ codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);
+ codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
+ codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
+ codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
+ codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
+ codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
+
+ return 0;
+}
+
+static int tritech_init(struct ac97_codec * codec)
+{
+ codec->codec_write(codec, 0x26, 0x0300);
+ codec->codec_write(codec, 0x26, 0x0000);
+ codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
+ codec->codec_write(codec, AC97_RESERVED_3A, 0x0000);
+ return 0;
+}
+
+
+/* copied from drivers/sound/maestro.c */
+static int tritech_maestro_init(struct ac97_codec * codec)
+{
+ /* no idea what this does */
+ codec->codec_write(codec, 0x2A, 0x0001);
+ codec->codec_write(codec, 0x2C, 0x0000);
+ codec->codec_write(codec, 0x2C, 0XFFFF);
+ return 0;
+}
+
+
+
+/*
+ * Presario700 workaround
+ * for Jack Sense/SPDIF Register mis-setting causing
+ * no audible output
+ * by Santiago Nullo 04/05/2002
+ */
+
+#define AC97_AD1886_JACK_SENSE 0x72
+
+static int ad1886_init(struct ac97_codec * codec)
+{
+ /* from AD1886 Specs */
+ codec->codec_write(codec, AC97_AD1886_JACK_SENSE, 0x0010);
+ return 0;
+}
+
+
+
+
+/*
+ * This is basically standard AC97. It should work as a default for
+ * almost all modern codecs. Note that some cards wire EAPD *backwards*
+ * That side of it is up to the card driver not us to cope with.
+ *
+ */
+
+static int eapd_control(struct ac97_codec * codec, int on)
+{
+ if(on)
+ codec->codec_write(codec, AC97_POWER_CONTROL,
+ codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
+ else
+ codec->codec_write(codec, AC97_POWER_CONTROL,
+ codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000);
+ return 0;
+}
+
+static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
+{
+ u16 reg;
+
+ reg = codec->codec_read(codec, AC97_SPDIF_CONTROL);
+
+ switch(rate)
+ {
+ /* Off by default */
+ default:
+ case 0:
+ reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+ codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF));
+ if(rate == 0)
+ return 0;
+ return -EINVAL;
+ case 1:
+ reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
+ break;
+ case 2:
+ reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
+ break;
+ case 3:
+ reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
+ break;
+ }
+
+ reg &= ~AC97_SC_CC_MASK;
+ reg |= (mode & AUDIO_CCMASK) << 6;
+
+ if(mode & AUDIO_DIGITAL)
+ reg |= 2;
+ if(mode & AUDIO_PRO)
+ reg |= 1;
+ if(mode & AUDIO_DRS)
+ reg |= 0x4000;
+
+ codec->codec_write(codec, AC97_SPDIF_CONTROL, reg);
+
+ reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+ reg &= (AC97_EA_SLOT_MASK);
+ reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots;
+ codec->codec_write(codec, AC97_EXTENDED_STATUS, reg);
+
+ reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
+ if(!(reg & 0x0400))
+ {
+ codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Crystal digital audio control (CS4299)
+ */
+
+static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
+{
+ u16 cv;
+
+ if(mode & AUDIO_DIGITAL)
+ return -EINVAL;
+
+ switch(rate)
+ {
+ case 0: cv = 0x0; break; /* SPEN off */
+ case 48000: cv = 0x8004; break; /* 48KHz digital */
+ case 44100: cv = 0x8104; break; /* 44.1KHz digital */
+ case 32768: /* 32Khz */
+ default:
+ return -EINVAL;
+ }
+ codec->codec_write(codec, 0x68, cv);
+ return 0;
+}
+
+/*
+ * CMedia digital audio control
+ * Needs more work.
+ */
+
+static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
+{
+ u16 cv;
+
+ if(mode & AUDIO_DIGITAL)
+ return -EINVAL;
+
+ switch(rate)
+ {
+ case 0: cv = 0x0001; break; /* SPEN off */
+ case 48000: cv = 0x0009; break; /* 48KHz digital */
+ default:
+ return -EINVAL;
+ }
+ codec->codec_write(codec, 0x2A, 0x05c4);
+ codec->codec_write(codec, 0x6C, cv);
+
+ /* Switch on mix to surround */
+ cv = codec->codec_read(codec, 0x64);
+ cv &= ~0x0200;
+ if(mode)
+ cv |= 0x0200;
+ codec->codec_write(codec, 0x64, cv);
+ return 0;
+}
+
+
+/* copied from drivers/sound/maestro.c */
+#if 0 /* there has been 1 person on the planet with a pt101 that we
+ know of. If they care, they can put this back in :) */
+static int pt101_init(struct ac97_codec * codec)
+{
+ printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
+ /* who knows.. */
+ codec->codec_write(codec, 0x2A, 0x0001);
+ codec->codec_write(codec, 0x2C, 0x0000);
+ codec->codec_write(codec, 0x2C, 0xFFFF);
+ codec->codec_write(codec, 0x10, 0x9F1F);
+ codec->codec_write(codec, 0x12, 0x0808);
+ codec->codec_write(codec, 0x14, 0x9F1F);
+ codec->codec_write(codec, 0x16, 0x9F1F);
+ codec->codec_write(codec, 0x18, 0x0404);
+ codec->codec_write(codec, 0x1A, 0x0000);
+ codec->codec_write(codec, 0x1C, 0x0000);
+ codec->codec_write(codec, 0x02, 0x0404);
+ codec->codec_write(codec, 0x04, 0x0808);
+ codec->codec_write(codec, 0x0C, 0x801F);
+ codec->codec_write(codec, 0x0E, 0x801F);
+ return 0;
+}
+#endif
+
+
+EXPORT_SYMBOL(ac97_read_proc);
+EXPORT_SYMBOL(ac97_probe_codec);
+
+/*
+ * AC97 library support routines
+ */
+
+/**
+ * ac97_set_dac_rate - set codec rate adaption
+ * @codec: ac97 code
+ * @rate: rate in hertz
+ *
+ * Set the DAC rate. Assumes the codec supports VRA. The caller is
+ * expected to have checked this little detail.
+ */
+
+unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate)
+{
+ unsigned int new_rate = rate;
+ u32 dacp;
+ u32 mast_vol, phone_vol, mono_vol, pcm_vol;
+ u32 mute_vol = 0x8000; /* The mute volume? */
+
+ if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE))
+ {
+ /* Mute several registers */
+ mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO);
+ mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO);
+ phone_vol = codec->codec_read(codec, AC97_HEADPHONE_VOL);
+ pcm_vol = codec->codec_read(codec, AC97_PCMOUT_VOL);
+ codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mute_vol);
+ codec->codec_write(codec, AC97_MASTER_VOL_MONO, mute_vol);
+ codec->codec_write(codec, AC97_HEADPHONE_VOL, mute_vol);
+ codec->codec_write(codec, AC97_PCMOUT_VOL, mute_vol);
+
+ /* Power down the DAC */
+ dacp=codec->codec_read(codec, AC97_POWER_CONTROL);
+ codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200);
+ /* Load the rate and read the effective rate */
+ codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate);
+ new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE);
+ /* Power it back up */
+ codec->codec_write(codec, AC97_POWER_CONTROL, dacp);
+
+ /* Restore volumes */
+ codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mast_vol);
+ codec->codec_write(codec, AC97_MASTER_VOL_MONO, mono_vol);
+ codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol);
+ codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol);
+ }
+ return new_rate;
+}
+
+EXPORT_SYMBOL(ac97_set_dac_rate);
+
+/**
+ * ac97_set_adc_rate - set codec rate adaption
+ * @codec: ac97 code
+ * @rate: rate in hertz
+ *
+ * Set the ADC rate. Assumes the codec supports VRA. The caller is
+ * expected to have checked this little detail.
+ */
+
+unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate)
+{
+ unsigned int new_rate = rate;
+ u32 dacp;
+
+ if(rate != codec->codec_read(codec, AC97_PCM_LR_ADC_RATE))
+ {
+ /* Power down the ADC */
+ dacp=codec->codec_read(codec, AC97_POWER_CONTROL);
+ codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0100);
+ /* Load the rate and read the effective rate */
+ codec->codec_write(codec, AC97_PCM_LR_ADC_RATE, rate);
+ new_rate=codec->codec_read(codec, AC97_PCM_LR_ADC_RATE);
+ /* Power it back up */
+ codec->codec_write(codec, AC97_POWER_CONTROL, dacp);
+ }
+ return new_rate;
+}
+
+EXPORT_SYMBOL(ac97_set_adc_rate);
+
+int ac97_save_state(struct ac97_codec *codec)
+{
+ return 0;
+}
+
+EXPORT_SYMBOL(ac97_save_state);
+
+int ac97_restore_state(struct ac97_codec *codec)
+{
+ int i;
+ unsigned int left, right, val;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if (!supported_mixer(codec, i))
+ continue;
+
+ val = codec->mixer_state[i];
+ right = val >> 8;
+ left = val & 0xff;
+ codec->write_mixer(codec, i, left, right);
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(ac97_restore_state);
+
+/**
+ * ac97_register_driver - register a codec helper
+ * @driver: Driver handler
+ *
+ * Register a handler for codecs matching the codec id. The handler
+ * attach function is called for all present codecs and will be
+ * called when new codecs are discovered.
+ */
+
+int ac97_register_driver(struct ac97_driver *driver)
+{
+ struct list_head *l;
+ struct ac97_codec *c;
+
+ down(&codec_sem);
+ INIT_LIST_HEAD(&driver->list);
+ list_add(&driver->list, &codec_drivers);
+
+ list_for_each(l, &codecs)
+ {
+ c = list_entry(l, struct ac97_codec, list);
+ if(c->driver != NULL || ((c->model ^ driver->codec_id) & driver->codec_mask))
+ continue;
+ if(driver->probe(c, driver))
+ continue;
+ c->driver = driver;
+ }
+ up(&codec_sem);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(ac97_register_driver);
+
+/**
+ * ac97_unregister_driver - unregister a codec helper
+ * @driver: Driver handler
+ *
+ * Unregister a handler for codecs matching the codec id. The handler
+ * remove function is called for all matching codecs.
+ */
+
+void ac97_unregister_driver(struct ac97_driver *driver)
+{
+ struct list_head *l;
+ struct ac97_codec *c;
+
+ down(&codec_sem);
+ list_del_init(&driver->list);
+
+ list_for_each(l, &codecs)
+ {
+ c = list_entry(l, struct ac97_codec, list);
+ if (c->driver == driver) {
+ driver->remove(c, driver);
+ c->driver = NULL;
+ }
+ }
+
+ up(&codec_sem);
+}
+
+EXPORT_SYMBOL_GPL(ac97_unregister_driver);
+
+static int swap_headphone(int remove_master)
+{
+ struct list_head *l;
+ struct ac97_codec *c;
+
+ if (remove_master) {
+ down(&codec_sem);
+ list_for_each(l, &codecs)
+ {
+ c = list_entry(l, struct ac97_codec, list);
+ if (supported_mixer(c, SOUND_MIXER_PHONEOUT))
+ c->supported_mixers &= ~SOUND_MASK_PHONEOUT;
+ }
+ up(&codec_sem);
+ } else
+ ac97_hw[SOUND_MIXER_PHONEOUT].offset = AC97_MASTER_VOL_STEREO;
+
+ /* Scale values already match */
+ ac97_hw[SOUND_MIXER_VOLUME].offset = AC97_MASTER_VOL_MONO;
+ return 0;
+}
+
+static int apply_quirk(int quirk)
+{
+ switch (quirk) {
+ case AC97_TUNE_NONE:
+ return 0;
+ case AC97_TUNE_HP_ONLY:
+ return swap_headphone(1);
+ case AC97_TUNE_SWAP_HP:
+ return swap_headphone(0);
+ case AC97_TUNE_SWAP_SURROUND:
+ return -ENOSYS; /* not yet implemented */
+ case AC97_TUNE_AD_SHARING:
+ return -ENOSYS; /* not yet implemented */
+ case AC97_TUNE_ALC_JACK:
+ return -ENOSYS; /* not yet implemented */
+ }
+ return -EINVAL;
+}
+
+/**
+ * ac97_tune_hardware - tune up the hardware
+ * @pdev: pci_dev pointer
+ * @quirk: quirk list
+ * @override: explicit quirk value (overrides if not AC97_TUNE_DEFAULT)
+ *
+ * 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 ac97_tune_hardware(struct pci_dev *pdev, struct ac97_quirk *quirk, int override)
+{
+ int result;
+
+ if (!quirk)
+ return -EINVAL;
+
+ if (override != AC97_TUNE_DEFAULT) {
+ result = apply_quirk(override);
+ if (result < 0)
+ printk(KERN_ERR "applying quirk type %d failed (%d)\n", override, result);
+ return result;
+ }
+
+ for (; quirk->vendor; quirk++) {
+ if (quirk->vendor != pdev->subsystem_vendor)
+ continue;
+ if ((! quirk->mask && quirk->device == pdev->subsystem_device) ||
+ quirk->device == (quirk->mask & pdev->subsystem_device)) {
+#ifdef DEBUG
+ printk("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, pdev->subsystem_device);
+#endif
+ result = apply_quirk(quirk->type);
+ if (result < 0)
+ printk(KERN_ERR "applying quirk type %d for %s failed (%d)\n", quirk->type, quirk->name, result);
+ return result;
+ }
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(ac97_tune_hardware);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/ac97_plugin_ad1980.c b/sound/oss/ac97_plugin_ad1980.c
new file mode 100644
index 000000000000..24a9acd28160
--- /dev/null
+++ b/sound/oss/ac97_plugin_ad1980.c
@@ -0,0 +1,126 @@
+/*
+ ac97_plugin_ad1980.c Copyright (C) 2003 Red Hat, Inc. All rights reserved.
+
+ The contents of this file are subject to the Open Software License version 1.1
+ that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is
+ included herein by reference.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU General Public License version 2 (the "GPL") as
+ distributed in the kernel source COPYING file, in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the OSL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the OSL or the GPL.
+
+ Authors: Alan Cox <alan@redhat.com>
+
+ This is an example codec plugin. This one switches the connections
+ around to match the setups some vendors use with audio switched to
+ non standard front connectors not the normal rear ones
+
+ This code primarily exists to demonstrate how to use the codec
+ interface
+
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/ac97_codec.h>
+
+/**
+ * ad1980_remove - codec remove callback
+ * @codec: The codec that is being removed
+ *
+ * This callback occurs when an AC97 codec is being removed. A
+ * codec remove call will not occur for a codec during that codec
+ * probe callback.
+ *
+ * Most drivers will need to lock their remove versus their
+ * use of the codec after the probe function.
+ */
+
+static void __devexit ad1980_remove(struct ac97_codec *codec, struct ac97_driver *driver)
+{
+ /* Nothing to do in the simple example */
+}
+
+
+/**
+ * ad1980_probe - codec found callback
+ * @codec: ac97 codec matching the idents
+ * @driver: ac97_driver it matched
+ *
+ * This entry point is called when a codec is found which matches
+ * the driver. At the point it is called the codec is basically
+ * operational, mixer operations have been initialised and can
+ * be overriden. Called in process context. The field driver_private
+ * is available for the driver to use to store stuff.
+ *
+ * The caller can claim the device by returning zero, or return
+ * a negative error code.
+ */
+
+static int ad1980_probe(struct ac97_codec *codec, struct ac97_driver *driver)
+{
+ u16 control;
+
+#define AC97_AD_MISC 0x76
+
+ /* Switch the inputs/outputs over (from Dell code) */
+ control = codec->codec_read(codec, AC97_AD_MISC);
+ codec->codec_write(codec, AC97_AD_MISC, control | 0x4420);
+
+ /* We could refuse the device since we dont need to hang around,
+ but we will claim it */
+ return 0;
+}
+
+
+static struct ac97_driver ad1980_driver = {
+ .codec_id = 0x41445370,
+ .codec_mask = 0xFFFFFFFF,
+ .name = "AD1980 example",
+ .probe = ad1980_probe,
+ .remove = __devexit_p(ad1980_remove),
+};
+
+/**
+ * ad1980_exit - module exit path
+ *
+ * Our module is being unloaded. At this point unregister_driver
+ * will call back our remove handler for any existing codecs. You
+ * may not unregister_driver from interrupt context or from a
+ * probe/remove callback.
+ */
+
+static void ad1980_exit(void)
+{
+ ac97_unregister_driver(&ad1980_driver);
+}
+
+/**
+ * ad1980_init - set up ad1980 handlers
+ *
+ * After we call the register function it will call our probe
+ * function for each existing matching device before returning to us.
+ * Any devices appearing afterwards whose id's match the codec_id
+ * will also cause the probe function to be called.
+ * You may not register_driver from interrupt context or from a
+ * probe/remove callback.
+ */
+
+static int ad1980_init(void)
+{
+ return ac97_register_driver(&ad1980_driver);
+}
+
+module_init(ad1980_init);
+module_exit(ad1980_exit);
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/aci.c b/sound/oss/aci.c
new file mode 100644
index 000000000000..3928c2802cc4
--- /dev/null
+++ b/sound/oss/aci.c
@@ -0,0 +1,711 @@
+/*
+ * Audio Command Interface (ACI) driver (sound/aci.c)
+ *
+ * ACI is a protocol used to communicate with the microcontroller on
+ * some sound cards produced by miro, e.g. the miroSOUND PCM12 and
+ * PCM20. The ACI has been developed for miro by Norberto Pellicci
+ * <pellicci@home.com>. Special thanks to both him and miro for
+ * providing the ACI specification.
+ *
+ * The main function of the ACI is to control the mixer and to get a
+ * product identification. On the PCM20, ACI also controls the radio
+ * tuner on this card, this is supported in the Video for Linux
+ * miropcm20 driver.
+ * -
+ * This is a fullfeatured implementation. Unsupported features
+ * are bugs... (:
+ *
+ * It is not longer necessary to load the mad16 module first. The
+ * user is currently responsible to set the mad16 mixer correctly.
+ *
+ * To toggle the solo mode for full duplex operation just use the OSS
+ * record switch for the pcm ('wave') controller. Robert
+ * -
+ *
+ * Revision history:
+ *
+ * 1995-11-10 Markus Kuhn <mskuhn@cip.informatik.uni-erlangen.de>
+ * First version written.
+ * 1995-12-31 Markus Kuhn
+ * Second revision, general code cleanup.
+ * 1996-05-16 Hannu Savolainen
+ * Integrated with other parts of the driver.
+ * 1996-05-28 Markus Kuhn
+ * Initialize CS4231A mixer, make ACI first mixer,
+ * use new private mixer API for solo mode.
+ * 1998-08-18 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Small modification to export ACI functions and
+ * complete modularisation.
+ * 2000-06-20 Robert Siemer <Robert.Siemer@gmx.de>
+ * Don't initialize the CS4231A mixer anymore, so the code is
+ * working again, and other small changes to fit in todays
+ * kernels.
+ * 2000-08-26 Robert Siemer
+ * Clean up and rewrite for 2.4.x. Maybe it's SMP safe now... (:
+ * ioctl bugfix, and integration of solo-mode into OSS-API,
+ * added (OSS-limited) equalizer support, return value bugfix,
+ * changed param aci_reset to reset, new params: ide, wss.
+ * 2001-04-20 Robert Siemer
+ * even more cleanups...
+ * 2001-10-08 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * Get rid of check_region, .bss optimizations, use set_current_state
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "sound_config.h"
+
+int aci_port; /* as determined by bit 4 in the OPTi 929 MC4 register */
+static int aci_idcode[2]; /* manufacturer and product ID */
+int aci_version; /* ACI firmware version */
+
+EXPORT_SYMBOL(aci_port);
+EXPORT_SYMBOL(aci_version);
+
+#include "aci.h"
+
+
+static int aci_solo; /* status bit of the card that can't be *
+ * checked with ACI versions prior to 0xb0 */
+static int aci_amp; /* status bit for power-amp/line-out level
+ but I have no docs about what is what... */
+static int aci_micpreamp=3; /* microphone preamp-level that can't be *
+ * checked with ACI versions prior to 0xb0 */
+
+static int mixer_device;
+static struct semaphore aci_sem;
+
+#ifdef MODULE
+static int reset;
+module_param(reset, bool, 0);
+MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer.");
+#else
+static int reset = 1;
+#endif
+
+static int ide=-1;
+module_param(ide, int, 0);
+MODULE_PARM_DESC(ide,"1 enable, 0 disable ide-port - untested"
+ " default: do nothing");
+static int wss=-1;
+module_param(wss, int, 0);
+MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested"
+ " default: do nothing; for PCM1-pro only");
+
+#ifdef DEBUG
+static void print_bits(unsigned char c)
+{
+ int j;
+ printk(KERN_DEBUG "aci: ");
+
+ for (j=7; j>=0; j--) {
+ printk("%d", (c >> j) & 0x1);
+ }
+
+ printk("\n");
+}
+#endif
+
+/*
+ * This busy wait code normally requires less than 15 loops and
+ * practically always less than 100 loops on my i486/DX2 66 MHz.
+ *
+ * Warning: Waiting on the general status flag after reseting the MUTE
+ * function can take a VERY long time, because the PCM12 does some kind
+ * of fade-in effect. For this reason, access to the MUTE function has
+ * not been implemented at all.
+ *
+ * - The OSS interface has no mute option. It takes about 3 seconds to
+ * fade-in on my PCM20. busy_wait() handles it great now... Robert
+ */
+
+static int busy_wait(void)
+{
+ #define MINTIME 500
+ long timeout;
+ unsigned char byte;
+
+ for (timeout = 1; timeout <= MINTIME+30; timeout++) {
+ if (((byte=inb(BUSY_REGISTER)) & 1) == 0) {
+ if (timeout >= MINTIME)
+ printk(KERN_DEBUG "aci: Got READYFLAG in round %ld.\n", timeout-MINTIME);
+ return byte;
+ }
+ if (timeout >= MINTIME) {
+ long out=10*HZ;
+ switch (timeout-MINTIME) {
+ case 0 ... 9:
+ out /= 10;
+ case 10 ... 19:
+ out /= 10;
+ case 20 ... 30:
+ out /= 10;
+ default:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(out);
+ break;
+ }
+ }
+ }
+ printk(KERN_WARNING "aci: busy_wait() time out.\n");
+ return -EBUSY;
+}
+
+/* The four ACI command types are fucked up. [-:
+ * implied is: 1w - special case for INIT
+ * write is: 2w1r
+ * read is: x(1w1r) where x is 1 or 2 (1 CHECK_SIG, 1 CHECK_STER,
+ * 1 VERSION, 2 IDCODE)
+ * the command is only in the first write, rest is protocol overhead
+ *
+ * indexed is technically a write and used for STATUS
+ * and the special case for TUNE is: 3w1r
+ *
+ * Here the new general sheme: TUNE --> aci_rw_cmd(x, y, z)
+ * indexed and write --> aci_rw_cmd(x, y, -1)
+ * implied and read (x=1) --> aci_rw_cmd(x, -1, -1)
+ *
+ * Read (x>=2) is not implemented (only used during initialization).
+ * Use aci_idcode[2] and aci_version... Robert
+ */
+
+/* Some notes for error detection: theoretically it is possible.
+ * But it doubles the I/O-traffic from ww(r) to wwwrw(r) in the normal
+ * case and doesn't seem to be designed for that... Robert
+ */
+
+static inline int aci_rawwrite(unsigned char byte)
+{
+ if (busy_wait() >= 0) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "aci_rawwrite(%d)\n", byte);
+#endif
+ outb(byte, COMMAND_REGISTER);
+ return 0;
+ } else
+ return -EBUSY;
+}
+
+static inline int aci_rawread(void)
+{
+ unsigned char byte;
+
+ if (busy_wait() >= 0) {
+ byte=inb(STATUS_REGISTER);
+#ifdef DEBUG
+ printk(KERN_DEBUG "%d = aci_rawread()\n", byte);
+#endif
+ return byte;
+ } else
+ return -EBUSY;
+}
+
+
+int aci_rw_cmd(int write1, int write2, int write3)
+{
+ int write[] = {write1, write2, write3};
+ int read = -EINTR, i;
+
+ if (down_interruptible(&aci_sem))
+ goto out;
+
+ for (i=0; i<3; i++) {
+ if (write[i]< 0 || write[i] > 255)
+ break;
+ else {
+ read = aci_rawwrite(write[i]);
+ if (read < 0)
+ goto out_up;
+ }
+
+ }
+
+ read = aci_rawread();
+out_up: up(&aci_sem);
+out: return read;
+}
+
+EXPORT_SYMBOL(aci_rw_cmd);
+
+static int setvolume(int __user *arg,
+ unsigned char left_index, unsigned char right_index)
+{
+ int vol, ret, uservol, buf;
+
+ __get_user(uservol, arg);
+
+ /* left channel */
+ vol = uservol & 0xff;
+ if (vol > 100)
+ vol = 100;
+ vol = SCALE(100, 0x20, vol);
+ if ((buf=aci_write_cmd(left_index, 0x20 - vol))<0)
+ return buf;
+ ret = SCALE(0x20, 100, vol);
+
+
+ /* right channel */
+ vol = (uservol >> 8) & 0xff;
+ if (vol > 100)
+ vol = 100;
+ vol = SCALE(100, 0x20, vol);
+ if ((buf=aci_write_cmd(right_index, 0x20 - vol))<0)
+ return buf;
+ ret |= SCALE(0x20, 100, vol) << 8;
+
+ __put_user(ret, arg);
+
+ return 0;
+}
+
+static int getvolume(int __user *arg,
+ unsigned char left_index, unsigned char right_index)
+{
+ int vol;
+ int buf;
+
+ /* left channel */
+ if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0)
+ return buf;
+ vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0);
+
+ /* right channel */
+ if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0)
+ return buf;
+ vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8;
+
+ __put_user(vol, arg);
+
+ return 0;
+}
+
+
+/* The equalizer is somewhat strange on the ACI. From -12dB to +12dB
+ * write: 0xff..down.to..0x80==0x00..up.to..0x7f
+ */
+
+static inline unsigned int eq_oss2aci(unsigned int vol)
+{
+ int boost=0;
+ unsigned int ret;
+
+ if (vol > 100)
+ vol = 100;
+ if (vol > 50) {
+ vol -= 51;
+ boost=1;
+ }
+ if (boost)
+ ret=SCALE(49, 0x7e, vol)+1;
+ else
+ ret=0xff - SCALE(50, 0x7f, vol);
+ return ret;
+}
+
+static inline unsigned int eq_aci2oss(unsigned int vol)
+{
+ if (vol < 0x80)
+ return SCALE(0x7f, 50, vol) + 50;
+ else
+ return SCALE(0x7f, 50, 0xff-vol);
+}
+
+
+static int setequalizer(int __user *arg,
+ unsigned char left_index, unsigned char right_index)
+{
+ int buf;
+ unsigned int vol;
+
+ __get_user(vol, arg);
+
+ /* left channel */
+ if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0)
+ return buf;
+
+ /* right channel */
+ if ((buf=aci_write_cmd(right_index, eq_oss2aci((vol>>8) & 0xff)))<0)
+ return buf;
+
+ /* the ACI equalizer is more precise */
+ return 0;
+}
+
+static int getequalizer(int __user *arg,
+ unsigned char left_index, unsigned char right_index)
+{
+ int buf;
+ unsigned int vol;
+
+ /* left channel */
+ if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0)
+ return buf;
+ vol = eq_aci2oss(buf);
+
+ /* right channel */
+ if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0)
+ return buf;
+ vol |= eq_aci2oss(buf) << 8;
+
+ __put_user(vol, arg);
+
+ return 0;
+}
+
+static int aci_mixer_ioctl (int dev, unsigned int cmd, void __user * arg)
+{
+ int vol, buf;
+ int __user *p = arg;
+
+ switch (cmd) {
+ case SOUND_MIXER_WRITE_VOLUME:
+ return setvolume(p, 0x01, 0x00);
+ case SOUND_MIXER_WRITE_CD:
+ return setvolume(p, 0x3c, 0x34);
+ case SOUND_MIXER_WRITE_MIC:
+ return setvolume(p, 0x38, 0x30);
+ case SOUND_MIXER_WRITE_LINE:
+ return setvolume(p, 0x39, 0x31);
+ case SOUND_MIXER_WRITE_SYNTH:
+ return setvolume(p, 0x3b, 0x33);
+ case SOUND_MIXER_WRITE_PCM:
+ return setvolume(p, 0x3a, 0x32);
+ case MIXER_WRITE(SOUND_MIXER_RADIO): /* fall through */
+ case SOUND_MIXER_WRITE_LINE1: /* AUX1 or radio */
+ return setvolume(p, 0x3d, 0x35);
+ case SOUND_MIXER_WRITE_LINE2: /* AUX2 */
+ return setvolume(p, 0x3e, 0x36);
+ case SOUND_MIXER_WRITE_BASS: /* set band one and two */
+ if (aci_idcode[1]=='C') {
+ if ((buf=setequalizer(p, 0x48, 0x40)) ||
+ (buf=setequalizer(p, 0x49, 0x41)));
+ return buf;
+ }
+ break;
+ case SOUND_MIXER_WRITE_TREBLE: /* set band six and seven */
+ if (aci_idcode[1]=='C') {
+ if ((buf=setequalizer(p, 0x4d, 0x45)) ||
+ (buf=setequalizer(p, 0x4e, 0x46)));
+ return buf;
+ }
+ break;
+ case SOUND_MIXER_WRITE_IGAIN: /* MIC pre-amp */
+ if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
+ __get_user(vol, p);
+ vol = vol & 0xff;
+ if (vol > 100)
+ vol = 100;
+ vol = SCALE(100, 3, vol);
+ if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0)
+ return buf;
+ aci_micpreamp = vol;
+ vol = SCALE(3, 100, vol);
+ vol |= (vol << 8);
+ __put_user(vol, p);
+ return 0;
+ }
+ break;
+ case SOUND_MIXER_WRITE_OGAIN: /* Power-amp/line-out level */
+ if (aci_idcode[1]=='A' || aci_idcode[1]=='B') {
+ __get_user(buf, p);
+ buf = buf & 0xff;
+ if (buf > 50)
+ vol = 1;
+ else
+ vol = 0;
+ if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0)
+ return buf;
+ aci_amp = vol;
+ if (aci_amp)
+ buf = (100 || 100<<8);
+ else
+ buf = 0;
+ __put_user(buf, p);
+ return 0;
+ }
+ break;
+ case SOUND_MIXER_WRITE_RECSRC:
+ /* handle solo mode control */
+ __get_user(buf, p);
+ /* unset solo when RECSRC for PCM is requested */
+ if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
+ vol = !(buf & SOUND_MASK_PCM);
+ if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0)
+ return buf;
+ aci_solo = vol;
+ }
+ buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE|
+ SOUND_MASK_SYNTH| SOUND_MASK_LINE2);
+ if (aci_idcode[1] == 'C') /* PCM20 radio */
+ buf |= SOUND_MASK_RADIO;
+ else
+ buf |= SOUND_MASK_LINE1;
+ if (!aci_solo)
+ buf |= SOUND_MASK_PCM;
+ __put_user(buf, p);
+ return 0;
+ case SOUND_MIXER_READ_DEVMASK:
+ buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD |
+ SOUND_MASK_MIC | SOUND_MASK_LINE |
+ SOUND_MASK_SYNTH | SOUND_MASK_PCM |
+ SOUND_MASK_LINE2);
+ switch (aci_idcode[1]) {
+ case 'C': /* PCM20 radio */
+ buf |= (SOUND_MASK_RADIO | SOUND_MASK_IGAIN |
+ SOUND_MASK_BASS | SOUND_MASK_TREBLE);
+ break;
+ case 'B': /* PCM12 */
+ buf |= (SOUND_MASK_LINE1 | SOUND_MASK_IGAIN |
+ SOUND_MASK_OGAIN);
+ break;
+ case 'A': /* PCM1-pro */
+ buf |= (SOUND_MASK_LINE1 | SOUND_MASK_OGAIN);
+ break;
+ default:
+ buf |= SOUND_MASK_LINE1;
+ }
+ __put_user(buf, p);
+ return 0;
+ case SOUND_MIXER_READ_STEREODEVS:
+ buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD |
+ SOUND_MASK_MIC | SOUND_MASK_LINE |
+ SOUND_MASK_SYNTH | SOUND_MASK_PCM |
+ SOUND_MASK_LINE2);
+ switch (aci_idcode[1]) {
+ case 'C': /* PCM20 radio */
+ buf |= (SOUND_MASK_RADIO |
+ SOUND_MASK_BASS | SOUND_MASK_TREBLE);
+ break;
+ default:
+ buf |= SOUND_MASK_LINE1;
+ }
+ __put_user(buf, p);
+ return 0;
+ case SOUND_MIXER_READ_RECMASK:
+ buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE|
+ SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM);
+ if (aci_idcode[1] == 'C') /* PCM20 radio */
+ buf |= SOUND_MASK_RADIO;
+ else
+ buf |= SOUND_MASK_LINE1;
+
+ __put_user(buf, p);
+ return 0;
+ case SOUND_MIXER_READ_RECSRC:
+ buf = (SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE |
+ SOUND_MASK_SYNTH | SOUND_MASK_LINE2);
+ /* do we need aci_solo or can I get it from the ACI? */
+ switch (aci_idcode[1]) {
+ case 'B': /* PCM12 */
+ case 'C': /* PCM20 radio */
+ if (aci_version >= 0xb0) {
+ if ((vol=aci_rw_cmd(ACI_STATUS,
+ ACI_S_GENERAL, -1))<0)
+ return vol;
+ if (vol & 0x20)
+ buf |= SOUND_MASK_PCM;
+ }
+ else
+ if (!aci_solo)
+ buf |= SOUND_MASK_PCM;
+ break;
+ default:
+ buf |= SOUND_MASK_PCM;
+ }
+ if (aci_idcode[1] == 'C') /* PCM20 radio */
+ buf |= SOUND_MASK_RADIO;
+ else
+ buf |= SOUND_MASK_LINE1;
+
+ __put_user(buf, p);
+ return 0;
+ case SOUND_MIXER_READ_CAPS:
+ __put_user(0, p);
+ return 0;
+ case SOUND_MIXER_READ_VOLUME:
+ return getvolume(p, 0x04, 0x03);
+ case SOUND_MIXER_READ_CD:
+ return getvolume(p, 0x0a, 0x09);
+ case SOUND_MIXER_READ_MIC:
+ return getvolume(p, 0x06, 0x05);
+ case SOUND_MIXER_READ_LINE:
+ return getvolume(p, 0x08, 0x07);
+ case SOUND_MIXER_READ_SYNTH:
+ return getvolume(p, 0x0c, 0x0b);
+ case SOUND_MIXER_READ_PCM:
+ return getvolume(p, 0x0e, 0x0d);
+ case MIXER_READ(SOUND_MIXER_RADIO): /* fall through */
+ case SOUND_MIXER_READ_LINE1: /* AUX1 */
+ return getvolume(p, 0x11, 0x10);
+ case SOUND_MIXER_READ_LINE2: /* AUX2 */
+ return getvolume(p, 0x13, 0x12);
+ case SOUND_MIXER_READ_BASS: /* get band one */
+ if (aci_idcode[1]=='C') {
+ return getequalizer(p, 0x23, 0x22);
+ }
+ break;
+ case SOUND_MIXER_READ_TREBLE: /* get band seven */
+ if (aci_idcode[1]=='C') {
+ return getequalizer(p, 0x2f, 0x2e);
+ }
+ break;
+ case SOUND_MIXER_READ_IGAIN: /* MIC pre-amp */
+ if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
+ /* aci_micpreamp or ACI? */
+ if (aci_version >= 0xb0) {
+ if ((buf=aci_indexed_cmd(ACI_STATUS,
+ ACI_S_READ_IGAIN))<0)
+ return buf;
+ }
+ else
+ buf=aci_micpreamp;
+ vol = SCALE(3, 100, buf <= 3 ? buf : 3);
+ vol |= vol << 8;
+ __put_user(vol, p);
+ return 0;
+ }
+ break;
+ case SOUND_MIXER_READ_OGAIN:
+ if (aci_amp)
+ buf = (100 || 100<<8);
+ else
+ buf = 0;
+ __put_user(buf, p);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct mixer_operations aci_mixer_operations =
+{
+ .owner = THIS_MODULE,
+ .id = "ACI",
+ .ioctl = aci_mixer_ioctl
+};
+
+/*
+ * There is also an internal mixer in the codec (CS4231A or AD1845),
+ * that deserves no purpose in an ACI based system which uses an
+ * external ACI controlled stereo mixer. Make sure that this codec
+ * mixer has the AUX1 input selected as the recording source, that the
+ * input gain is set near maximum and that the other channels going
+ * from the inputs to the codec output are muted.
+ */
+
+static int __init attach_aci(void)
+{
+ char *boardname;
+ int i, rc = -EBUSY;
+
+ init_MUTEX(&aci_sem);
+
+ outb(0xE3, 0xf8f); /* Write MAD16 password */
+ aci_port = (inb(0xf90) & 0x10) ?
+ 0x344: 0x354; /* Get aci_port from MC4_PORT */
+
+ if (!request_region(aci_port, 3, "sound mixer (ACI)")) {
+ printk(KERN_NOTICE
+ "aci: I/O area 0x%03x-0x%03x already used.\n",
+ aci_port, aci_port+2);
+ goto out;
+ }
+
+ /* force ACI into a known state */
+ rc = -EFAULT;
+ for (i=0; i<3; i++)
+ if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0)
+ goto out_release_region;
+
+ /* official this is one aci read call: */
+ rc = -EFAULT;
+ if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 ||
+ (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) {
+ printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n",
+ aci_port);
+ goto out_release_region;
+ }
+
+ if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) {
+ printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n",
+ aci_port);
+ goto out_release_region;
+ }
+
+ if (aci_idcode[0] == 'm') {
+ /* It looks like a miro sound card. */
+ switch (aci_idcode[1]) {
+ case 'A':
+ boardname = "PCM1 pro / early PCM12";
+ break;
+ case 'B':
+ boardname = "PCM12";
+ break;
+ case 'C':
+ boardname = "PCM20 radio";
+ break;
+ default:
+ boardname = "unknown miro";
+ }
+ } else {
+ printk(KERN_WARNING "aci: Warning: unsupported card! - "
+ "no hardware, no specs...\n");
+ boardname = "unknown Cardinal Technologies";
+ }
+
+ printk(KERN_INFO "<ACI 0x%02x, id %02x/%02x \"%c/%c\", (%s)> at 0x%03x\n",
+ aci_version,
+ aci_idcode[0], aci_idcode[1],
+ aci_idcode[0], aci_idcode[1],
+ boardname, aci_port);
+
+ rc = -EBUSY;
+ if (reset) {
+ /* first write()s after reset fail with my PCM20 */
+ if (aci_rw_cmd(ACI_INIT, -1, -1)<0 ||
+ aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 ||
+ aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0)
+ goto out_release_region;
+ }
+
+ /* the PCM20 is muted after reset (and reboot) */
+ if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0)
+ goto out_release_region;
+
+ if (ide>=0)
+ if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0)
+ goto out_release_region;
+
+ if (wss>=0 && aci_idcode[1]=='A')
+ if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0)
+ goto out_release_region;
+
+ mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname,
+ &aci_mixer_operations,
+ sizeof(aci_mixer_operations), NULL);
+ rc = 0;
+ if (mixer_device < 0) {
+ printk(KERN_ERR "aci: Failed to install mixer.\n");
+ rc = mixer_device;
+ goto out_release_region;
+ } /* else Maybe initialize the CS4231A mixer here... */
+out: return rc;
+out_release_region:
+ release_region(aci_port, 3);
+ goto out;
+}
+
+static void __exit unload_aci(void)
+{
+ sound_unload_mixerdev(mixer_device);
+ release_region(aci_port, 3);
+}
+
+module_init(attach_aci);
+module_exit(unload_aci);
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/aci.h b/sound/oss/aci.h
new file mode 100644
index 000000000000..20102ee088eb
--- /dev/null
+++ b/sound/oss/aci.h
@@ -0,0 +1,57 @@
+#ifndef _ACI_H_
+#define _ACI_H_
+
+extern int aci_port;
+extern int aci_version; /* ACI firmware version */
+extern int aci_rw_cmd(int write1, int write2, int write3);
+
+#define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1)
+#define aci_write_cmd(a, b) aci_rw_cmd(a, b, -1)
+#define aci_read_cmd(a) aci_rw_cmd(a,-1, -1)
+
+#define COMMAND_REGISTER (aci_port) /* write register */
+#define STATUS_REGISTER (aci_port + 1) /* read register */
+#define BUSY_REGISTER (aci_port + 2) /* also used for rds */
+
+#define RDS_REGISTER BUSY_REGISTER
+
+#define ACI_SET_MUTE 0x0d
+#define ACI_SET_POWERAMP 0x0f
+#define ACI_SET_TUNERMUTE 0xa3
+#define ACI_SET_TUNERMONO 0xa4
+#define ACI_SET_IDE 0xd0
+#define ACI_SET_WSS 0xd1
+#define ACI_SET_SOLOMODE 0xd2
+#define ACI_WRITE_IGAIN 0x03
+#define ACI_WRITE_TUNE 0xa7
+#define ACI_READ_TUNERSTEREO 0xa8
+#define ACI_READ_TUNERSTATION 0xa9
+#define ACI_READ_VERSION 0xf1
+#define ACI_READ_IDCODE 0xf2
+#define ACI_INIT 0xff
+#define ACI_STATUS 0xf0
+#define ACI_S_GENERAL 0x00
+#define ACI_S_READ_IGAIN 0x21
+#define ACI_ERROR_OP 0xdf
+
+/*
+ * The following macro SCALE can be used to scale one integer volume
+ * value into another one using only integer arithmetic. If the input
+ * value x is in the range 0 <= x <= xmax, then the result will be in
+ * the range 0 <= SCALE(xmax,ymax,x) <= ymax.
+ *
+ * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the
+ * following nice properties:
+ *
+ * - SCALE(xmax,ymax,xmax) = ymax
+ * - SCALE(xmax,ymax,0) = 0
+ * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x)
+ *
+ * In addition, the rounding error is minimal and nicely distributed.
+ * The proofs are left as an exercise to the reader.
+ */
+
+#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax))
+
+
+#endif /* _ACI_H_ */
diff --git a/sound/oss/ad1816.c b/sound/oss/ad1816.c
new file mode 100644
index 000000000000..22dae5d0fda3
--- /dev/null
+++ b/sound/oss/ad1816.c
@@ -0,0 +1,1369 @@
+/*
+ *
+ * AD1816 lowlevel sound driver for Linux 2.6.0 and above
+ *
+ * Copyright (C) 1998-2003 by Thorsten Knabe <linux@thorsten-knabe.de>
+ *
+ * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996
+ *
+ *
+ * version: 1.5
+ * status: beta
+ * date: 2003/07/15
+ *
+ * Changes:
+ * Oleg Drokin: Some cleanup of load/unload functions. 1998/11/24
+ *
+ * Thorsten Knabe: attach and unload rewritten,
+ * some argument checks added 1998/11/30
+ *
+ * Thorsten Knabe: Buggy isa bridge workaround added 1999/01/16
+ *
+ * David Moews/Thorsten Knabe: Introduced options
+ * parameter. Added slightly modified patch from
+ * David Moews to disable dsp audio sources by setting
+ * bit 0 of options parameter. This seems to be
+ * required by some Aztech/Newcom SC-16 cards. 1999/04/18
+ *
+ * Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03
+ *
+ * Christoph Hellwig: Added isapnp support 2000/03/15
+ *
+ * Arnaldo Carvalho de Melo: get rid of check_region 2001/10/07
+ *
+ * Thorsten Knabe: Compiling with CONFIG_PNP enabled
+ * works again. It is now possible to use more than one
+ * AD1816 sound card. Sample rate now may be changed during
+ * playback/capture. printk() uses log levels everywhere.
+ * SMP fixes. DMA handling fixes.
+ * Other minor code cleanup. 2003/07/15
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#define DEBUGNOISE(x)
+
+#define CHECK_FOR_POWER { int timeout=100; \
+ while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\
+ timeout--; \
+ } \
+ if (timeout==0) {\
+ printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \
+ } \
+}
+
+/* structure to hold device specific information */
+typedef struct
+{
+ int base; /* set in attach */
+ int irq;
+ int dma_playback;
+ int dma_capture;
+
+ int opened; /* open */
+ int speed;
+ int channels;
+ int audio_format;
+ int audio_mode;
+
+ int recmask; /* setup */
+ unsigned char format_bits;
+ int supported_devices;
+ int supported_rec_devices;
+ unsigned short levels[SOUND_MIXER_NRDEVICES];
+ /* misc */
+ struct pnp_dev *pnpdev; /* configured via pnp */
+ int dev_no; /* this is the # in audio_devs and NOT
+ in ad1816_info */
+ spinlock_t lock;
+} ad1816_info;
+
+static int nr_ad1816_devs;
+static int ad1816_clockfreq = 33000;
+static int options;
+
+/* supported audio formats */
+static int ad_format_mask =
+AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW;
+
+/* array of device info structures */
+static ad1816_info dev_info[MAX_AUDIO_DEV];
+
+
+/* ------------------------------------------------------------------- */
+
+/* functions for easier access to inderect registers */
+
+static int ad_read (ad1816_info * devc, int reg)
+{
+ int result;
+
+ CHECK_FOR_POWER;
+ outb ((unsigned char) (reg & 0x3f), devc->base+0);
+ result = inb(devc->base+2);
+ result+= inb(devc->base+3)<<8;
+ return (result);
+}
+
+
+static void ad_write (ad1816_info * devc, int reg, int data)
+{
+ CHECK_FOR_POWER;
+ outb ((unsigned char) (reg & 0xff), devc->base+0);
+ outb ((unsigned char) (data & 0xff),devc->base+2);
+ outb ((unsigned char) ((data>>8)&0xff),devc->base+3);
+}
+
+/* ------------------------------------------------------------------- */
+
+/* function interface required by struct audio_driver */
+
+static void ad1816_halt_input (int dev)
+{
+ unsigned long flags;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+ unsigned char buffer;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_input called\n"));
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ if(!isa_dma_bridge_buggy) {
+ disable_dma(audio_devs[dev]->dmap_in->dma);
+ }
+
+ buffer=inb(devc->base+9);
+ if (buffer & 0x01) {
+ /* disable capture */
+ outb(buffer & ~0x01,devc->base+9);
+ }
+
+ if(!isa_dma_bridge_buggy) {
+ enable_dma(audio_devs[dev]->dmap_in->dma);
+ }
+
+ /* Clear interrupt status */
+ outb (~0x40, devc->base+1);
+
+ devc->audio_mode &= ~PCM_ENABLE_INPUT;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1816_halt_output (int dev)
+{
+ unsigned long flags;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ unsigned char buffer;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_output called!\n"));
+
+ spin_lock_irqsave(&devc->lock,flags);
+ /* Mute pcm output */
+ ad_write(devc, 4, ad_read(devc,4)|0x8080);
+
+ if(!isa_dma_bridge_buggy) {
+ disable_dma(audio_devs[dev]->dmap_out->dma);
+ }
+
+ buffer=inb(devc->base+8);
+ if (buffer & 0x01) {
+ /* disable capture */
+ outb(buffer & ~0x01,devc->base+8);
+ }
+
+ if(!isa_dma_bridge_buggy) {
+ enable_dma(audio_devs[dev]->dmap_out->dma);
+ }
+
+ /* Clear interrupt status */
+ outb ((unsigned char)~0x80, devc->base+1);
+
+ devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1816_output_block (int dev, unsigned long buf,
+ int count, int intrflag)
+{
+ unsigned long flags;
+ unsigned long cnt;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag));
+
+ cnt = count/4 - 1;
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ /* set transfer count */
+ ad_write (devc, 8, cnt & 0xffff);
+
+ devc->audio_mode |= PCM_ENABLE_OUTPUT;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+
+static void ad1816_start_input (int dev, unsigned long buf, int count,
+ int intrflag)
+{
+ unsigned long flags;
+ unsigned long cnt;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag));
+
+ cnt = count/4 - 1;
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ /* set transfer count */
+ ad_write (devc, 10, cnt & 0xffff);
+ devc->audio_mode |= PCM_ENABLE_INPUT;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int ad1816_prepare_for_input (int dev, int bsize, int bcount)
+{
+ unsigned long flags;
+ unsigned int freq;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+ unsigned char fmt_bits;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount));
+
+ spin_lock_irqsave(&devc->lock,flags);
+ fmt_bits= (devc->format_bits&0x7)<<3;
+
+ /* set mono/stereo mode */
+ if (devc->channels > 1) {
+ fmt_bits |=0x4;
+ }
+ /* set Mono/Stereo in playback/capture register */
+ outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8);
+ outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9);
+
+ freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq;
+
+ /* write playback/capture speeds */
+ ad_write (devc, 2, freq & 0xffff);
+ ad_write (devc, 3, freq & 0xffff);
+
+ spin_unlock_irqrestore(&devc->lock,flags);
+
+ ad1816_halt_input(dev);
+ return 0;
+}
+
+static int ad1816_prepare_for_output (int dev, int bsize, int bcount)
+{
+ unsigned long flags;
+ unsigned int freq;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+ unsigned char fmt_bits;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount));
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ fmt_bits= (devc->format_bits&0x7)<<3;
+ /* set mono/stereo mode */
+ if (devc->channels > 1) {
+ fmt_bits |=0x4;
+ }
+
+ /* write format bits to playback/capture registers */
+ outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8);
+ outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9);
+
+ freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq;
+
+ /* write playback/capture speeds */
+ ad_write (devc, 2, freq & 0xffff);
+ ad_write (devc, 3, freq & 0xffff);
+
+ spin_unlock_irqrestore(&devc->lock,flags);
+
+ ad1816_halt_output(dev);
+ return 0;
+
+}
+
+static void ad1816_trigger (int dev, int state)
+{
+ unsigned long flags;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base));
+
+ /* mode may have changed */
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ /* mask out modes not specified on open call */
+ state &= devc->audio_mode;
+
+ /* setup soundchip to new io-mode */
+ if (state & PCM_ENABLE_INPUT) {
+ /* enable capture */
+ outb(inb(devc->base+9)|0x01, devc->base+9);
+ } else {
+ /* disable capture */
+ outb(inb(devc->base+9)&~0x01, devc->base+9);
+ }
+
+ if (state & PCM_ENABLE_OUTPUT) {
+ /* enable playback */
+ outb(inb(devc->base+8)|0x01, devc->base+8);
+ /* unmute pcm output */
+ ad_write(devc, 4, ad_read(devc,4)&~0x8080);
+ } else {
+ /* mute pcm output */
+ ad_write(devc, 4, ad_read(devc,4)|0x8080);
+ /* disable capture */
+ outb(inb(devc->base+8)&~0x01, devc->base+8);
+ }
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+
+/* halt input & output */
+static void ad1816_halt (int dev)
+{
+ ad1816_halt_input(dev);
+ ad1816_halt_output(dev);
+}
+
+static void ad1816_reset (int dev)
+{
+ ad1816_halt (dev);
+}
+
+/* set playback speed */
+static int ad1816_set_speed (int dev, int arg)
+{
+ unsigned long flags;
+ unsigned int freq;
+ int ret;
+
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ spin_lock_irqsave(&devc->lock, flags);
+ if (arg == 0) {
+ ret = devc->speed;
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return ret;
+ }
+ /* range checking */
+ if (arg < 4000) {
+ arg = 4000;
+ }
+ if (arg > 55000) {
+ arg = 55000;
+ }
+ devc->speed = arg;
+
+ /* change speed during playback */
+ freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq;
+ /* write playback/capture speeds */
+ ad_write (devc, 2, freq & 0xffff);
+ ad_write (devc, 3, freq & 0xffff);
+
+ ret = devc->speed;
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return ret;
+
+}
+
+static unsigned int ad1816_set_bits (int dev, unsigned int arg)
+{
+ unsigned long flags;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ static struct format_tbl {
+ int format;
+ unsigned char bits;
+ } format2bits[] = {
+ { 0, 0 },
+ { AFMT_MU_LAW, 1 },
+ { AFMT_A_LAW, 3 },
+ { AFMT_IMA_ADPCM, 0 },
+ { AFMT_U8, 0 },
+ { AFMT_S16_LE, 2 },
+ { AFMT_S16_BE, 6 },
+ { AFMT_S8, 0 },
+ { AFMT_U16_LE, 0 },
+ { AFMT_U16_BE, 0 }
+ };
+
+ int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
+
+ spin_lock_irqsave(&devc->lock, flags);
+ /* return current format */
+ if (arg == 0) {
+ arg = devc->audio_format;
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return arg;
+ }
+ devc->audio_format = arg;
+
+ /* search matching format bits */
+ for (i = 0; i < n; i++)
+ if (format2bits[i].format == arg) {
+ devc->format_bits = format2bits[i].bits;
+ devc->audio_format = arg;
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return arg;
+ }
+
+ /* Still hanging here. Something must be terribly wrong */
+ devc->format_bits = 0;
+ devc->audio_format = AFMT_U8;
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return(AFMT_U8);
+}
+
+static short ad1816_set_channels (int dev, short arg)
+{
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ if (arg != 1 && arg != 2)
+ return devc->channels;
+
+ devc->channels = arg;
+ return arg;
+}
+
+/* open device */
+static int ad1816_open (int dev, int mode)
+{
+ ad1816_info *devc = NULL;
+ unsigned long flags;
+
+ /* is device number valid ? */
+ if (dev < 0 || dev >= num_audiodevs)
+ return -(ENXIO);
+
+ /* get device info of this dev */
+ devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ /* make check if device already open atomic */
+ spin_lock_irqsave(&devc->lock,flags);
+
+ if (devc->opened) {
+ spin_unlock_irqrestore(&devc->lock,flags);
+ return -(EBUSY);
+ }
+
+ /* mark device as open */
+ devc->opened = 1;
+
+ devc->audio_mode = 0;
+ devc->speed = 8000;
+ devc->audio_format=AFMT_U8;
+ devc->channels=1;
+ spin_unlock_irqrestore(&devc->lock,flags);
+ ad1816_reset(devc->dev_no); /* halt all pending output */
+ return 0;
+}
+
+static void ad1816_close (int dev) /* close device */
+{
+ unsigned long flags;
+ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+ /* halt all pending output */
+ ad1816_reset(devc->dev_no);
+
+ spin_lock_irqsave(&devc->lock,flags);
+ devc->opened = 0;
+ devc->audio_mode = 0;
+ devc->speed = 8000;
+ devc->audio_format=AFMT_U8;
+ devc->format_bits = 0;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+
+/* ------------------------------------------------------------------- */
+
+/* Audio driver structure */
+
+static struct audio_driver ad1816_audio_driver =
+{
+ .owner = THIS_MODULE,
+ .open = ad1816_open,
+ .close = ad1816_close,
+ .output_block = ad1816_output_block,
+ .start_input = ad1816_start_input,
+ .prepare_for_input = ad1816_prepare_for_input,
+ .prepare_for_output = ad1816_prepare_for_output,
+ .halt_io = ad1816_halt,
+ .halt_input = ad1816_halt_input,
+ .halt_output = ad1816_halt_output,
+ .trigger = ad1816_trigger,
+ .set_speed = ad1816_set_speed,
+ .set_bits = ad1816_set_bits,
+ .set_channels = ad1816_set_channels,
+};
+
+
+/* ------------------------------------------------------------------- */
+
+/* Interrupt handler */
+
+
+static irqreturn_t ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
+{
+ unsigned char status;
+ ad1816_info *devc = (ad1816_info *)dev_id;
+
+ if (irq < 0 || irq > 15) {
+ printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq);
+ return IRQ_NONE;
+ }
+
+ spin_lock(&devc->lock);
+
+ /* read interrupt register */
+ status = inb (devc->base+1);
+ /* Clear all interrupt */
+ outb (~status, devc->base+1);
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: Got interrupt subclass %d\n",status));
+
+ if (status == 0) {
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: interrupt: Got interrupt, but no source.\n"));
+ spin_unlock(&devc->lock);
+ return IRQ_NONE;
+ }
+
+ if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64))
+ DMAbuf_inputintr (devc->dev_no);
+
+ if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128))
+ DMAbuf_outputintr (devc->dev_no, 1);
+
+ spin_unlock(&devc->lock);
+ return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------------- */
+
+/* Mixer stuff */
+
+struct mixer_def {
+ unsigned int regno: 7;
+ unsigned int polarity:1; /* 0=normal, 1=reversed */
+ unsigned int bitpos:4;
+ unsigned int nbits:4;
+};
+
+static char mix_cvt[101] = {
+ 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+ 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
+ 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
+ 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
+ 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
+ 100
+};
+
+typedef struct mixer_def mixer_ent;
+
+/*
+ * Most of the mixer entries work in backwards. Setting the polarity field
+ * makes them to work correctly.
+ *
+ * The channel numbering used by individual soundcards is not fixed. Some
+ * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
+ * The current version doesn't try to compensate this.
+ */
+
+#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r) \
+ {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}}
+
+
+mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = {
+MIX_ENT(SOUND_MIXER_VOLUME, 14, 1, 8, 5, 14, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 5, 1, 8, 6, 5, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_PCM, 4, 1, 8, 6, 4, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 18, 1, 8, 5, 18, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_MIC, 19, 1, 8, 5, 19, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_CD, 15, 1, 8, 5, 15, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 20, 0, 8, 4, 20, 0, 0, 4),
+MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1, 17, 1, 8, 5, 17, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE2, 16, 1, 8, 5, 16, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE3, 39, 0, 9, 4, 39, 1, 0, 5)
+};
+
+
+static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x4343, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x0000, /* FM */
+ 0x4343, /* PCM */
+ 0x0000, /* PC Speaker */
+ 0x0000, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x0000, /* CD */
+ 0x0000, /* Recording monitor */
+ 0x0000, /* SB PCM */
+ 0x0000, /* Recording level */
+ 0x0000, /* Input gain */
+ 0x0000, /* Output gain */
+ 0x0000, /* Line1 */
+ 0x0000, /* Line2 */
+ 0x0000 /* Line3 (usually line in)*/
+};
+
+#define LEFT_CHN 0
+#define RIGHT_CHN 1
+
+
+
+static int
+ad1816_set_recmask (ad1816_info * devc, int mask)
+{
+ unsigned long flags;
+ unsigned char recdev;
+ int i, n;
+
+ spin_lock_irqsave(&devc->lock, flags);
+ mask &= devc->supported_rec_devices;
+
+ n = 0;
+ /* Count selected device bits */
+ for (i = 0; i < 32; i++)
+ if (mask & (1 << i))
+ n++;
+
+ if (n == 0)
+ mask = SOUND_MASK_MIC;
+ else if (n != 1) { /* Too many devices selected */
+ /* Filter out active settings */
+ mask &= ~devc->recmask;
+
+ n = 0;
+ /* Count selected device bits */
+ for (i = 0; i < 32; i++)
+ if (mask & (1 << i))
+ n++;
+
+ if (n != 1)
+ mask = SOUND_MASK_MIC;
+ }
+
+ switch (mask) {
+ case SOUND_MASK_MIC:
+ recdev = 5;
+ break;
+
+ case SOUND_MASK_LINE:
+ recdev = 0;
+ break;
+
+ case SOUND_MASK_CD:
+ recdev = 2;
+ break;
+
+ case SOUND_MASK_LINE1:
+ recdev = 4;
+ break;
+
+ case SOUND_MASK_LINE2:
+ recdev = 3;
+ break;
+
+ case SOUND_MASK_VOLUME:
+ recdev = 1;
+ break;
+
+ default:
+ mask = SOUND_MASK_MIC;
+ recdev = 5;
+ }
+
+ recdev <<= 4;
+ ad_write (devc, 20,
+ (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8));
+
+ devc->recmask = mask;
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return mask;
+}
+
+static void
+change_bits (int *regval, int dev, int chn, int newval)
+{
+ unsigned char mask;
+ int shift;
+
+ /* Reverse polarity*/
+
+ if (mix_devices[dev][chn].polarity == 1)
+ newval = 100 - newval;
+
+ mask = (1 << mix_devices[dev][chn].nbits) - 1;
+ shift = mix_devices[dev][chn].bitpos;
+ /* Scale it */
+ newval = (int) ((newval * mask) + 50) / 100;
+ /* Clear bits */
+ *regval &= ~(mask << shift);
+ /* Set new value */
+ *regval |= (newval & mask) << shift;
+}
+
+static int
+ad1816_mixer_get (ad1816_info * devc, int dev)
+{
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_get called!\n"));
+
+ /* range check + supported mixer check */
+ if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
+ return (-(EINVAL));
+ if (!((1 << dev) & devc->supported_devices))
+ return -(EINVAL);
+
+ return devc->levels[dev];
+}
+
+static int
+ad1816_mixer_set (ad1816_info * devc, int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+ int retvol;
+
+ int regoffs;
+ int val;
+ int valmute;
+ unsigned long flags;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_set called!\n"));
+
+ if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
+ return -(EINVAL);
+
+ if (left > 100)
+ left = 100;
+ if (left < 0)
+ left = 0;
+ if (right > 100)
+ right = 100;
+ if (right < 0)
+ right = 0;
+
+ /* Mono control */
+ if (mix_devices[dev][RIGHT_CHN].nbits == 0)
+ right = left;
+ retvol = left | (right << 8);
+
+ /* Scale it */
+
+ left = mix_cvt[left];
+ right = mix_cvt[right];
+
+ /* reject all mixers that are not supported */
+ if (!(devc->supported_devices & (1 << dev)))
+ return -(EINVAL);
+
+ /* sanity check */
+ if (mix_devices[dev][LEFT_CHN].nbits == 0)
+ return -(EINVAL);
+ spin_lock_irqsave(&devc->lock, flags);
+
+ /* keep precise volume internal */
+ devc->levels[dev] = retvol;
+
+ /* Set the left channel */
+ regoffs = mix_devices[dev][LEFT_CHN].regno;
+ val = ad_read (devc, regoffs);
+ change_bits (&val, dev, LEFT_CHN, left);
+
+ valmute=val;
+
+ /* Mute bit masking on some registers */
+ if ( regoffs==5 || regoffs==14 || regoffs==15 ||
+ regoffs==16 || regoffs==17 || regoffs==18 ||
+ regoffs==19 || regoffs==39) {
+ if (left==0)
+ valmute |= 0x8000;
+ else
+ valmute &= ~0x8000;
+ }
+ ad_write (devc, regoffs, valmute); /* mute */
+
+ /*
+ * Set the right channel
+ */
+
+ /* Was just a mono channel */
+ if (mix_devices[dev][RIGHT_CHN].nbits == 0) {
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return retvol;
+ }
+
+ regoffs = mix_devices[dev][RIGHT_CHN].regno;
+ val = ad_read (devc, regoffs);
+ change_bits (&val, dev, RIGHT_CHN, right);
+
+ valmute=val;
+ if ( regoffs==5 || regoffs==14 || regoffs==15 ||
+ regoffs==16 || regoffs==17 || regoffs==18 ||
+ regoffs==19 || regoffs==39) {
+ if (right==0)
+ valmute |= 0x80;
+ else
+ valmute &= ~0x80;
+ }
+ ad_write (devc, regoffs, valmute); /* mute */
+ spin_unlock_irqrestore(&devc->lock, flags);
+ return retvol;
+}
+
+#define MIXER_DEVICES ( SOUND_MASK_VOLUME | \
+ SOUND_MASK_SYNTH | \
+ SOUND_MASK_PCM | \
+ SOUND_MASK_LINE | \
+ SOUND_MASK_LINE1 | \
+ SOUND_MASK_LINE2 | \
+ SOUND_MASK_LINE3 | \
+ SOUND_MASK_MIC | \
+ SOUND_MASK_CD | \
+ SOUND_MASK_RECLEV \
+ )
+#define REC_DEVICES ( SOUND_MASK_LINE2 |\
+ SOUND_MASK_LINE |\
+ SOUND_MASK_LINE1 |\
+ SOUND_MASK_MIC |\
+ SOUND_MASK_CD |\
+ SOUND_MASK_VOLUME \
+ )
+
+static void
+ad1816_mixer_reset (ad1816_info * devc)
+{
+ int i;
+
+ devc->supported_devices = MIXER_DEVICES;
+
+ devc->supported_rec_devices = REC_DEVICES;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (devc->supported_devices & (1 << i))
+ ad1816_mixer_set (devc, i, default_mixer_levels[i]);
+ ad1816_set_recmask (devc, SOUND_MASK_MIC);
+}
+
+static int
+ad1816_mixer_ioctl (int dev, unsigned int cmd, void __user * arg)
+{
+ ad1816_info *devc = mixer_devs[dev]->devc;
+ int val;
+ int __user *p = arg;
+
+ DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_ioctl called!\n"));
+
+ /* Mixer ioctl */
+ if (((cmd >> 8) & 0xff) == 'M') {
+
+ /* set ioctl */
+ if (_SIOC_DIR (cmd) & _SIOC_WRITE) {
+ switch (cmd & 0xff){
+ case SOUND_MIXER_RECSRC:
+
+ if (get_user(val, p))
+ return -EFAULT;
+ val=ad1816_set_recmask (devc, val);
+ return put_user(val, p);
+ break;
+
+ default:
+ if (get_user(val, p))
+ return -EFAULT;
+ if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0)
+ return val;
+ else
+ return put_user(val, p);
+ }
+ } else {
+ /* read ioctl */
+ switch (cmd & 0xff) {
+
+ case SOUND_MIXER_RECSRC:
+ val=devc->recmask;
+ return put_user(val, p);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ val=devc->supported_devices;
+ return put_user(val, p);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+ return put_user(val, p);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ val=devc->supported_rec_devices;
+ return put_user(val, p);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ val=SOUND_CAP_EXCL_INPUT;
+ return put_user(val, p);
+ break;
+
+ default:
+ if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0)
+ return val;
+ else
+ return put_user(val, p);
+ }
+ }
+ } else
+ /* not for mixer */
+ return -(EINVAL);
+}
+
+/* ------------------------------------------------------------------- */
+
+/* Mixer structure */
+
+static struct mixer_operations ad1816_mixer_operations = {
+ .owner = THIS_MODULE,
+ .id = "AD1816",
+ .name = "AD1816 Mixer",
+ .ioctl = ad1816_mixer_ioctl
+};
+
+
+/* ------------------------------------------------------------------- */
+
+/* stuff for card recognition, init and unloading PNP ...*/
+
+
+/* check if AD1816 present at specified hw_config and register device with OS
+ * return 1 if initialization was successful, 0 otherwise
+ */
+static int __init ad1816_init_card (struct address_info *hw_config,
+ struct pnp_dev *pnp)
+{
+ ad1816_info *devc = NULL;
+ int tmp;
+ int oss_devno = -1;
+
+ printk(KERN_INFO "ad1816: initializing card: io=0x%x, irq=%d, dma=%d, "
+ "dma2=%d, clockfreq=%d, options=%d isadmabug=%d "
+ "%s\n",
+ hw_config->io_base,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma2,
+ ad1816_clockfreq,
+ options,
+ isa_dma_bridge_buggy,
+ pnp?"(PNP)":"");
+
+ /* ad1816_info structure remaining ? */
+ if (nr_ad1816_devs >= MAX_AUDIO_DEV) {
+ printk(KERN_WARNING "ad1816: no more ad1816_info structures "
+ "left\n");
+ goto out;
+ }
+
+ devc = &dev_info[nr_ad1816_devs];
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->dma_playback=hw_config->dma;
+ devc->dma_capture=hw_config->dma2;
+ devc->opened = 0;
+ devc->pnpdev = pnp;
+ spin_lock_init(&devc->lock);
+
+ if (!request_region(devc->base, 16, "AD1816 Sound")) {
+ printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n",
+ devc->base);
+ goto out;
+ }
+
+ printk(KERN_INFO "ad1816: Examining AD1816 at address 0x%03x.\n",
+ devc->base);
+
+
+ /* tests for ad1816 */
+ /* base+0: bit 1 must be set but not 255 */
+ tmp=inb(devc->base);
+ if ( (tmp&0x80)==0 || tmp==255 ) {
+ printk (KERN_INFO "ad1816: Chip is not an AD1816 or chip "
+ "is not active (Test 0)\n");
+ goto out_release_region;
+ }
+
+ /* writes to ireg 8 are copied to ireg 9 */
+ ad_write(devc,8,12345);
+ if (ad_read(devc,9)!=12345) {
+ printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 1)\n");
+ goto out_release_region;
+ }
+
+ /* writes to ireg 8 are copied to ireg 9 */
+ ad_write(devc,8,54321);
+ if (ad_read(devc,9)!=54321) {
+ printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 2)\n");
+ goto out_release_region;
+ }
+
+ /* writes to ireg 10 are copied to ireg 11 */
+ ad_write(devc,10,54321);
+ if (ad_read(devc,11)!=54321) {
+ printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 3)\n");
+ goto out_release_region;
+ }
+
+ /* writes to ireg 10 are copied to ireg 11 */
+ ad_write(devc,10,12345);
+ if (ad_read(devc,11)!=12345) {
+ printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 4)\n");
+ goto out_release_region;
+ }
+
+ /* bit in base +1 cannot be set to 1 */
+ tmp=inb(devc->base+1);
+ outb(0xff,devc->base+1);
+ if (inb(devc->base+1)!=tmp) {
+ printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 5)\n");
+ goto out_release_region;
+ }
+
+ printk(KERN_INFO "ad1816: AD1816 (version %d) successfully detected!\n",
+ ad_read(devc,45));
+
+ /* disable all interrupts */
+ ad_write(devc,1,0);
+
+ /* Clear pending interrupts */
+ outb (0, devc->base+1);
+
+ /* allocate irq */
+ if (devc->irq < 0 || devc->irq > 15)
+ goto out_release_region;
+ if (request_irq(devc->irq, ad1816_interrupt,0,
+ "SoundPort", devc) < 0) {
+ printk(KERN_WARNING "ad1816: IRQ in use\n");
+ goto out_release_region;
+ }
+
+ /* DMA stuff */
+ if (sound_alloc_dma (devc->dma_playback, "Sound System")) {
+ printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n",
+ devc->dma_playback);
+ goto out_free_irq;
+ }
+
+ if ( devc->dma_capture >= 0 &&
+ devc->dma_capture != devc->dma_playback) {
+ if (sound_alloc_dma(devc->dma_capture,
+ "Sound System (capture)")) {
+ printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n",
+ devc->dma_capture);
+ goto out_free_dma;
+ }
+ devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX;
+ } else {
+ printk(KERN_WARNING "ad1816: Only one DMA channel "
+ "available/configured. No duplex operation possible\n");
+ devc->audio_mode=DMA_AUTOMODE;
+ }
+
+ conf_printf2 ("AD1816 audio driver",
+ devc->base, devc->irq, devc->dma_playback,
+ devc->dma_capture);
+
+ /* register device */
+ if ((oss_devno = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
+ "AD1816 audio driver",
+ &ad1816_audio_driver,
+ sizeof (struct audio_driver),
+ devc->audio_mode,
+ ad_format_mask,
+ devc,
+ devc->dma_playback,
+ devc->dma_capture)) < 0) {
+ printk(KERN_WARNING "ad1816: Can't install sound driver\n");
+ goto out_free_dma_2;
+ }
+
+
+ ad_write(devc,32,0x80f0); /* sound system mode */
+ if (options&1) {
+ ad_write(devc,33,0); /* disable all audiosources for dsp */
+ } else {
+ ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */
+ }
+ ad_write(devc,4,0x8080); /* default values for volumes (muted)*/
+ ad_write(devc,5,0x8080);
+ ad_write(devc,6,0x8080);
+ ad_write(devc,7,0x8080);
+ ad_write(devc,15,0x8888);
+ ad_write(devc,16,0x8888);
+ ad_write(devc,17,0x8888);
+ ad_write(devc,18,0x8888);
+ ad_write(devc,19,0xc888); /* +20db mic active */
+ ad_write(devc,14,0x0000); /* Master volume unmuted */
+ ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */
+ ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */
+ outb(0x10,devc->base+8); /* set dma mode */
+ outb(0x10,devc->base+9);
+
+ /* enable capture + playback interrupt */
+ ad_write(devc,1,0xc000);
+
+ /* set mixer defaults */
+ ad1816_mixer_reset (devc);
+
+ /* register mixer */
+ if ((audio_devs[oss_devno]->mixer_dev=sound_install_mixer(
+ MIXER_DRIVER_VERSION,
+ "AD1816 audio driver",
+ &ad1816_mixer_operations,
+ sizeof (struct mixer_operations),
+ devc)) < 0) {
+ printk(KERN_WARNING "Can't install mixer\n");
+ }
+ /* make ad1816_info active */
+ nr_ad1816_devs++;
+ printk(KERN_INFO "ad1816: card successfully installed!\n");
+ return 1;
+ /* error handling */
+out_free_dma_2:
+ if (devc->dma_capture >= 0 && devc->dma_capture != devc->dma_playback)
+ sound_free_dma(devc->dma_capture);
+out_free_dma:
+ sound_free_dma(devc->dma_playback);
+out_free_irq:
+ free_irq(devc->irq, devc);
+out_release_region:
+ release_region(devc->base, 16);
+out:
+ return 0;
+}
+
+static void __exit unload_card(ad1816_info *devc)
+{
+ int mixer, dev = 0;
+
+ if (devc != NULL) {
+ printk("ad1816: Unloading card at address 0x%03x\n",devc->base);
+
+ dev = devc->dev_no;
+ mixer = audio_devs[dev]->mixer_dev;
+
+ /* unreg mixer*/
+ if(mixer>=0) {
+ sound_unload_mixerdev(mixer);
+ }
+ /* unreg audiodev */
+ sound_unload_audiodev(dev);
+
+ /* free dma channels */
+ if (devc->dma_capture>=0 &&
+ devc->dma_capture != devc->dma_playback) {
+ sound_free_dma(devc->dma_capture);
+ }
+ sound_free_dma (devc->dma_playback);
+ /* free irq */
+ free_irq(devc->irq, devc);
+ /* free io */
+ release_region (devc->base, 16);
+#ifdef __ISAPNP__
+ if (devc->pnpdev) {
+ pnp_disable_dev(devc->pnpdev);
+ pnp_device_detach(devc->pnpdev);
+ }
+#endif
+
+ } else
+ printk(KERN_WARNING "ad1816: no device/card specified\n");
+}
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata dma2 = -1;
+
+#ifdef __ISAPNP__
+/* use isapnp for configuration */
+static int isapnp = 1;
+static int isapnpjump;
+module_param(isapnp, bool, 0);
+module_param(isapnpjump, int, 0);
+#endif
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(dma, int, 0);
+module_param(dma2, int, 0);
+module_param(ad1816_clockfreq, int, 0);
+module_param(options, int, 0);
+
+#ifdef __ISAPNP__
+static struct {
+ unsigned short card_vendor, card_device;
+ unsigned short vendor;
+ unsigned short function;
+ struct ad1816_data *data;
+} isapnp_ad1816_list[] __initdata = {
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150),
+ NULL },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180),
+ NULL },
+ {0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list);
+
+
+static void __init ad1816_config_pnp_card(struct pnp_card *card,
+ unsigned short vendor,
+ unsigned short function)
+{
+ struct address_info cfg;
+ struct pnp_dev *card_dev = pnp_find_dev(card, vendor, function, NULL);
+ if (!card_dev) return;
+ if (pnp_device_attach(card_dev) < 0) {
+ printk(KERN_WARNING "ad1816: Failed to attach PnP device\n");
+ return;
+ }
+ if (pnp_activate_dev(card_dev) < 0) {
+ printk(KERN_WARNING "ad1816: Failed to activate PnP device\n");
+ pnp_device_detach(card_dev);
+ return;
+ }
+ cfg.io_base = pnp_port_start(card_dev, 2);
+ cfg.irq = pnp_irq(card_dev, 0);
+ cfg.dma = pnp_irq(card_dev, 0);
+ cfg.dma2 = pnp_irq(card_dev, 1);
+ if (!ad1816_init_card(&cfg, card_dev)) {
+ pnp_disable_dev(card_dev);
+ pnp_device_detach(card_dev);
+ }
+}
+
+static void __init ad1816_config_pnp_cards(void)
+{
+ int nr_pnp_cfg;
+ int i;
+
+ /* Count entries in isapnp_ad1816_list */
+ for (nr_pnp_cfg = 0; isapnp_ad1816_list[nr_pnp_cfg].card_vendor != 0;
+ nr_pnp_cfg++);
+ /* Check and adjust isapnpjump */
+ if( isapnpjump < 0 || isapnpjump >= nr_pnp_cfg) {
+ printk(KERN_WARNING
+ "ad1816: Valid range for isapnpjump is 0-%d. "
+ "Adjusted to 0.\n", nr_pnp_cfg-1);
+ isapnpjump = 0;
+ }
+ for (i = isapnpjump; isapnp_ad1816_list[i].card_vendor != 0; i++) {
+ struct pnp_card *card = NULL;
+ /* iterate over all pnp cards */
+ while ((card = pnp_find_card(isapnp_ad1816_list[i].card_vendor,
+ isapnp_ad1816_list[i].card_device, card)))
+ ad1816_config_pnp_card(card,
+ isapnp_ad1816_list[i].vendor,
+ isapnp_ad1816_list[i].function);
+ }
+}
+#endif
+
+/* module initialization */
+static int __init init_ad1816(void)
+{
+ printk(KERN_INFO "ad1816: AD1816 sounddriver "
+ "Copyright (C) 1998-2003 by Thorsten Knabe and "
+ "others\n");
+#ifdef AD1816_CLOCK
+ /* set ad1816_clockfreq if set during compilation */
+ ad1816_clockfreq=AD1816_CLOCK;
+#endif
+ if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) {
+ ad1816_clockfreq=33000;
+ }
+
+#ifdef __ISAPNP__
+ /* configure PnP cards */
+ if(isapnp) ad1816_config_pnp_cards();
+#endif
+ /* configure card by module params */
+ if (io != -1 && irq != -1 && dma != -1) {
+ struct address_info cfg;
+ cfg.io_base = io;
+ cfg.irq = irq;
+ cfg.dma = dma;
+ cfg.dma2 = dma2;
+ ad1816_init_card(&cfg, NULL);
+ }
+ if (nr_ad1816_devs <= 0)
+ return -ENODEV;
+ return 0;
+}
+
+/* module cleanup */
+static void __exit cleanup_ad1816 (void)
+{
+ int i;
+ ad1816_info *devc = NULL;
+
+ /* remove any soundcard */
+ for (i = 0; i < nr_ad1816_devs; i++) {
+ devc = &dev_info[i];
+ unload_card(devc);
+ }
+ nr_ad1816_devs=0;
+ printk(KERN_INFO "ad1816: driver unloaded!\n");
+}
+
+module_init(init_ad1816);
+module_exit(cleanup_ad1816);
+
+#ifndef MODULE
+/* kernel command line parameter evaluation */
+static int __init setup_ad1816(char *str)
+{
+ /* io, irq, dma, dma2 */
+ int ints[5];
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ io = ints[1];
+ irq = ints[2];
+ dma = ints[3];
+ dma2 = ints[4];
+ return 1;
+}
+
+__setup("ad1816=", setup_ad1816);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
new file mode 100644
index 000000000000..4384dac3f794
--- /dev/null
+++ b/sound/oss/ad1848.c
@@ -0,0 +1,3159 @@
+/*
+ * sound/ad1848.c
+ *
+ * The low level driver for the AD1848/CS4248 codec chip which
+ * is used for example in the MS Sound System.
+ *
+ * The CS4231 which is used in the GUS MAX and some other cards is
+ * upwards compatible with AD1848 and this driver is able to drive it.
+ *
+ * CS4231A and AD1845 are upward compatible with CS4231. However
+ * the new features of these chips are different.
+ *
+ * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU).
+ * CS4232A is an improved version of CS4232.
+ *
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
+ * general sleep/wakeup clean up.
+ * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free
+ * of irqs. Use dev_id.
+ * Christoph Hellwig : adapted to module_init/module_exit
+ * Aki Laukkanen : added power management support
+ * Arnaldo C. de Melo : added missing restore_flags in ad1848_resume
+ * Miguel Freitas : added ISA PnP support
+ * Alan Cox : Added CS4236->4239 identification
+ * Daniel T. Cobra : Alernate config/mixer for later chips
+ * Alan Cox : Merged chip idents and config code
+ *
+ * TODO
+ * APM save restore assist code on IBM thinkpad
+ *
+ * Status:
+ * Tested. Believed fully functional.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/pm.h>
+#include <linux/isapnp.h>
+#include <linux/pnp.h>
+#include <linux/spinlock.h>
+
+#define DEB(x)
+#define DEB1(x)
+#include "sound_config.h"
+
+#include "ad1848.h"
+#include "ad1848_mixer.h"
+
+typedef struct
+{
+ spinlock_t lock;
+ int base;
+ int irq;
+ int dma1, dma2;
+ int dual_dma; /* 1, when two DMA channels allocated */
+ int subtype;
+ unsigned char MCE_bit;
+ unsigned char saved_regs[64]; /* Includes extended register space */
+ int debug_flag;
+
+ int audio_flags;
+ int record_dev, playback_dev;
+
+ int xfer_count;
+ int audio_mode;
+ int open_mode;
+ int intr_active;
+ char *chip_name, *name;
+ int model;
+#define MD_1848 1
+#define MD_4231 2
+#define MD_4231A 3
+#define MD_1845 4
+#define MD_4232 5
+#define MD_C930 6
+#define MD_IWAVE 7
+#define MD_4235 8 /* Crystal Audio CS4235 */
+#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/
+#define MD_4236 10 /* 4236 and higher */
+#define MD_42xB 11 /* CS 42xB */
+#define MD_4239 12 /* CS4239 */
+
+ /* Mixer parameters */
+ int recmask;
+ int supported_devices, orig_devices;
+ int supported_rec_devices, orig_rec_devices;
+ int *levels;
+ short mixer_reroute[32];
+ int dev_no;
+ volatile unsigned long timer_ticks;
+ int timer_running;
+ int irq_ok;
+ mixer_ents *mix_devices;
+ int mixer_output_port;
+
+ /* Power management */
+ struct pm_dev *pmdev;
+} ad1848_info;
+
+typedef struct ad1848_port_info
+{
+ int open_mode;
+ int speed;
+ unsigned char speed_bits;
+ int channels;
+ int audio_format;
+ unsigned char format_bits;
+}
+ad1848_port_info;
+
+static struct address_info cfg;
+static int nr_ad1848_devs;
+
+static int deskpro_xl;
+static int deskpro_m;
+static int soundpro;
+
+static volatile signed char irq2dev[17] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+#ifndef EXCLUDE_TIMERS
+static int timer_installed = -1;
+#endif
+
+static int loaded;
+
+static int ad_format_mask[13 /*devc->model */ ] =
+{
+ 0,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE /* CS4235 */,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM
+};
+
+static ad1848_info adev_info[MAX_AUDIO_DEV];
+
+#define io_Index_Addr(d) ((d)->base)
+#define io_Indexed_Data(d) ((d)->base+1)
+#define io_Status(d) ((d)->base+2)
+#define io_Polled_IO(d) ((d)->base+3)
+
+static struct {
+ unsigned char flags;
+#define CAP_F_TIMER 0x01
+} capabilities [10 /*devc->model */ ] = {
+ {0}
+ ,{0} /* MD_1848 */
+ ,{CAP_F_TIMER} /* MD_4231 */
+ ,{CAP_F_TIMER} /* MD_4231A */
+ ,{CAP_F_TIMER} /* MD_1845 */
+ ,{CAP_F_TIMER} /* MD_4232 */
+ ,{0} /* MD_C930 */
+ ,{CAP_F_TIMER} /* MD_IWAVE */
+ ,{0} /* MD_4235 */
+ ,{CAP_F_TIMER} /* MD_1845_SSCAPE */
+};
+
+#ifdef CONFIG_PNP
+static int isapnp = 1;
+static int isapnpjump;
+static int reverse;
+
+static int audio_activated;
+#else
+static int isapnp;
+#endif
+
+
+
+static int ad1848_open(int dev, int mode);
+static void ad1848_close(int dev);
+static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag);
+static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag);
+static int ad1848_prepare_for_output(int dev, int bsize, int bcount);
+static int ad1848_prepare_for_input(int dev, int bsize, int bcount);
+static void ad1848_halt(int dev);
+static void ad1848_halt_input(int dev);
+static void ad1848_halt_output(int dev);
+static void ad1848_trigger(int dev, int bits);
+static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+#ifndef EXCLUDE_TIMERS
+static int ad1848_tmr_install(int dev);
+static void ad1848_tmr_reprogram(int dev);
+#endif
+
+static int ad_read(ad1848_info * devc, int reg)
+{
+ int x;
+ int timeout = 900000;
+
+ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ if(reg < 32)
+ {
+ outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+ x = inb(io_Indexed_Data(devc));
+ }
+ else
+ {
+ int xreg, xra;
+
+ xreg = (reg & 0xff) - 32;
+ xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
+ outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+ outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
+ x = inb(io_Indexed_Data(devc));
+ }
+
+ return x;
+}
+
+static void ad_write(ad1848_info * devc, int reg, int data)
+{
+ int timeout = 900000;
+
+ while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */
+ timeout--;
+
+ if(reg < 32)
+ {
+ outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+ outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc));
+ }
+ else
+ {
+ int xreg, xra;
+
+ xreg = (reg & 0xff) - 32;
+ xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
+ outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+ outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
+ outb((unsigned char) (data & 0xff), io_Indexed_Data(devc));
+ }
+}
+
+static void wait_for_calibration(ad1848_info * devc)
+{
+ int timeout = 0;
+
+ /*
+ * Wait until the auto calibration process has finished.
+ *
+ * 1) Wait until the chip becomes ready (reads don't return 0x80).
+ * 2) Wait until the ACI bit of I11 gets on and then off.
+ */
+
+ timeout = 100000;
+ while (timeout > 0 && inb(devc->base) == 0x80)
+ timeout--;
+ if (inb(devc->base) & 0x80)
+ printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n");
+
+ timeout = 100;
+ while (timeout > 0 && !(ad_read(devc, 11) & 0x20))
+ timeout--;
+ if (!(ad_read(devc, 11) & 0x20))
+ return;
+
+ timeout = 80000;
+ while (timeout > 0 && (ad_read(devc, 11) & 0x20))
+ timeout--;
+ if (ad_read(devc, 11) & 0x20)
+ if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE))
+ printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n");
+}
+
+static void ad_mute(ad1848_info * devc)
+{
+ int i;
+ unsigned char prev;
+
+ /*
+ * Save old register settings and mute output channels
+ */
+
+ for (i = 6; i < 8; i++)
+ {
+ prev = devc->saved_regs[i] = ad_read(devc, i);
+ }
+
+}
+
+static void ad_unmute(ad1848_info * devc)
+{
+}
+
+static void ad_enter_MCE(ad1848_info * devc)
+{
+ int timeout = 1000;
+ unsigned short prev;
+
+ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ devc->MCE_bit = 0x40;
+ prev = inb(io_Index_Addr(devc));
+ if (prev & 0x40)
+ {
+ return;
+ }
+ outb((devc->MCE_bit), io_Index_Addr(devc));
+}
+
+static void ad_leave_MCE(ad1848_info * devc)
+{
+ unsigned char prev, acal;
+ int timeout = 1000;
+
+ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ acal = ad_read(devc, 9);
+
+ devc->MCE_bit = 0x00;
+ prev = inb(io_Index_Addr(devc));
+ outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
+
+ if ((prev & 0x40) == 0) /* Not in MCE mode */
+ {
+ return;
+ }
+ outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
+ if (acal & 0x08) /* Auto calibration is enabled */
+ wait_for_calibration(devc);
+}
+
+static int ad1848_set_recmask(ad1848_info * devc, int mask)
+{
+ unsigned char recdev;
+ int i, n;
+ unsigned long flags;
+
+ mask &= devc->supported_rec_devices;
+
+ /* Rename the mixer bits if necessary */
+ for (i = 0; i < 32; i++)
+ {
+ if (devc->mixer_reroute[i] != i)
+ {
+ if (mask & (1 << i))
+ {
+ mask &= ~(1 << i);
+ mask |= (1 << devc->mixer_reroute[i]);
+ }
+ }
+ }
+
+ n = 0;
+ for (i = 0; i < 32; i++) /* Count selected device bits */
+ if (mask & (1 << i))
+ n++;
+
+ spin_lock_irqsave(&devc->lock,flags);
+ if (!soundpro) {
+ if (n == 0)
+ mask = SOUND_MASK_MIC;
+ else if (n != 1) { /* Too many devices selected */
+ mask &= ~devc->recmask; /* Filter out active settings */
+
+ n = 0;
+ for (i = 0; i < 32; i++) /* Count selected device bits */
+ if (mask & (1 << i))
+ n++;
+
+ if (n != 1)
+ mask = SOUND_MASK_MIC;
+ }
+ switch (mask) {
+ case SOUND_MASK_MIC:
+ recdev = 2;
+ break;
+
+ case SOUND_MASK_LINE:
+ case SOUND_MASK_LINE3:
+ recdev = 0;
+ break;
+
+ case SOUND_MASK_CD:
+ case SOUND_MASK_LINE1:
+ recdev = 1;
+ break;
+
+ case SOUND_MASK_IMIX:
+ recdev = 3;
+ break;
+
+ default:
+ mask = SOUND_MASK_MIC;
+ recdev = 2;
+ }
+
+ recdev <<= 6;
+ ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
+ ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev);
+ } else { /* soundpro */
+ unsigned char val;
+ int set_rec_bit;
+ int j;
+
+ for (i = 0; i < 32; i++) { /* For each bit */
+ if ((devc->supported_rec_devices & (1 << i)) == 0)
+ continue; /* Device not supported */
+
+ for (j = LEFT_CHN; j <= RIGHT_CHN; j++) {
+ if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */
+ continue;
+
+ /*
+ * This is tricky:
+ * set_rec_bit becomes 1 if the corresponding bit in mask is set
+ * then it gets flipped if the polarity is inverse
+ */
+ set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol;
+
+ val = ad_read(devc, devc->mix_devices[i][j].recreg);
+ val &= ~(1 << devc->mix_devices[i][j].recpos);
+ val |= (set_rec_bit << devc->mix_devices[i][j].recpos);
+ ad_write(devc, devc->mix_devices[i][j].recreg, val);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&devc->lock,flags);
+
+ /* Rename the mixer bits back if necessary */
+ for (i = 0; i < 32; i++)
+ {
+ if (devc->mixer_reroute[i] != i)
+ {
+ if (mask & (1 << devc->mixer_reroute[i]))
+ {
+ mask &= ~(1 << devc->mixer_reroute[i]);
+ mask |= (1 << i);
+ }
+ }
+ }
+ devc->recmask = mask;
+ return mask;
+}
+
+static void change_bits(ad1848_info * devc, unsigned char *regval,
+ unsigned char *muteval, int dev, int chn, int newval)
+{
+ unsigned char mask;
+ int shift;
+ int mute;
+ int mutemask;
+ int set_mute_bit;
+
+ set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol;
+
+ if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */
+ newval = 100 - newval;
+
+ mask = (1 << devc->mix_devices[dev][chn].nbits) - 1;
+ shift = devc->mix_devices[dev][chn].bitpos;
+
+ if (devc->mix_devices[dev][chn].mutepos == 8)
+ { /* if there is no mute bit */
+ mute = 0; /* No mute bit; do nothing special */
+ mutemask = ~0; /* No mute bit; do nothing special */
+ }
+ else
+ {
+ mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
+ mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
+ }
+
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
+ *regval &= ~(mask << shift); /* Clear bits */
+ *regval |= (newval & mask) << shift; /* Set new value */
+
+ *muteval &= mutemask;
+ *muteval |= mute;
+}
+
+static int ad1848_mixer_get(ad1848_info * devc, int dev)
+{
+ if (!((1 << dev) & devc->supported_devices))
+ return -EINVAL;
+
+ dev = devc->mixer_reroute[dev];
+
+ return devc->levels[dev];
+}
+
+static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel)
+{
+ int regoffs, muteregoffs;
+ unsigned char val, muteval;
+ unsigned long flags;
+
+ regoffs = devc->mix_devices[dev][channel].regno;
+ muteregoffs = devc->mix_devices[dev][channel].mutereg;
+ val = ad_read(devc, regoffs);
+
+ if (muteregoffs != regoffs) {
+ muteval = ad_read(devc, muteregoffs);
+ change_bits(devc, &val, &muteval, dev, channel, value);
+ }
+ else
+ change_bits(devc, &val, &val, dev, channel, value);
+
+ spin_lock_irqsave(&devc->lock,flags);
+ ad_write(devc, regoffs, val);
+ devc->saved_regs[regoffs] = val;
+ if (muteregoffs != regoffs) {
+ ad_write(devc, muteregoffs, muteval);
+ devc->saved_regs[muteregoffs] = muteval;
+ }
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int ad1848_mixer_set(ad1848_info * devc, int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+ int retvol;
+
+ if (dev > 31)
+ return -EINVAL;
+
+ if (!(devc->supported_devices & (1 << dev)))
+ return -EINVAL;
+
+ dev = devc->mixer_reroute[dev];
+
+ if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
+ return -EINVAL;
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */
+ right = left;
+
+ retvol = left | (right << 8);
+
+ /* Scale volumes */
+ left = mix_cvt[left];
+ right = mix_cvt[right];
+
+ devc->levels[dev] = retvol;
+
+ /*
+ * Set the left channel
+ */
+ ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN);
+
+ /*
+ * Set the right channel
+ */
+ if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
+ goto out;
+ ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN);
+
+ out:
+ return retvol;
+}
+
+static void ad1848_mixer_reset(ad1848_info * devc)
+{
+ int i;
+ char name[32];
+ unsigned long flags;
+
+ devc->mix_devices = &(ad1848_mix_devices[0]);
+
+ sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs);
+
+ for (i = 0; i < 32; i++)
+ devc->mixer_reroute[i] = i;
+
+ devc->supported_rec_devices = MODE1_REC_DEVICES;
+
+ switch (devc->model)
+ {
+ case MD_4231:
+ case MD_4231A:
+ case MD_1845:
+ case MD_1845_SSCAPE:
+ devc->supported_devices = MODE2_MIXER_DEVICES;
+ break;
+
+ case MD_C930:
+ devc->supported_devices = C930_MIXER_DEVICES;
+ devc->mix_devices = &(c930_mix_devices[0]);
+ break;
+
+ case MD_IWAVE:
+ devc->supported_devices = MODE3_MIXER_DEVICES;
+ devc->mix_devices = &(iwave_mix_devices[0]);
+ break;
+
+ case MD_42xB:
+ case MD_4239:
+ devc->mix_devices = &(cs42xb_mix_devices[0]);
+ devc->supported_devices = MODE3_MIXER_DEVICES;
+ break;
+ case MD_4232:
+ case MD_4235:
+ case MD_4236:
+ devc->supported_devices = MODE3_MIXER_DEVICES;
+ break;
+
+ case MD_1848:
+ if (soundpro) {
+ devc->supported_devices = SPRO_MIXER_DEVICES;
+ devc->supported_rec_devices = SPRO_REC_DEVICES;
+ devc->mix_devices = &(spro_mix_devices[0]);
+ break;
+ }
+
+ default:
+ devc->supported_devices = MODE1_MIXER_DEVICES;
+ }
+
+ devc->orig_devices = devc->supported_devices;
+ devc->orig_rec_devices = devc->supported_rec_devices;
+
+ devc->levels = load_mixer_volumes(name, default_mixer_levels, 1);
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ {
+ if (devc->supported_devices & (1 << i))
+ ad1848_mixer_set(devc, i, devc->levels[i]);
+ }
+
+ ad1848_set_recmask(devc, SOUND_MASK_MIC);
+
+ devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
+
+ spin_lock_irqsave(&devc->lock,flags);
+ if (!soundpro) {
+ if (devc->mixer_output_port & AUDIO_SPEAKER)
+ ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
+ else
+ ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
+ } else {
+ /*
+ * From the "wouldn't it be nice if the mixer API had (better)
+ * support for custom stuff" category
+ */
+ /* Enable surround mode and SB16 mixer */
+ ad_write(devc, 16, 0x60);
+ }
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int ad1848_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+ ad1848_info *devc = mixer_devs[dev]->devc;
+ int val;
+
+ if (cmd == SOUND_MIXER_PRIVATE1)
+ {
+ if (get_user(val, (int __user *)arg))
+ return -EFAULT;
+
+ if (val != 0xffff)
+ {
+ unsigned long flags;
+ val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
+ devc->mixer_output_port = val;
+ val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */
+ devc->mixer_output_port = val;
+ spin_lock_irqsave(&devc->lock,flags);
+ if (val & AUDIO_SPEAKER)
+ ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
+ else
+ ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
+ spin_unlock_irqrestore(&devc->lock,flags);
+ }
+ val = devc->mixer_output_port;
+ return put_user(val, (int __user *)arg);
+ }
+ if (cmd == SOUND_MIXER_PRIVATE2)
+ {
+ if (get_user(val, (int __user *)arg))
+ return -EFAULT;
+ return(ad1848_control(AD1848_MIXER_REROUTE, val));
+ }
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ {
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ if (get_user(val, (int __user *)arg))
+ return -EFAULT;
+ val = ad1848_set_recmask(devc, val);
+ break;
+
+ default:
+ if (get_user(val, (int __user *)arg))
+ return -EFAULT;
+ val = ad1848_mixer_set(devc, cmd & 0xff, val);
+ break;
+ }
+ return put_user(val, (int __user *)arg);
+ }
+ else
+ {
+ switch (cmd & 0xff)
+ {
+ /*
+ * Return parameters
+ */
+
+ case SOUND_MIXER_RECSRC:
+ val = devc->recmask;
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ val = devc->supported_devices;
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ val = devc->supported_devices;
+ if (devc->model != MD_C930)
+ val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ val = devc->supported_rec_devices;
+ break;
+
+ case SOUND_MIXER_CAPS:
+ val=SOUND_CAP_EXCL_INPUT;
+ break;
+
+ default:
+ val = ad1848_mixer_get(devc, cmd & 0xff);
+ break;
+ }
+ return put_user(val, (int __user *)arg);
+ }
+ }
+ else
+ return -EINVAL;
+}
+
+static int ad1848_set_speed(int dev, int arg)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ /*
+ * The sampling speed is encoded in the least significant nibble of I8. The
+ * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
+ * three bits select the divisor (indirectly):
+ *
+ * The available speeds are in the following table. Keep the speeds in
+ * the increasing order.
+ */
+ typedef struct
+ {
+ int speed;
+ unsigned char bits;
+ }
+ speed_struct;
+
+ static speed_struct speed_table[] =
+ {
+ {5510, (0 << 1) | 1},
+ {5510, (0 << 1) | 1},
+ {6620, (7 << 1) | 1},
+ {8000, (0 << 1) | 0},
+ {9600, (7 << 1) | 0},
+ {11025, (1 << 1) | 1},
+ {16000, (1 << 1) | 0},
+ {18900, (2 << 1) | 1},
+ {22050, (3 << 1) | 1},
+ {27420, (2 << 1) | 0},
+ {32000, (3 << 1) | 0},
+ {33075, (6 << 1) | 1},
+ {37800, (4 << 1) | 1},
+ {44100, (5 << 1) | 1},
+ {48000, (6 << 1) | 0}
+ };
+
+ int i, n, selected = -1;
+
+ n = sizeof(speed_table) / sizeof(speed_struct);
+
+ if (arg <= 0)
+ return portc->speed;
+
+ if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */
+ {
+ if (arg < 4000)
+ arg = 4000;
+ if (arg > 50000)
+ arg = 50000;
+
+ portc->speed = arg;
+ portc->speed_bits = speed_table[3].bits;
+ return portc->speed;
+ }
+ if (arg < speed_table[0].speed)
+ selected = 0;
+ if (arg > speed_table[n - 1].speed)
+ selected = n - 1;
+
+ for (i = 1 /*really */ ; selected == -1 && i < n; i++)
+ {
+ if (speed_table[i].speed == arg)
+ selected = i;
+ else if (speed_table[i].speed > arg)
+ {
+ int diff1, diff2;
+
+ diff1 = arg - speed_table[i - 1].speed;
+ diff2 = speed_table[i].speed - arg;
+
+ if (diff1 < diff2)
+ selected = i - 1;
+ else
+ selected = i;
+ }
+ }
+ if (selected == -1)
+ {
+ printk(KERN_WARNING "ad1848: Can't find speed???\n");
+ selected = 3;
+ }
+ portc->speed = speed_table[selected].speed;
+ portc->speed_bits = speed_table[selected].bits;
+ return portc->speed;
+}
+
+static short ad1848_set_channels(int dev, short arg)
+{
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ if (arg != 1 && arg != 2)
+ return portc->channels;
+
+ portc->channels = arg;
+ return arg;
+}
+
+static unsigned int ad1848_set_bits(int dev, unsigned int arg)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ static struct format_tbl
+ {
+ int format;
+ unsigned char bits;
+ }
+ format2bits[] =
+ {
+ {
+ 0, 0
+ }
+ ,
+ {
+ AFMT_MU_LAW, 1
+ }
+ ,
+ {
+ AFMT_A_LAW, 3
+ }
+ ,
+ {
+ AFMT_IMA_ADPCM, 5
+ }
+ ,
+ {
+ AFMT_U8, 0
+ }
+ ,
+ {
+ AFMT_S16_LE, 2
+ }
+ ,
+ {
+ AFMT_S16_BE, 6
+ }
+ ,
+ {
+ AFMT_S8, 0
+ }
+ ,
+ {
+ AFMT_U16_LE, 0
+ }
+ ,
+ {
+ AFMT_U16_BE, 0
+ }
+ };
+ int i, n = sizeof(format2bits) / sizeof(struct format_tbl);
+
+ if (arg == 0)
+ return portc->audio_format;
+
+ if (!(arg & ad_format_mask[devc->model]))
+ arg = AFMT_U8;
+
+ portc->audio_format = arg;
+
+ for (i = 0; i < n; i++)
+ if (format2bits[i].format == arg)
+ {
+ if ((portc->format_bits = format2bits[i].bits) == 0)
+ return portc->audio_format = AFMT_U8; /* Was not supported */
+
+ return arg;
+ }
+ /* Still hanging here. Something must be terribly wrong */
+ portc->format_bits = 0;
+ return portc->audio_format = AFMT_U8;
+}
+
+static struct audio_driver ad1848_audio_driver =
+{
+ .owner = THIS_MODULE,
+ .open = ad1848_open,
+ .close = ad1848_close,
+ .output_block = ad1848_output_block,
+ .start_input = ad1848_start_input,
+ .prepare_for_input = ad1848_prepare_for_input,
+ .prepare_for_output = ad1848_prepare_for_output,
+ .halt_io = ad1848_halt,
+ .halt_input = ad1848_halt_input,
+ .halt_output = ad1848_halt_output,
+ .trigger = ad1848_trigger,
+ .set_speed = ad1848_set_speed,
+ .set_bits = ad1848_set_bits,
+ .set_channels = ad1848_set_channels
+};
+
+static struct mixer_operations ad1848_mixer_operations =
+{
+ .owner = THIS_MODULE,
+ .id = "SOUNDPORT",
+ .name = "AD1848/CS4248/CS4231",
+ .ioctl = ad1848_mixer_ioctl
+};
+
+static int ad1848_open(int dev, int mode)
+{
+ ad1848_info *devc;
+ ad1848_port_info *portc;
+ unsigned long flags;
+
+ if (dev < 0 || dev >= num_audiodevs)
+ return -ENXIO;
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+ portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ /* here we don't have to protect against intr */
+ spin_lock(&devc->lock);
+ if (portc->open_mode || (devc->open_mode & mode))
+ {
+ spin_unlock(&devc->lock);
+ return -EBUSY;
+ }
+ devc->dual_dma = 0;
+
+ if (audio_devs[dev]->flags & DMA_DUPLEX)
+ {
+ devc->dual_dma = 1;
+ }
+ devc->intr_active = 0;
+ devc->audio_mode = 0;
+ devc->open_mode |= mode;
+ portc->open_mode = mode;
+ spin_unlock(&devc->lock);
+ ad1848_trigger(dev, 0);
+
+ if (mode & OPEN_READ)
+ devc->record_dev = dev;
+ if (mode & OPEN_WRITE)
+ devc->playback_dev = dev;
+/*
+ * Mute output until the playback really starts. This decreases clicking (hope so).
+ */
+ spin_lock_irqsave(&devc->lock,flags);
+ ad_mute(devc);
+ spin_unlock_irqrestore(&devc->lock,flags);
+
+ return 0;
+}
+
+static void ad1848_close(int dev)
+{
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ DEB(printk("ad1848_close(void)\n"));
+
+ devc->intr_active = 0;
+ ad1848_halt(dev);
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ devc->audio_mode = 0;
+ devc->open_mode &= ~portc->open_mode;
+ portc->open_mode = 0;
+
+ ad_unmute(devc);
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ cnt = count;
+
+ if (portc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (portc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->audio_mode |= PCM_ENABLE_OUTPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ spin_lock_irqsave(&devc->lock,flags);
+
+ ad_write(devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+
+ devc->xfer_count = cnt;
+ devc->audio_mode |= PCM_ENABLE_OUTPUT;
+ devc->intr_active = 1;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ cnt = count;
+ if (portc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (portc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->audio_mode |= PCM_ENABLE_INPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ spin_lock_irqsave(&devc->lock,flags);
+
+ if (devc->model == MD_1848)
+ {
+ ad_write(devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+ }
+ else
+ {
+ ad_write(devc, 31, (unsigned char) (cnt & 0xff));
+ ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
+ }
+
+ ad_unmute(devc);
+
+ devc->xfer_count = cnt;
+ devc->audio_mode |= PCM_ENABLE_INPUT;
+ devc->intr_active = 1;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int ad1848_prepare_for_output(int dev, int bsize, int bcount)
+{
+ int timeout;
+ unsigned char fs, old_fs, tmp = 0;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ ad_mute(devc);
+
+ spin_lock_irqsave(&devc->lock,flags);
+ fs = portc->speed_bits | (portc->format_bits << 5);
+
+ if (portc->channels > 1)
+ fs |= 0x10;
+
+ ad_enter_MCE(devc); /* Enables changes to the format select reg */
+
+ if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */
+ {
+ fs &= 0xf0; /* Mask off the rate select bits */
+
+ ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
+ ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
+ }
+ old_fs = ad_read(devc, 8);
+
+ if (devc->model == MD_4232 || devc->model >= MD_4236)
+ {
+ tmp = ad_read(devc, 16);
+ ad_write(devc, 16, tmp | 0x30);
+ }
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
+
+ ad_write(devc, 8, fs);
+
+ /*
+ * Write to I8 starts resynchronization. Wait until it completes.
+ */
+
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+
+ if (devc->model >= MD_4232)
+ ad_write(devc, 16, tmp & ~0x30);
+
+ ad_leave_MCE(devc); /*
+ * Starts the calibration process.
+ */
+ spin_unlock_irqrestore(&devc->lock,flags);
+ devc->xfer_count = 0;
+
+#ifndef EXCLUDE_TIMERS
+ if (dev == timer_installed && devc->timer_running)
+ if ((fs & 0x01) != (old_fs & 0x01))
+ {
+ ad1848_tmr_reprogram(dev);
+ }
+#endif
+ ad1848_halt_output(dev);
+ return 0;
+}
+
+static int ad1848_prepare_for_input(int dev, int bsize, int bcount)
+{
+ int timeout;
+ unsigned char fs, old_fs, tmp = 0;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ if (devc->audio_mode)
+ return 0;
+
+ spin_lock_irqsave(&devc->lock,flags);
+ fs = portc->speed_bits | (portc->format_bits << 5);
+
+ if (portc->channels > 1)
+ fs |= 0x10;
+
+ ad_enter_MCE(devc); /* Enables changes to the format select reg */
+
+ if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */
+ {
+ fs &= 0xf0; /* Mask off the rate select bits */
+
+ ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
+ ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
+ }
+ if (devc->model == MD_4232)
+ {
+ tmp = ad_read(devc, 16);
+ ad_write(devc, 16, tmp | 0x30);
+ }
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
+
+ /*
+ * If mode >= 2 (CS4231), set I28. It's the capture format register.
+ */
+
+ if (devc->model != MD_1848)
+ {
+ old_fs = ad_read(devc, 28);
+ ad_write(devc, 28, fs);
+
+ /*
+ * Write to I28 starts resynchronization. Wait until it completes.
+ */
+
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+
+ if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE)
+ {
+ /*
+ * CS4231 compatible devices don't have separate sampling rate selection
+ * register for recording an playback. The I8 register is shared so we have to
+ * set the speed encoding bits of it too.
+ */
+ unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0);
+
+ ad_write(devc, 8, tmp);
+ /*
+ * Write to I8 starts resynchronization. Wait until it completes.
+ */
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+ }
+ }
+ else
+ { /* For AD1848 set I8. */
+
+ old_fs = ad_read(devc, 8);
+ ad_write(devc, 8, fs);
+ /*
+ * Write to I8 starts resynchronization. Wait until it completes.
+ */
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+ }
+
+ if (devc->model == MD_4232)
+ ad_write(devc, 16, tmp & ~0x30);
+
+ ad_leave_MCE(devc); /*
+ * Starts the calibration process.
+ */
+ spin_unlock_irqrestore(&devc->lock,flags);
+ devc->xfer_count = 0;
+
+#ifndef EXCLUDE_TIMERS
+ if (dev == timer_installed && devc->timer_running)
+ {
+ if ((fs & 0x01) != (old_fs & 0x01))
+ {
+ ad1848_tmr_reprogram(dev);
+ }
+ }
+#endif
+ ad1848_halt_input(dev);
+ return 0;
+}
+
+static void ad1848_halt(int dev)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ unsigned char bits = ad_read(devc, 9);
+
+ if (bits & 0x01 && (portc->open_mode & OPEN_WRITE))
+ ad1848_halt_output(dev);
+
+ if (bits & 0x02 && (portc->open_mode & OPEN_READ))
+ ad1848_halt_input(dev);
+ devc->audio_mode = 0;
+}
+
+static void ad1848_halt_input(int dev)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ unsigned long flags;
+
+ if (!(ad_read(devc, 9) & 0x02))
+ return; /* Capture not enabled */
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ ad_mute(devc);
+
+ {
+ int tmout;
+
+ if(!isa_dma_bridge_buggy)
+ disable_dma(audio_devs[dev]->dmap_in->dma);
+
+ for (tmout = 0; tmout < 100000; tmout++)
+ if (ad_read(devc, 11) & 0x10)
+ break;
+ ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */
+
+ if(!isa_dma_bridge_buggy)
+ enable_dma(audio_devs[dev]->dmap_in->dma);
+ devc->audio_mode &= ~PCM_ENABLE_INPUT;
+ }
+
+ outb(0, io_Status(devc)); /* Clear interrupt status */
+ outb(0, io_Status(devc)); /* Clear interrupt status */
+
+ devc->audio_mode &= ~PCM_ENABLE_INPUT;
+
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1848_halt_output(int dev)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ unsigned long flags;
+
+ if (!(ad_read(devc, 9) & 0x01))
+ return; /* Playback not enabled */
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ ad_mute(devc);
+ {
+ int tmout;
+
+ if(!isa_dma_bridge_buggy)
+ disable_dma(audio_devs[dev]->dmap_out->dma);
+
+ for (tmout = 0; tmout < 100000; tmout++)
+ if (ad_read(devc, 11) & 0x10)
+ break;
+ ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */
+
+ if(!isa_dma_bridge_buggy)
+ enable_dma(audio_devs[dev]->dmap_out->dma);
+
+ devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+ }
+
+ outb((0), io_Status(devc)); /* Clear interrupt status */
+ outb((0), io_Status(devc)); /* Clear interrupt status */
+
+ devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1848_trigger(int dev, int state)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+ unsigned long flags;
+ unsigned char tmp, old;
+
+ spin_lock_irqsave(&devc->lock,flags);
+ state &= devc->audio_mode;
+
+ tmp = old = ad_read(devc, 9);
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ tmp |= 0x02;
+ else
+ tmp &= ~0x02;
+ }
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if (state & PCM_ENABLE_OUTPUT)
+ tmp |= 0x01;
+ else
+ tmp &= ~0x01;
+ }
+ /* ad_mute(devc); */
+ if (tmp != old)
+ {
+ ad_write(devc, 9, tmp);
+ ad_unmute(devc);
+ }
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1848_init_hw(ad1848_info * devc)
+{
+ int i;
+ int *init_values;
+
+ /*
+ * Initial values for the indirect registers of CS4248/AD1848.
+ */
+ static int init_values_a[] =
+ {
+ 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,
+
+ /* Positions 16 to 31 just for CS4231/2 and ad1845 */
+ 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ static int init_values_b[] =
+ {
+ /*
+ Values for the newer chips
+ Some of the register initialization values were changed. In
+ order to get rid of the click that preceded PCM playback,
+ calibration was disabled on the 10th byte. On that same byte,
+ dual DMA was enabled; on the 11th byte, ADC dithering was
+ enabled, since that is theoretically desirable; on the 13th
+ byte, Mode 3 was selected, to enable access to extended
+ registers.
+ */
+ 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00,
+ 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ /*
+ * Select initialisation data
+ */
+
+ init_values = init_values_a;
+ if(devc->model >= MD_4236)
+ init_values = init_values_b;
+
+ for (i = 0; i < 16; i++)
+ ad_write(devc, i, init_values[i]);
+
+
+ ad_mute(devc); /* Initialize some variables */
+ ad_unmute(devc); /* Leave it unmuted now */
+
+ if (devc->model > MD_1848)
+ {
+ if (devc->model == MD_1845_SSCAPE)
+ ad_write(devc, 12, ad_read(devc, 12) | 0x50);
+ else
+ ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */
+
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
+
+ if (devc->model != MD_1845_SSCAPE)
+ for (i = 16; i < 32; i++)
+ ad_write(devc, i, init_values[i]);
+
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
+ }
+ if (devc->model > MD_1848)
+ {
+ if (devc->audio_flags & DMA_DUPLEX)
+ ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */
+ else
+ ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
+
+ if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
+ ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */
+
+ if (devc->model == MD_IWAVE)
+ { /* Some magic Interwave specific initialization */
+ ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
+ ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
+ ad_write(devc, 17, 0xc2); /* Alternate feature enable */
+ }
+ }
+ else
+ {
+ devc->audio_flags &= ~DMA_DUPLEX;
+ ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
+ if (soundpro)
+ ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */
+ }
+
+ outb((0), io_Status(devc)); /* Clear pending interrupts */
+
+ /*
+ * Toggle the MCE bit. It completes the initialization phase.
+ */
+
+ ad_enter_MCE(devc); /* In case the bit was off */
+ ad_leave_MCE(devc);
+
+ ad1848_mixer_reset(devc);
+}
+
+int ad1848_detect(struct resource *ports, int *ad_flags, int *osp)
+{
+ unsigned char tmp;
+ ad1848_info *devc = &adev_info[nr_ad1848_devs];
+ unsigned char tmp1 = 0xff, tmp2 = 0xff;
+ int optiC930 = 0; /* OPTi 82C930 flag */
+ int interwave = 0;
+ int ad1847_flag = 0;
+ int cs4248_flag = 0;
+ int sscape_flag = 0;
+ int io_base = ports->start;
+
+ int i;
+
+ DDB(printk("ad1848_detect(%x)\n", io_base));
+
+ if (ad_flags)
+ {
+ if (*ad_flags == 0x12345678)
+ {
+ interwave = 1;
+ *ad_flags = 0;
+ }
+
+ if (*ad_flags == 0x87654321)
+ {
+ sscape_flag = 1;
+ *ad_flags = 0;
+ }
+
+ if (*ad_flags == 0x12345677)
+ {
+ cs4248_flag = 1;
+ *ad_flags = 0;
+ }
+ }
+ if (nr_ad1848_devs >= MAX_AUDIO_DEV)
+ {
+ printk(KERN_ERR "ad1848 - Too many audio devices\n");
+ return 0;
+ }
+ spin_lock_init(&devc->lock);
+ devc->base = io_base;
+ devc->irq_ok = 0;
+ devc->timer_running = 0;
+ devc->MCE_bit = 0x40;
+ devc->irq = 0;
+ devc->open_mode = 0;
+ devc->chip_name = devc->name = "AD1848";
+ devc->model = MD_1848; /* AD1848 or CS4248 */
+ devc->levels = NULL;
+ devc->debug_flag = 0;
+
+ /*
+ * Check that the I/O address is in use.
+ *
+ * The bit 0x80 of the base I/O port is known to be 0 after the
+ * chip has performed its power on initialization. Just assume
+ * this has happened before the OS is starting.
+ *
+ * If the I/O address is unused, it typically returns 0xff.
+ */
+
+ if (inb(devc->base) == 0xff)
+ {
+ DDB(printk("ad1848_detect: The base I/O address appears to be dead\n"));
+ }
+
+ /*
+ * Wait for the device to stop initialization
+ */
+
+ DDB(printk("ad1848_detect() - step 0\n"));
+
+ for (i = 0; i < 10000000; i++)
+ {
+ unsigned char x = inb(devc->base);
+
+ if (x == 0xff || !(x & 0x80))
+ break;
+ }
+
+ DDB(printk("ad1848_detect() - step A\n"));
+
+ if (inb(devc->base) == 0x80) /* Not ready. Let's wait */
+ ad_leave_MCE(devc);
+
+ if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */
+ {
+ DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base)));
+ return 0;
+ }
+
+ /*
+ * Test if it's possible to change contents of the indirect registers.
+ * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
+ * so try to avoid using it.
+ */
+
+ DDB(printk("ad1848_detect() - step B\n"));
+ ad_write(devc, 0, 0xaa);
+ ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
+
+ if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45)
+ {
+ if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */
+ ad1847_flag = 1;
+ else
+ {
+ DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+ }
+ DDB(printk("ad1848_detect() - step C\n"));
+ ad_write(devc, 0, 0x45);
+ ad_write(devc, 1, 0xaa);
+
+ if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa)
+ {
+ if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */
+ ad1847_flag = 1;
+ else
+ {
+ DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+ }
+
+ /*
+ * The indirect register I12 has some read only bits. Let's
+ * try to change them.
+ */
+
+ DDB(printk("ad1848_detect() - step D\n"));
+ tmp = ad_read(devc, 12);
+ ad_write(devc, 12, (~tmp) & 0x0f);
+
+ if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f))
+ {
+ DDB(printk("ad1848 detect error - step D (%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+ * 0x01=RevB and 0x0A=RevC.
+ */
+
+ /*
+ * The original AD1848/CS4248 has just 15 indirect registers. This means
+ * that I0 and I16 should return the same value (etc.).
+ * However this doesn't work with CS4248. Actually it seems to be impossible
+ * to detect if the chip is a CS4231 or CS4248.
+ * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
+ * with CS4231.
+ */
+
+ /*
+ * OPTi 82C930 has mode2 control bit in another place. This test will fail
+ * with it. Accept this situation as a possible indication of this chip.
+ */
+
+ DDB(printk("ad1848_detect() - step F\n"));
+ ad_write(devc, 12, 0); /* Mode2=disabled */
+
+ for (i = 0; i < 16; i++)
+ {
+ if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16)))
+ {
+ DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
+ if (!ad1847_flag)
+ optiC930 = 1;
+ break;
+ }
+ }
+
+ /*
+ * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
+ * The bit 0x80 is always 1 in CS4248 and CS4231.
+ */
+
+ DDB(printk("ad1848_detect() - step G\n"));
+
+ if (ad_flags && *ad_flags == 400)
+ *ad_flags = 0;
+ else
+ ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */
+
+
+ if (ad_flags)
+ *ad_flags = 0;
+
+ tmp1 = ad_read(devc, 12);
+ if (tmp1 & 0x80)
+ {
+ if (ad_flags)
+ *ad_flags |= AD_F_CS4248;
+
+ devc->chip_name = "CS4248"; /* Our best knowledge just now */
+ }
+ if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40))
+ {
+ /*
+ * CS4231 detected - is it?
+ *
+ * Verify that setting I0 doesn't change I16.
+ */
+
+ DDB(printk("ad1848_detect() - step H\n"));
+ ad_write(devc, 16, 0); /* Set I16 to known value */
+
+ ad_write(devc, 0, 0x45);
+ if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */
+ {
+ ad_write(devc, 0, 0xaa);
+ if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */
+ {
+ DDB(printk("ad1848 detect error - step H(%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * Verify that some bits of I25 are read only.
+ */
+
+ DDB(printk("ad1848_detect() - step I\n"));
+ tmp1 = ad_read(devc, 25); /* Original bits */
+ ad_write(devc, 25, ~tmp1); /* Invert all bits */
+ if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7))
+ {
+ int id;
+
+ /*
+ * It's at least CS4231
+ */
+
+ devc->chip_name = "CS4231";
+ devc->model = MD_4231;
+
+ /*
+ * It could be an AD1845 or CS4231A as well.
+ * CS4231 and AD1845 report the same revision info in I25
+ * while the CS4231A reports different.
+ */
+
+ id = ad_read(devc, 25);
+ if ((id & 0xe7) == 0x80) /* Device busy??? */
+ id = ad_read(devc, 25);
+ if ((id & 0xe7) == 0x80) /* Device still busy??? */
+ id = ad_read(devc, 25);
+ DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25)));
+
+ if ((id & 0xe7) == 0x80) {
+ /*
+ * It must be a CS4231 or AD1845. The register I23 of
+ * CS4231 is undefined and it appears to be read only.
+ * AD1845 uses I23 for setting sample rate. Assume
+ * the chip is AD1845 if I23 is changeable.
+ */
+
+ unsigned char tmp = ad_read(devc, 23);
+ ad_write(devc, 23, ~tmp);
+
+ if (interwave)
+ {
+ devc->model = MD_IWAVE;
+ devc->chip_name = "IWave";
+ }
+ else if (ad_read(devc, 23) != tmp) /* AD1845 ? */
+ {
+ devc->chip_name = "AD1845";
+ devc->model = MD_1845;
+ }
+ else if (cs4248_flag)
+ {
+ if (ad_flags)
+ *ad_flags |= AD_F_CS4248;
+ devc->chip_name = "CS4248";
+ devc->model = MD_1848;
+ ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */
+ }
+ ad_write(devc, 23, tmp); /* Restore */
+ }
+ else
+ {
+ switch (id & 0x1f) {
+ case 3: /* CS4236/CS4235/CS42xB/CS4239 */
+ {
+ int xid;
+ ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */
+ ad_write(devc, 23, 0x9c); /* select extended register 25 */
+ xid = inb(io_Indexed_Data(devc));
+ ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */
+ switch (xid & 0x1f)
+ {
+ case 0x00:
+ devc->chip_name = "CS4237B(B)";
+ devc->model = MD_42xB;
+ break;
+ case 0x08:
+ /* Seems to be a 4238 ?? */
+ devc->chip_name = "CS4238";
+ devc->model = MD_42xB;
+ break;
+ case 0x09:
+ devc->chip_name = "CS4238B";
+ devc->model = MD_42xB;
+ break;
+ case 0x0b:
+ devc->chip_name = "CS4236B";
+ devc->model = MD_4236;
+ break;
+ case 0x10:
+ devc->chip_name = "CS4237B";
+ devc->model = MD_42xB;
+ break;
+ case 0x1d:
+ devc->chip_name = "CS4235";
+ devc->model = MD_4235;
+ break;
+ case 0x1e:
+ devc->chip_name = "CS4239";
+ devc->model = MD_4239;
+ break;
+ default:
+ printk("Chip ident is %X.\n", xid&0x1F);
+ devc->chip_name = "CS42xx";
+ devc->model = MD_4232;
+ break;
+ }
+ }
+ break;
+
+ case 2: /* CS4232/CS4232A */
+ devc->chip_name = "CS4232";
+ devc->model = MD_4232;
+ break;
+
+ case 0:
+ if ((id & 0xe0) == 0xa0)
+ {
+ devc->chip_name = "CS4231A";
+ devc->model = MD_4231A;
+ }
+ else
+ {
+ devc->chip_name = "CS4321";
+ devc->model = MD_4231;
+ }
+ break;
+
+ default: /* maybe */
+ DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7));
+ if (optiC930)
+ {
+ devc->chip_name = "82C930";
+ devc->model = MD_C930;
+ }
+ else
+ {
+ devc->chip_name = "CS4231";
+ devc->model = MD_4231;
+ }
+ }
+ }
+ }
+ ad_write(devc, 25, tmp1); /* Restore bits */
+
+ DDB(printk("ad1848_detect() - step K\n"));
+ }
+ } else if (tmp1 == 0x0a) {
+ /*
+ * Is it perhaps a SoundPro CMI8330?
+ * If so, then we should be able to change indirect registers
+ * greater than I15 after activating MODE2, even though reading
+ * back I12 does not show it.
+ */
+
+ /*
+ * Let's try comparing register values
+ */
+ for (i = 0; i < 16; i++) {
+ if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) {
+ DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2));
+ soundpro = 1;
+ devc->chip_name = "SoundPro CMI 8330";
+ break;
+ }
+ }
+ }
+
+ DDB(printk("ad1848_detect() - step L\n"));
+ if (ad_flags)
+ {
+ if (devc->model != MD_1848)
+ *ad_flags |= AD_F_CS4231;
+ }
+ DDB(printk("ad1848_detect() - Detected OK\n"));
+
+ if (devc->model == MD_1848 && ad1847_flag)
+ devc->chip_name = "AD1847";
+
+
+ if (sscape_flag == 1)
+ devc->model = MD_1845_SSCAPE;
+
+ return 1;
+}
+
+int ad1848_init (char *name, struct resource *ports, int irq, int dma_playback,
+ int dma_capture, int share_dma, int *osp, struct module *owner)
+{
+ /*
+ * NOTE! If irq < 0, there is another driver which has allocated the IRQ
+ * so that this driver doesn't need to allocate/deallocate it.
+ * The actually used IRQ is ABS(irq).
+ */
+
+ int my_dev;
+ char dev_name[100];
+ int e;
+
+ ad1848_info *devc = &adev_info[nr_ad1848_devs];
+
+ ad1848_port_info *portc = NULL;
+
+ devc->irq = (irq > 0) ? irq : 0;
+ devc->open_mode = 0;
+ devc->timer_ticks = 0;
+ devc->dma1 = dma_playback;
+ devc->dma2 = dma_capture;
+ devc->subtype = cfg.card_subtype;
+ devc->audio_flags = DMA_AUTOMODE;
+ devc->playback_dev = devc->record_dev = 0;
+ if (name != NULL)
+ devc->name = name;
+
+ if (name != NULL && name[0] != 0)
+ sprintf(dev_name,
+ "%s (%s)", name, devc->chip_name);
+ else
+ sprintf(dev_name,
+ "Generic audio codec (%s)", devc->chip_name);
+
+ rename_region(ports, devc->name);
+
+ conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture);
+
+ if (devc->model == MD_1848 || devc->model == MD_C930)
+ devc->audio_flags |= DMA_HARDSTOP;
+
+ if (devc->model > MD_1848)
+ {
+ if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
+ devc->audio_flags &= ~DMA_DUPLEX;
+ else
+ devc->audio_flags |= DMA_DUPLEX;
+ }
+
+ portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL);
+ if(portc==NULL) {
+ release_region(devc->base, 4);
+ return -1;
+ }
+
+ if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+ dev_name,
+ &ad1848_audio_driver,
+ sizeof(struct audio_driver),
+ devc->audio_flags,
+ ad_format_mask[devc->model],
+ devc,
+ dma_playback,
+ dma_capture)) < 0)
+ {
+ release_region(devc->base, 4);
+ kfree(portc);
+ return -1;
+ }
+
+ audio_devs[my_dev]->portc = portc;
+ audio_devs[my_dev]->mixer_dev = -1;
+ if (owner)
+ audio_devs[my_dev]->d->owner = owner;
+ memset((char *) portc, 0, sizeof(*portc));
+
+ nr_ad1848_devs++;
+
+ devc->pmdev = pm_register(PM_ISA_DEV, my_dev, ad1848_pm_callback);
+ if (devc->pmdev)
+ devc->pmdev->data = devc;
+
+ ad1848_init_hw(devc);
+
+ if (irq > 0)
+ {
+ devc->dev_no = my_dev;
+ if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0)
+ {
+ printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n");
+ /* Don't free it either then.. */
+ devc->irq = 0;
+ }
+ if (capabilities[devc->model].flags & CAP_F_TIMER)
+ {
+#ifndef CONFIG_SMP
+ int x;
+ unsigned char tmp = ad_read(devc, 16);
+#endif
+
+ devc->timer_ticks = 0;
+
+ ad_write(devc, 21, 0x00); /* Timer MSB */
+ ad_write(devc, 20, 0x10); /* Timer LSB */
+#ifndef CONFIG_SMP
+ ad_write(devc, 16, tmp | 0x40); /* Enable timer */
+ for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
+ ad_write(devc, 16, tmp & ~0x40); /* Disable timer */
+
+ if (devc->timer_ticks == 0)
+ printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq);
+ else
+ {
+ DDB(printk("Interrupt test OK\n"));
+ devc->irq_ok = 1;
+ }
+#else
+ devc->irq_ok = 1;
+#endif
+ }
+ else
+ devc->irq_ok = 1; /* Couldn't test. assume it's OK */
+ } else if (irq < 0)
+ irq2dev[-irq] = devc->dev_no = my_dev;
+
+#ifndef EXCLUDE_TIMERS
+ if ((capabilities[devc->model].flags & CAP_F_TIMER) &&
+ devc->irq_ok)
+ ad1848_tmr_install(my_dev);
+#endif
+
+ if (!share_dma)
+ {
+ if (sound_alloc_dma(dma_playback, devc->name))
+ printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback);
+
+ if (dma_capture != dma_playback)
+ if (sound_alloc_dma(dma_capture, devc->name))
+ printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture);
+ }
+
+ if ((e = sound_install_mixer(MIXER_DRIVER_VERSION,
+ dev_name,
+ &ad1848_mixer_operations,
+ sizeof(struct mixer_operations),
+ devc)) >= 0)
+ {
+ audio_devs[my_dev]->mixer_dev = e;
+ if (owner)
+ mixer_devs[e]->owner = owner;
+ }
+ return my_dev;
+}
+
+int ad1848_control(int cmd, int arg)
+{
+ ad1848_info *devc;
+ unsigned long flags;
+
+ if (nr_ad1848_devs < 1)
+ return -ENODEV;
+
+ devc = &adev_info[nr_ad1848_devs - 1];
+
+ switch (cmd)
+ {
+ case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */
+ if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE)
+ return -EINVAL;
+ spin_lock_irqsave(&devc->lock,flags);
+ ad_enter_MCE(devc);
+ ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
+ ad_leave_MCE(devc);
+ spin_unlock_irqrestore(&devc->lock,flags);
+ break;
+
+ case AD1848_MIXER_REROUTE:
+ {
+ int o = (arg >> 8) & 0xff;
+ int n = arg & 0xff;
+
+ if (o < 0 || o >= SOUND_MIXER_NRDEVICES)
+ return -EINVAL;
+
+ if (!(devc->supported_devices & (1 << o)) &&
+ !(devc->supported_rec_devices & (1 << o)))
+ return -EINVAL;
+
+ if (n == SOUND_MIXER_NONE)
+ { /* Just hide this control */
+ ad1848_mixer_set(devc, o, 0); /* Shut up it */
+ devc->supported_devices &= ~(1 << o);
+ devc->supported_rec_devices &= ~(1 << o);
+ break;
+ }
+
+ /* Make the mixer control identified by o to appear as n */
+ if (n < 0 || n >= SOUND_MIXER_NRDEVICES)
+ return -EINVAL;
+
+ devc->mixer_reroute[n] = o; /* Rename the control */
+ if (devc->supported_devices & (1 << o))
+ devc->supported_devices |= (1 << n);
+ if (devc->supported_rec_devices & (1 << o))
+ devc->supported_rec_devices |= (1 << n);
+
+ devc->supported_devices &= ~(1 << o);
+ devc->supported_rec_devices &= ~(1 << o);
+ }
+ break;
+ }
+ return 0;
+}
+
+void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
+{
+ int i, mixer, dev = 0;
+ ad1848_info *devc = NULL;
+
+ for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
+ {
+ if (adev_info[i].base == io_base)
+ {
+ devc = &adev_info[i];
+ dev = devc->dev_no;
+ }
+ }
+
+ if (devc != NULL)
+ {
+ if(audio_devs[dev]->portc!=NULL)
+ kfree(audio_devs[dev]->portc);
+ release_region(devc->base, 4);
+
+ if (!share_dma)
+ {
+ if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */
+ free_irq(devc->irq, (void *)devc->dev_no);
+
+ sound_free_dma(dma_playback);
+
+ if (dma_playback != dma_capture)
+ sound_free_dma(dma_capture);
+
+ }
+ mixer = audio_devs[devc->dev_no]->mixer_dev;
+ if(mixer>=0)
+ sound_unload_mixerdev(mixer);
+
+ if (devc->pmdev)
+ pm_unregister(devc->pmdev);
+
+ nr_ad1848_devs--;
+ for ( ; i < nr_ad1848_devs ; i++)
+ adev_info[i] = adev_info[i+1];
+ }
+ else
+ printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
+}
+
+irqreturn_t adintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+ unsigned char status;
+ ad1848_info *devc;
+ int dev;
+ int alt_stat = 0xff;
+ unsigned char c930_stat = 0;
+ int cnt = 0;
+
+ dev = (int)dev_id;
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+
+interrupt_again: /* Jump back here if int status doesn't reset */
+
+ status = inb(io_Status(devc));
+
+ if (status == 0x80)
+ printk(KERN_DEBUG "adintr: Why?\n");
+ if (devc->model == MD_1848)
+ outb((0), io_Status(devc)); /* Clear interrupt status */
+
+ if (status & 0x01)
+ {
+ if (devc->model == MD_C930)
+ { /* 82C930 has interrupt status register in MAD16 register MC11 */
+
+ spin_lock(&devc->lock);
+
+ /* 0xe0e is C930 address port
+ * 0xe0f is C930 data port
+ */
+ outb(11, 0xe0e);
+ c930_stat = inb(0xe0f);
+ outb((~c930_stat), 0xe0f);
+
+ spin_unlock(&devc->lock);
+
+ alt_stat = (c930_stat << 2) & 0x30;
+ }
+ else if (devc->model != MD_1848)
+ {
+ spin_lock(&devc->lock);
+ alt_stat = ad_read(devc, 24);
+ ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */
+ spin_unlock(&devc->lock);
+ }
+
+ if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20))
+ {
+ DMAbuf_inputintr(devc->record_dev);
+ }
+ if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) &&
+ (alt_stat & 0x10))
+ {
+ DMAbuf_outputintr(devc->playback_dev, 1);
+ }
+ if (devc->model != MD_1848 && (alt_stat & 0x40)) /* Timer interrupt */
+ {
+ devc->timer_ticks++;
+#ifndef EXCLUDE_TIMERS
+ if (timer_installed == dev && devc->timer_running)
+ sound_timer_interrupt();
+#endif
+ }
+ }
+/*
+ * Sometimes playback or capture interrupts occur while a timer interrupt
+ * is being handled. The interrupt will not be retriggered if we don't
+ * handle it now. Check if an interrupt is still pending and restart
+ * the handler in this case.
+ */
+ if (inb(io_Status(devc)) & 0x01 && cnt++ < 4)
+ {
+ goto interrupt_again;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * Experimental initialization sequence for the integrated sound system
+ * of the Compaq Deskpro M.
+ */
+
+static int init_deskpro_m(struct address_info *hw_config)
+{
+ unsigned char tmp;
+
+ if ((tmp = inb(0xc44)) == 0xff)
+ {
+ DDB(printk("init_deskpro_m: Dead port 0xc44\n"));
+ return 0;
+ }
+
+ outb(0x10, 0xc44);
+ outb(0x40, 0xc45);
+ outb(0x00, 0xc46);
+ outb(0xe8, 0xc47);
+ outb(0x14, 0xc44);
+ outb(0x40, 0xc45);
+ outb(0x00, 0xc46);
+ outb(0xe8, 0xc47);
+ outb(0x10, 0xc44);
+
+ return 1;
+}
+
+/*
+ * Experimental initialization sequence for the integrated sound system
+ * of Compaq Deskpro XL.
+ */
+
+static int init_deskpro(struct address_info *hw_config)
+{
+ unsigned char tmp;
+
+ if ((tmp = inb(0xc44)) == 0xff)
+ {
+ DDB(printk("init_deskpro: Dead port 0xc44\n"));
+ return 0;
+ }
+ outb((tmp | 0x04), 0xc44); /* Select bank 1 */
+ if (inb(0xc44) != 0x04)
+ {
+ DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
+ return 0;
+ }
+ /*
+ * OK. It looks like a Deskpro so let's proceed.
+ */
+
+ /*
+ * I/O port 0xc44 Audio configuration register.
+ *
+ * bits 0xc0: Audio revision bits
+ * 0x00 = Compaq Business Audio
+ * 0x40 = MS Sound System Compatible (reset default)
+ * 0x80 = Reserved
+ * 0xc0 = Reserved
+ * bit 0x20: No Wait State Enable
+ * 0x00 = Disabled (reset default, DMA mode)
+ * 0x20 = Enabled (programmed I/O mode)
+ * bit 0x10: MS Sound System Decode Enable
+ * 0x00 = Decoding disabled (reset default)
+ * 0x10 = Decoding enabled
+ * bit 0x08: FM Synthesis Decode Enable
+ * 0x00 = Decoding Disabled (reset default)
+ * 0x08 = Decoding enabled
+ * bit 0x04 Bank select
+ * 0x00 = Bank 0
+ * 0x04 = Bank 1
+ * bits 0x03 MSS Base address
+ * 0x00 = 0x530 (reset default)
+ * 0x01 = 0x604
+ * 0x02 = 0xf40
+ * 0x03 = 0xe80
+ */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc44 (before): ");
+ outb((tmp & ~0x04), 0xc44);
+ printk("%02x ", inb(0xc44));
+ outb((tmp | 0x04), 0xc44);
+ printk("%02x\n", inb(0xc44));
+#endif
+
+ /* Set bank 1 of the register */
+ tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */
+
+ switch (hw_config->io_base)
+ {
+ case 0x530:
+ tmp |= 0x00;
+ break;
+ case 0x604:
+ tmp |= 0x01;
+ break;
+ case 0xf40:
+ tmp |= 0x02;
+ break;
+ case 0xe80:
+ tmp |= 0x03;
+ break;
+ default:
+ DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
+ return 0;
+ }
+ outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc44 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc44));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc44));
+#endif
+
+ /*
+ * I/O port 0xc45 FM Address Decode/MSS ID Register.
+ *
+ * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88)
+ * bank=0, bit 0x01: SBIC Power Control Bit
+ * 0x00 = Powered up
+ * 0x01 = Powered down
+ * bank=1, bits 0xfc: MSS ID (default=0x40)
+ */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc45 (before): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc45));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc45));
+#endif
+
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc45 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc45));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc45));
+#endif
+
+
+ /*
+ * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register.
+ *
+ * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03)
+ * bank=1, bits 0xff: Audio addressing ASIC id
+ */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc46 (before): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc46));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc46));
+#endif
+
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ outb((0x11), 0xc46); /* ASIC ID = 0x11 */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc46 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc46));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc46));
+#endif
+
+ /*
+ * I/O port 0xc47 FM Address Decode Register.
+ *
+ * bank=0, bits 0xff: Decode enable selection for various FM address bits
+ * bank=1, bits 0xff: Reserved
+ */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc47 (before): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc47));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc47));
+#endif
+
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */
+
+#ifdef DEBUGXL
+ /* Debug printing */
+ printk("Port 0xc47 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc47));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc47));
+#endif
+
+ /*
+ * I/O port 0xc6f = Audio Disable Function Register
+ */
+
+#ifdef DEBUGXL
+ printk("Port 0xc6f (before) = %02x\n", inb(0xc6f));
+#endif
+
+ outb((0x80), 0xc6f);
+
+#ifdef DEBUGXL
+ printk("Port 0xc6f (after) = %02x\n", inb(0xc6f));
+#endif
+
+ return 1;
+}
+
+int probe_ms_sound(struct address_info *hw_config, struct resource *ports)
+{
+ unsigned char tmp;
+
+ DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype));
+
+ if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
+ {
+ /* check_opl3(0x388, hw_config); */
+ return ad1848_detect(ports, NULL, hw_config->osp);
+ }
+
+ if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */
+ {
+ if (!init_deskpro(hw_config))
+ return 0;
+ }
+
+ if (deskpro_m) /* Compaq Deskpro M */
+ {
+ if (!init_deskpro_m(hw_config))
+ return 0;
+ }
+
+ /*
+ * Check if the IO port returns valid signature. The original MS Sound
+ * system returns 0x04 while some cards (AudioTrix Pro for example)
+ * return 0x00 or 0x0f.
+ */
+
+ if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */
+ {
+ int ret;
+
+ DDB(printk("I/O address is inactive (%x)\n", tmp));
+ if (!(ret = ad1848_detect(ports, NULL, hw_config->osp)))
+ return 0;
+ return 1;
+ }
+ DDB(printk("MSS signature = %x\n", tmp & 0x3f));
+ if ((tmp & 0x3f) != 0x04 &&
+ (tmp & 0x3f) != 0x0f &&
+ (tmp & 0x3f) != 0x00)
+ {
+ int ret;
+
+ MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3)));
+ DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n"));
+ if (!(ret = ad1848_detect(ports, NULL, hw_config->osp)))
+ return 0;
+
+ hw_config->card_subtype = 1;
+ return 1;
+ }
+ if ((hw_config->irq != 5) &&
+ (hw_config->irq != 7) &&
+ (hw_config->irq != 9) &&
+ (hw_config->irq != 10) &&
+ (hw_config->irq != 11) &&
+ (hw_config->irq != 12))
+ {
+ printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ {
+ printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma);
+ return 0;
+ }
+ /*
+ * Check that DMA0 is not in use with a 8 bit board.
+ */
+
+ if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n");
+ return 0;
+ }
+ if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
+ return 0;
+ }
+ return ad1848_detect(ports, NULL, hw_config->osp);
+}
+
+void attach_ms_sound(struct address_info *hw_config, struct resource *ports, struct module *owner)
+{
+ static signed char interrupt_bits[12] =
+ {
+ -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20
+ };
+ signed char bits;
+ char dma2_bit = 0;
+
+ static char dma_bits[4] =
+ {
+ 1, 2, 0, 3
+ };
+
+ int config_port = hw_config->io_base + 0;
+ int version_port = hw_config->io_base + 3;
+ int dma = hw_config->dma;
+ int dma2 = hw_config->dma2;
+
+ if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
+ {
+ hw_config->slots[0] = ad1848_init("MS Sound System", ports,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma2, 0,
+ hw_config->osp,
+ owner);
+ return;
+ }
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == -1)
+ {
+ printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
+ release_region(ports->start, 4);
+ release_region(ports->start - 4, 4);
+ return;
+ }
+ outb((bits | 0x40), config_port);
+ if ((inb(version_port) & 0x40) == 0)
+ printk(KERN_ERR "[MSS: IRQ Conflict?]\n");
+
+/*
+ * Handle the capture DMA channel
+ */
+
+ if (dma2 != -1 && dma2 != dma)
+ {
+ if (!((dma == 0 && dma2 == 1) ||
+ (dma == 1 && dma2 == 0) ||
+ (dma == 3 && dma2 == 0)))
+ { /* Unsupported combination. Try to swap channels */
+ int tmp = dma;
+
+ dma = dma2;
+ dma2 = tmp;
+ }
+ if ((dma == 0 && dma2 == 1) ||
+ (dma == 1 && dma2 == 0) ||
+ (dma == 3 && dma2 == 0))
+ {
+ dma2_bit = 0x04; /* Enable capture DMA */
+ }
+ else
+ {
+ printk(KERN_WARNING "MSS: Invalid capture DMA\n");
+ dma2 = dma;
+ }
+ }
+ else
+ {
+ dma2 = dma;
+ }
+
+ hw_config->dma = dma;
+ hw_config->dma2 = dma2;
+
+ outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */
+
+ hw_config->slots[0] = ad1848_init("MS Sound System", ports,
+ hw_config->irq,
+ dma, dma2, 0,
+ hw_config->osp,
+ THIS_MODULE);
+}
+
+void unload_ms_sound(struct address_info *hw_config)
+{
+ ad1848_unload(hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma2, 0);
+ sound_unload_audiodev(hw_config->slots[0]);
+ release_region(hw_config->io_base, 4);
+}
+
+#ifndef EXCLUDE_TIMERS
+
+/*
+ * Timer stuff (for /dev/music).
+ */
+
+static unsigned int current_interval;
+
+static unsigned int ad1848_tmr_start(int dev, unsigned int usecs)
+{
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */
+ unsigned long divider;
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ /*
+ * Length of the timer interval (in nanoseconds) depends on the
+ * selected crystal oscillator. Check this from bit 0x01 of I8.
+ *
+ * AD1845 has just one oscillator which has cycle time of 10.050 us
+ * (when a 24.576 MHz xtal oscillator is used).
+ *
+ * Convert requested interval to nanoseconds before computing
+ * the timer divider.
+ */
+
+ if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
+ xtal_nsecs = 10050;
+ else if (ad_read(devc, 8) & 0x01)
+ xtal_nsecs = 9920;
+ else
+ xtal_nsecs = 9969;
+
+ divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs;
+
+ if (divider < 100) /* Don't allow shorter intervals than about 1ms */
+ divider = 100;
+
+ if (divider > 65535) /* Overflow check */
+ divider = 65535;
+
+ ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */
+ ad_write(devc, 20, divider & 0xff); /* Set lower bits */
+ ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */
+ devc->timer_running = 1;
+ spin_unlock_irqrestore(&devc->lock,flags);
+
+ return current_interval = (divider * xtal_nsecs + 500) / 1000;
+}
+
+static void ad1848_tmr_reprogram(int dev)
+{
+ /*
+ * Audio driver has changed sampling rate so that a different xtal
+ * oscillator was selected. We have to reprogram the timer rate.
+ */
+
+ ad1848_tmr_start(dev, current_interval);
+ sound_timer_syncinterval(current_interval);
+}
+
+static void ad1848_tmr_disable(int dev)
+{
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ spin_lock_irqsave(&devc->lock,flags);
+ ad_write(devc, 16, ad_read(devc, 16) & ~0x40);
+ devc->timer_running = 0;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void ad1848_tmr_restart(int dev)
+{
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ if (current_interval == 0)
+ return;
+
+ spin_lock_irqsave(&devc->lock,flags);
+ ad_write(devc, 16, ad_read(devc, 16) | 0x40);
+ devc->timer_running = 1;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static struct sound_lowlev_timer ad1848_tmr =
+{
+ 0,
+ 2,
+ ad1848_tmr_start,
+ ad1848_tmr_disable,
+ ad1848_tmr_restart
+};
+
+static int ad1848_tmr_install(int dev)
+{
+ if (timer_installed != -1)
+ return 0; /* Don't install another timer */
+
+ timer_installed = ad1848_tmr.dev = dev;
+ sound_timer_init(&ad1848_tmr, audio_devs[dev]->name);
+
+ return 1;
+}
+#endif /* EXCLUDE_TIMERS */
+
+static int ad1848_suspend(ad1848_info *devc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ ad_mute(devc);
+
+ spin_unlock_irqrestore(&devc->lock,flags);
+ return 0;
+}
+
+static int ad1848_resume(ad1848_info *devc)
+{
+ int mixer_levels[32], i;
+
+ /* Thinkpad is a bit more of PITA than normal. The BIOS tends to
+ restore it in a different config to the one we use. Need to
+ fix this somehow */
+
+ /* store old mixer levels */
+ memcpy(mixer_levels, devc->levels, sizeof (mixer_levels));
+ ad1848_init_hw(devc);
+
+ /* restore mixer levels */
+ for (i = 0; i < 32; i++)
+ ad1848_mixer_set(devc, devc->dev_no, mixer_levels[i]);
+
+ if (!devc->subtype) {
+ static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 };
+ static char dma_bits[4] = { 1, 2, 0, 3 };
+ unsigned long flags;
+ signed char bits;
+ char dma2_bit = 0;
+
+ int config_port = devc->base + 0;
+
+ bits = interrupt_bits[devc->irq];
+ if (bits == -1) {
+ printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq);
+ return -1;
+ }
+
+ spin_lock_irqsave(&devc->lock,flags);
+
+ outb((bits | 0x40), config_port);
+
+ if (devc->dma2 != -1 && devc->dma2 != devc->dma1)
+ if ( (devc->dma1 == 0 && devc->dma2 == 1) ||
+ (devc->dma1 == 1 && devc->dma2 == 0) ||
+ (devc->dma1 == 3 && devc->dma2 == 0))
+ dma2_bit = 0x04;
+
+ outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port);
+ spin_unlock_irqrestore(&devc->lock,flags);
+ }
+
+ return 0;
+}
+
+static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ ad1848_info *devc = dev->data;
+ if (devc) {
+ DEB(printk("ad1848: pm event received: 0x%x\n", rqst));
+
+ switch (rqst) {
+ case PM_SUSPEND:
+ ad1848_suspend(devc);
+ break;
+ case PM_RESUME:
+ ad1848_resume(devc);
+ break;
+ }
+ }
+ return 0;
+}
+
+
+EXPORT_SYMBOL(ad1848_detect);
+EXPORT_SYMBOL(ad1848_init);
+EXPORT_SYMBOL(ad1848_unload);
+EXPORT_SYMBOL(ad1848_control);
+EXPORT_SYMBOL(adintr);
+EXPORT_SYMBOL(probe_ms_sound);
+EXPORT_SYMBOL(attach_ms_sound);
+EXPORT_SYMBOL(unload_ms_sound);
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata dma2 = -1;
+static int __initdata type = 0;
+
+module_param(io, int, 0); /* I/O for a raw AD1848 card */
+module_param(irq, int, 0); /* IRQ to use */
+module_param(dma, int, 0); /* First DMA channel */
+module_param(dma2, int, 0); /* Second DMA channel */
+module_param(type, int, 0); /* Card type */
+module_param(deskpro_xl, bool, 0); /* Special magic for Deskpro XL boxen */
+module_param(deskpro_m, bool, 0); /* Special magic for Deskpro M box */
+module_param(soundpro, bool, 0); /* More special magic for SoundPro chips */
+
+#ifdef CONFIG_PNP
+module_param(isapnp, int, 0);
+module_param(isapnpjump, int, 0);
+module_param(reverse, bool, 0);
+MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled");
+MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke.");
+MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order");
+
+static struct pnp_dev *ad1848_dev = NULL;
+
+/* Please add new entries at the end of the table */
+static struct {
+ char *name;
+ unsigned short card_vendor, card_device,
+ vendor, function;
+ short mss_io, irq, dma, dma2; /* index into isapnp table */
+ int type;
+} ad1848_isapnp_list[] __initdata = {
+ {"CMI 8330 SoundPRO",
+ ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
+ ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
+ 0, 0, 0,-1, 0},
+ {"CS4232 based card",
+ ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000),
+ 0, 0, 0, 1, 0},
+ {"CS4232 based card",
+ ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100),
+ 0, 0, 0, 1, 0},
+ {"OPL3-SA2 WSS mode",
+ ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021),
+ 1, 0, 0, 1, 1},
+ {"Advanced Gravis InterWave Audio",
+ ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
+ ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000),
+ 0, 0, 0, 1, 0},
+ {NULL}
+};
+
+static struct isapnp_device_id id_table[] __devinitdata = {
+ { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
+ ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 },
+ /* The main driver for this card is opl3sa2
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 },
+ */
+ { ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
+ ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 },
+ {0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+static struct pnp_dev *activate_dev(char *devname, char *resname, struct pnp_dev *dev)
+{
+ int err;
+
+ err = pnp_device_attach(dev);
+ if (err < 0)
+ return(NULL);
+
+ if((err = pnp_activate_dev(dev)) < 0) {
+ printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err);
+
+ pnp_device_detach(dev);
+
+ return(NULL);
+ }
+ audio_activated = 1;
+ return(dev);
+}
+
+static struct pnp_dev *ad1848_init_generic(struct pnp_card *bus, struct address_info *hw_config, int slot)
+{
+
+ /* Configure Audio device */
+ if((ad1848_dev = pnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL)))
+ {
+ if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev)))
+ {
+ hw_config->io_base = pnp_port_start(ad1848_dev, ad1848_isapnp_list[slot].mss_io);
+ hw_config->irq = pnp_irq(ad1848_dev, ad1848_isapnp_list[slot].irq);
+ hw_config->dma = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma);
+ if(ad1848_isapnp_list[slot].dma2 != -1)
+ hw_config->dma2 = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma2);
+ else
+ hw_config->dma2 = -1;
+ hw_config->card_subtype = ad1848_isapnp_list[slot].type;
+ } else
+ return(NULL);
+ } else
+ return(NULL);
+
+ return(ad1848_dev);
+}
+
+static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pnp_card *bus, int slot)
+{
+ char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name;
+
+ /* Initialize this baby. */
+
+ if(ad1848_init_generic(bus, hw_config, slot)) {
+ /* We got it. */
+
+ printk(KERN_NOTICE "ad1848: PnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
+ busname,
+ hw_config->io_base, hw_config->irq, hw_config->dma,
+ hw_config->dma2);
+ return 1;
+ }
+ return 0;
+}
+
+static int __init ad1848_isapnp_probe(struct address_info *hw_config)
+{
+ static int first = 1;
+ int i;
+
+ /* Count entries in sb_isapnp_list */
+ for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++);
+ i--;
+
+ /* Check and adjust isapnpjump */
+ if( isapnpjump < 0 || isapnpjump > i) {
+ isapnpjump = reverse ? i : 0;
+ printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump);
+ }
+
+ if(!first || !reverse)
+ i = isapnpjump;
+ first = 0;
+ while(ad1848_isapnp_list[i].card_vendor != 0) {
+ static struct pnp_card *bus = NULL;
+
+ while ((bus = pnp_find_card(
+ ad1848_isapnp_list[i].card_vendor,
+ ad1848_isapnp_list[i].card_device,
+ bus))) {
+
+ if(ad1848_isapnp_init(hw_config, bus, i)) {
+ isapnpjump = i; /* start next search from here */
+ return 0;
+ }
+ }
+ i += reverse ? -1 : 1;
+ }
+
+ return -ENODEV;
+}
+#endif
+
+
+static int __init init_ad1848(void)
+{
+ printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+#ifdef CONFIG_PNP
+ if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) {
+ printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n");
+ isapnp = 0;
+ }
+#endif
+
+ if(io != -1) {
+ struct resource *ports;
+ if( isapnp == 0 )
+ {
+ if(irq == -1 || dma == -1) {
+ printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n");
+ return -EINVAL;
+ }
+
+ cfg.irq = irq;
+ cfg.io_base = io;
+ cfg.dma = dma;
+ cfg.dma2 = dma2;
+ cfg.card_subtype = type;
+ }
+
+ ports = request_region(io + 4, 4, "ad1848");
+
+ if (!ports)
+ return -EBUSY;
+
+ if (!request_region(io, 4, "WSS config")) {
+ release_region(io + 4, 4);
+ return -EBUSY;
+ }
+
+ if (!probe_ms_sound(&cfg, ports)) {
+ release_region(io + 4, 4);
+ release_region(io, 4);
+ return -ENODEV;
+ }
+ attach_ms_sound(&cfg, ports, THIS_MODULE);
+ loaded = 1;
+ }
+ return 0;
+}
+
+static void __exit cleanup_ad1848(void)
+{
+ if(loaded)
+ unload_ms_sound(&cfg);
+
+#ifdef CONFIG_PNP
+ if(ad1848_dev){
+ if(audio_activated)
+ pnp_device_detach(ad1848_dev);
+ }
+#endif
+}
+
+module_init(init_ad1848);
+module_exit(cleanup_ad1848);
+
+#ifndef MODULE
+static int __init setup_ad1848(char *str)
+{
+ /* io, irq, dma, dma2, type */
+ int ints[6];
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ io = ints[1];
+ irq = ints[2];
+ dma = ints[3];
+ dma2 = ints[4];
+ type = ints[5];
+
+ return 1;
+}
+
+__setup("ad1848=", setup_ad1848);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/ad1848.h b/sound/oss/ad1848.h
new file mode 100644
index 000000000000..d0573b023973
--- /dev/null
+++ b/sound/oss/ad1848.h
@@ -0,0 +1,25 @@
+
+#include <linux/interrupt.h>
+
+#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */
+#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */
+
+#define AD1848_SET_XTAL 1
+#define AD1848_MIXER_REROUTE 2
+
+#define AD1848_REROUTE(oldctl, newctl) \
+ ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl))
+
+
+int ad1848_init(char *name, struct resource *ports, int irq, int dma_playback,
+ int dma_capture, int share_dma, int *osp, struct module *owner);
+void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma);
+
+int ad1848_detect (struct resource *ports, int *flags, int *osp);
+int ad1848_control(int cmd, int arg);
+
+irqreturn_t adintr(int irq, void *dev_id, struct pt_regs * dummy);
+void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner);
+
+int probe_ms_sound(struct address_info *hw_config, struct resource *ports);
+void unload_ms_sound(struct address_info *hw_info);
diff --git a/sound/oss/ad1848_mixer.h b/sound/oss/ad1848_mixer.h
new file mode 100644
index 000000000000..f9231c6cd4e1
--- /dev/null
+++ b/sound/oss/ad1848_mixer.h
@@ -0,0 +1,253 @@
+/*
+ * sound/ad1848_mixer.h
+ *
+ * Definitions for the mixer of AD1848 and compatible codecs.
+ */
+
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+
+/*
+ * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
+ * Sound card manufacturers have connected actual inputs (CD, synth, line,
+ * etc) to these inputs in different order. Therefore it's difficult
+ * to assign mixer channels to these inputs correctly. The following
+ * contains two alternative mappings. The first one is for GUS MAX and
+ * the second is just a generic one (line1, line2 and line3).
+ * (Actually this is not a mapping but rather some kind of interleaving
+ * solution).
+ */
+#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \
+ SOUND_MASK_LINE1 | SOUND_MASK_IMIX)
+
+#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_LINE1)
+
+#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \
+ SOUND_MASK_LINE2 | \
+ SOUND_MASK_IGAIN | \
+ SOUND_MASK_PCM | SOUND_MASK_IMIX)
+
+#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
+ SOUND_MASK_MIC | \
+ SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_IGAIN | \
+ SOUND_MASK_PCM | SOUND_MASK_IMIX)
+
+#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME)
+
+/* OPTi 82C930 has no IMIX level control, but it can still be selected as an
+ * input
+ */
+#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
+ SOUND_MASK_MIC | SOUND_MASK_VOLUME | \
+ SOUND_MASK_LINE3 | \
+ SOUND_MASK_IGAIN | SOUND_MASK_PCM)
+
+#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \
+ SOUND_MASK_LINE | SOUND_MASK_SYNTH | \
+ SOUND_MASK_CD | SOUND_MASK_MIC | \
+ SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \
+ SOUND_MASK_OGAIN)
+
+struct mixer_def {
+ unsigned int regno:6; /* register number for volume */
+ unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */
+ unsigned int bitpos:3; /* position of bits in register for volume */
+ unsigned int nbits:3; /* number of bits in register for volume */
+ unsigned int mutereg:6; /* register number for mute bit */
+ unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */
+ unsigned int mutepos:4; /* position of mute bit in register */
+ unsigned int recreg:6; /* register number for recording bit */
+ unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */
+ unsigned int recpos:4; /* position of recording bit in register */
+};
+
+static char mix_cvt[101] = {
+ 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+ 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
+ 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
+ 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
+ 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
+ 100
+};
+
+typedef struct mixer_def mixer_ent;
+typedef mixer_ent mixer_ents[2];
+
+/*
+ * Most of the mixer entries work in backwards. Setting the polarity field
+ * makes them to work correctly.
+ *
+ * The channel numbering used by individual sound cards is not fixed. Some
+ * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
+ * The current version doesn't try to compensate this.
+ */
+
+#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \
+ [name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \
+ {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}}
+
+#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
+ rec_reg_l, rec_pola_l, rec_pos_l, \
+ reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
+ rec_reg_r, rec_pola_r, rec_pos_r) \
+ [name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
+ rec_reg_l, rec_pola_l, rec_pos_l}, \
+ {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
+ rec_reg_r, rec_pola_r, rec_pos_r}}
+
+static mixer_ents ad1848_mix_devices[32] = {
+ MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8),
+ MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
+ MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
+ MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
+ MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
+};
+
+static mixer_ents iwave_mix_devices[32] = {
+ MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8),
+ MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
+ MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
+ MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8),
+ MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
+ MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
+};
+
+static mixer_ents cs42xb_mix_devices[32] = {
+ /* Digital master volume actually has seven bits, but we only use
+ six to avoid the discontinuity when the analog gain kicks in. */
+ MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7),
+ MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
+ MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
+ /* For the IMIX entry, it was not possible to use the MIX_ENT macro
+ because the mute bit is in different positions for the two
+ channels and requires reverse polarity. */
+ [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8},
+ {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}},
+ MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
+ MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7)
+};
+
+/* OPTi 82C930 has somewhat different port addresses.
+ * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1
+ * VOLUME, SYNTH, LINE, CD are not enabled above.
+ * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
+ */
+static mixer_ents c930_mix_devices[32] = {
+ MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7),
+ MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7),
+ MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7),
+ MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7),
+ MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7),
+ MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7),
+ MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7),
+ MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
+ MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7),
+ MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7),
+ MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7)
+};
+
+static mixer_ents spro_mix_devices[32] = {
+ MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8),
+ MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8,
+ 5, 1, 1, 4, 23, 0, 3, 0, 0, 8),
+ MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8),
+ MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8),
+ MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2,
+ 20, 0, 0, 4, 17, 1, 3, 16, 0, 1),
+ MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4,
+ 21, 0, 0, 4, 17, 1, 1, 16, 0, 3),
+ MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
+ MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8),
+ /* This is external wavetable */
+ MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4,
+ 22, 0, 0, 4, 23, 1, 0, 23, 0, 5),
+};
+
+static int default_mixer_levels[32] =
+{
+ 0x3232, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x4b4b, /* FM */
+ 0x3232, /* PCM */
+ 0x1515, /* PC Speaker */
+ 0x2020, /* Ext Line */
+ 0x1010, /* Mic */
+ 0x4b4b, /* CD */
+ 0x0000, /* Recording monitor */
+ 0x4b4b, /* Second PCM */
+ 0x4b4b, /* Recording level */
+ 0x4b4b, /* Input gain */
+ 0x4b4b, /* Output gain */
+ 0x2020, /* Line1 */
+ 0x2020, /* Line2 */
+ 0x1515 /* Line3 (usually line in)*/
+};
+
+#define LEFT_CHN 0
+#define RIGHT_CHN 1
+
+/*
+ * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1)
+ */
+
+#ifndef AUDIO_SPEAKER
+#define AUDIO_SPEAKER 0x01 /* Enable mono output */
+#define AUDIO_HEADPHONE 0x02 /* Sparc only */
+#define AUDIO_LINE_OUT 0x04 /* Sparc only */
+#endif
diff --git a/sound/oss/ad1889.c b/sound/oss/ad1889.c
new file mode 100644
index 000000000000..b767c621fd09
--- /dev/null
+++ b/sound/oss/ad1889.c
@@ -0,0 +1,1103 @@
+/*
+ * Copyright 2001-2004 Randolph Chung <tausq@debian.org>
+ *
+ * Analog Devices 1889 PCI audio driver (AD1819 AC97-compatible 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Notes:
+ * 1. Only flat DMA is supported; s-g is not supported right now
+ *
+ *
+<jsm> tausq: Anyway, to set up sample rates for D to A, you just use the sample rate on the codec. For A to D, you need to set the codec always to 48K (using the split sample rate feature on the codec) and then set the resampler on the AD1889 to the sample rate you want.
+<jsm> Also, when changing the sample rate on the codec you need to power it down and re power it up for the change to take effect!
+ *
+ * $Id: ad1889.c,v 1.3 2002/10/19 21:31:44 grundler Exp $
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/ac97_codec.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+
+#include "ad1889.h"
+
+#define DBG(fmt, arg...) printk(fmt, ##arg)
+#define DEVNAME "ad1889"
+
+#define NR_HW_CH 4
+#define DAC_RUNNING 1
+#define ADC_RUNNING 2
+
+#define UNDERRUN(dev) (0)
+
+#define AD1889_READW(dev,reg) readw(dev->regbase + reg)
+#define AD1889_WRITEW(dev,reg,val) writew((val), dev->regbase + reg)
+#define AD1889_READL(dev,reg) readl(dev->regbase + reg)
+#define AD1889_WRITEL(dev,reg,val) writel((val), dev->regbase + reg)
+
+//now 100ms
+/* #define WAIT_10MS() schedule_timeout(HZ/10) */
+#define WAIT_10MS() do { int __i; for (__i = 0; __i < 100; __i++) udelay(1000); } while(0)
+
+/* currently only support a single device */
+static ad1889_dev_t *ad1889_dev = NULL;
+
+/************************* helper routines ***************************** */
+static inline void ad1889_set_wav_rate(ad1889_dev_t *dev, int rate)
+{
+ struct ac97_codec *ac97_codec = dev->ac97_codec;
+
+ DBG("Setting WAV rate to %d\n", rate);
+ dev->state[AD_WAV_STATE].dmabuf.rate = rate;
+ AD1889_WRITEW(dev, AD_DSWAS, rate);
+
+ /* Cycle the DAC to enable the new rate */
+ ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0x0200);
+ WAIT_10MS();
+ ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0);
+}
+
+static inline void ad1889_set_wav_fmt(ad1889_dev_t *dev, int fmt)
+{
+ u16 tmp;
+
+ DBG("Setting WAV format to 0x%x\n", fmt);
+
+ tmp = AD1889_READW(ad1889_dev, AD_DSWSMC);
+ if (fmt & AFMT_S16_LE) {
+ //tmp |= 0x0100; /* set WA16 */
+ tmp |= 0x0300; /* set WA16 stereo */
+ } else if (fmt & AFMT_U8) {
+ tmp &= ~0x0100; /* clear WA16 */
+ }
+ AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp);
+}
+
+static inline void ad1889_set_adc_fmt(ad1889_dev_t *dev, int fmt)
+{
+ u16 tmp;
+
+ DBG("Setting ADC format to 0x%x\n", fmt);
+
+ tmp = AD1889_READW(ad1889_dev, AD_DSRAMC);
+ if (fmt & AFMT_S16_LE) {
+ tmp |= 0x0100; /* set WA16 */
+ } else if (fmt & AFMT_U8) {
+ tmp &= ~0x0100; /* clear WA16 */
+ }
+ AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp);
+}
+
+static void ad1889_start_wav(ad1889_state_t *state)
+{
+ unsigned long flags;
+ struct dmabuf *dmabuf = &state->dmabuf;
+ int cnt;
+ u16 tmp;
+
+ spin_lock_irqsave(&state->card->lock, flags);
+
+ if (dmabuf->dma_len) /* DMA already in flight */
+ goto skip_dma;
+
+ /* setup dma */
+ cnt = dmabuf->wr_ptr - dmabuf->rd_ptr;
+ if (cnt == 0) /* done - don't need to do anything */
+ goto skip_dma;
+
+ /* If the wr_ptr has wrapped, only map to the end */
+ if (cnt < 0)
+ cnt = DMA_SIZE - dmabuf->rd_ptr;
+
+ dmabuf->dma_handle = pci_map_single(ad1889_dev->pci,
+ dmabuf->rawbuf + dmabuf->rd_ptr,
+ cnt, PCI_DMA_TODEVICE);
+ dmabuf->dma_len = cnt;
+ dmabuf->ready = 1;
+
+ DBG("Starting playback at 0x%p for %ld bytes\n", dmabuf->rawbuf +
+ dmabuf->rd_ptr, dmabuf->dma_len);
+
+ /* load up the current register set */
+ AD1889_WRITEL(ad1889_dev, AD_DMAWAVCC, cnt);
+ AD1889_WRITEL(ad1889_dev, AD_DMAWAVICC, cnt);
+ AD1889_WRITEL(ad1889_dev, AD_DMAWAVCA, dmabuf->dma_handle);
+
+ /* TODO: for now we load the base registers with the same thing */
+ AD1889_WRITEL(ad1889_dev, AD_DMAWAVBC, cnt);
+ AD1889_WRITEL(ad1889_dev, AD_DMAWAVIBC, cnt);
+ AD1889_WRITEL(ad1889_dev, AD_DMAWAVBA, dmabuf->dma_handle);
+
+ /* and we're off to the races... */
+ AD1889_WRITEL(ad1889_dev, AD_DMACHSS, 0x8);
+ tmp = AD1889_READW(ad1889_dev, AD_DSWSMC);
+ tmp |= 0x0400; /* set WAEN */
+ AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp);
+ (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */
+
+ dmabuf->enable |= DAC_RUNNING;
+
+skip_dma:
+ spin_unlock_irqrestore(&state->card->lock, flags);
+}
+
+
+static void ad1889_stop_wav(ad1889_state_t *state)
+{
+ unsigned long flags;
+ struct dmabuf *dmabuf = &state->dmabuf;
+
+ spin_lock_irqsave(&state->card->lock, flags);
+
+ if (dmabuf->enable & DAC_RUNNING) {
+ u16 tmp;
+ unsigned long cnt = dmabuf->dma_len;
+
+ tmp = AD1889_READW(ad1889_dev, AD_DSWSMC);
+ tmp &= ~0x0400; /* clear WAEN */
+ AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp);
+ (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */
+ pci_unmap_single(ad1889_dev->pci, dmabuf->dma_handle,
+ cnt, PCI_DMA_TODEVICE);
+
+ dmabuf->enable &= ~DAC_RUNNING;
+
+ /* update dma pointers */
+ dmabuf->rd_ptr += cnt;
+ dmabuf->rd_ptr &= (DMA_SIZE - 1);
+
+ dmabuf->dma_handle = 0;
+ dmabuf->dma_len = 0;
+ dmabuf->ready = 0;
+
+ wake_up(&dmabuf->wait);
+ }
+
+ spin_unlock_irqrestore(&state->card->lock, flags);
+}
+
+
+#if 0
+static void ad1889_startstop_adc(ad1889_state_t *state, int start)
+{
+ u16 tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->card->lock, flags);
+
+ tmp = AD1889_READW(ad1889_dev, AD_DSRAMC);
+ if (start) {
+ state->dmabuf.enable |= ADC_RUNNING;
+ tmp |= 0x0004; /* set ADEN */
+ } else {
+ state->dmabuf.enable &= ~ADC_RUNNING;
+ tmp &= ~0x0004; /* clear ADEN */
+ }
+ AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp);
+
+ spin_unlock_irqrestore(&state->card->lock, flags);
+}
+#endif
+
+static ad1889_dev_t *ad1889_alloc_dev(struct pci_dev *pci)
+{
+ ad1889_dev_t *dev;
+ struct dmabuf *dmabuf;
+ int i;
+
+ if ((dev = kmalloc(sizeof(ad1889_dev_t), GFP_KERNEL)) == NULL)
+ return NULL;
+ memset(dev, 0, sizeof(ad1889_dev_t));
+ spin_lock_init(&dev->lock);
+ dev->pci = pci;
+
+ for (i = 0; i < AD_MAX_STATES; i++) {
+ dev->state[i].card = dev;
+ init_MUTEX(&dev->state[i].sem);
+ init_waitqueue_head(&dev->state[i].dmabuf.wait);
+ }
+
+ /* allocate dma buffer */
+
+ for (i = 0; i < AD_MAX_STATES; i++) {
+ dmabuf = &dev->state[i].dmabuf;
+ dmabuf->rawbuf = kmalloc(DMA_SIZE, GFP_KERNEL|GFP_DMA);
+ if (!dmabuf->rawbuf)
+ goto err_free_dmabuf;
+ dmabuf->rawbuf_size = DMA_SIZE;
+ dmabuf->dma_handle = 0;
+ dmabuf->rd_ptr = dmabuf->wr_ptr = dmabuf->dma_len = 0UL;
+ dmabuf->ready = 0;
+ dmabuf->rate = 48000;
+ }
+ return dev;
+
+err_free_dmabuf:
+ while (--i >= 0)
+ kfree(dev->state[i].dmabuf.rawbuf);
+ kfree(dev);
+ return NULL;
+}
+
+static void ad1889_free_dev(ad1889_dev_t *dev)
+{
+ int j;
+ struct dmabuf *dmabuf;
+
+ if (dev == NULL)
+ return;
+
+ if (dev->ac97_codec)
+ ac97_release_codec(dev->ac97_codec);
+
+ for (j = 0; j < AD_MAX_STATES; j++) {
+ dmabuf = &dev->state[j].dmabuf;
+ if (dmabuf->rawbuf != NULL)
+ kfree(dmabuf->rawbuf);
+ }
+
+ kfree(dev);
+}
+
+static inline void ad1889_trigger_playback(ad1889_dev_t *dev)
+{
+#if 0
+ u32 val;
+ struct dmabuf *dmabuf = &dev->state[AD_WAV_STATE].dmabuf;
+#endif
+
+ ad1889_start_wav(&dev->state[AD_WAV_STATE]);
+}
+
+static int ad1889_read_proc (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *out = page;
+ int len, i;
+ ad1889_dev_t *dev = data;
+ ad1889_reg_t regs[] = {
+ { "WSMC", AD_DSWSMC, 16 },
+ { "RAMC", AD_DSRAMC, 16 },
+ { "WADA", AD_DSWADA, 16 },
+ { "SYDA", AD_DSSYDA, 16 },
+ { "WAS", AD_DSWAS, 16 },
+ { "RES", AD_DSRES, 16 },
+ { "CCS", AD_DSCCS, 16 },
+ { "ADCBA", AD_DMAADCBA, 32 },
+ { "ADCCA", AD_DMAADCCA, 32 },
+ { "ADCBC", AD_DMAADCBC, 32 },
+ { "ADCCC", AD_DMAADCCC, 32 },
+ { "ADCIBC", AD_DMAADCIBC, 32 },
+ { "ADCICC", AD_DMAADCICC, 32 },
+ { "ADCCTRL", AD_DMAADCCTRL, 16 },
+ { "WAVBA", AD_DMAWAVBA, 32 },
+ { "WAVCA", AD_DMAWAVCA, 32 },
+ { "WAVBC", AD_DMAWAVBC, 32 },
+ { "WAVCC", AD_DMAWAVCC, 32 },
+ { "WAVIBC", AD_DMAWAVIBC, 32 },
+ { "WAVICC", AD_DMAWAVICC, 32 },
+ { "WAVCTRL", AD_DMAWAVCTRL, 16 },
+ { "DISR", AD_DMADISR, 32 },
+ { "CHSS", AD_DMACHSS, 32 },
+ { "IPC", AD_GPIOIPC, 16 },
+ { "OP", AD_GPIOOP, 16 },
+ { "IP", AD_GPIOIP, 16 },
+ { "ACIC", AD_ACIC, 16 },
+ { "AC97_RESET", 0x100 + AC97_RESET, 16 },
+ { "AC97_MASTER_VOL_STEREO", 0x100 + AC97_MASTER_VOL_STEREO, 16 },
+ { "AC97_HEADPHONE_VOL", 0x100 + AC97_HEADPHONE_VOL, 16 },
+ { "AC97_MASTER_VOL_MONO", 0x100 + AC97_MASTER_VOL_MONO, 16 },
+ { "AC97_MASTER_TONE", 0x100 + AC97_MASTER_TONE, 16 },
+ { "AC97_PCBEEP_VOL", 0x100 + AC97_PCBEEP_VOL, 16 },
+ { "AC97_PHONE_VOL", 0x100 + AC97_PHONE_VOL, 16 },
+ { "AC97_MIC_VOL", 0x100 + AC97_MIC_VOL, 16 },
+ { "AC97_LINEIN_VOL", 0x100 + AC97_LINEIN_VOL, 16 },
+ { "AC97_CD_VOL", 0x100 + AC97_CD_VOL, 16 },
+ { "AC97_VIDEO_VOL", 0x100 + AC97_VIDEO_VOL, 16 },
+ { "AC97_AUX_VOL", 0x100 + AC97_AUX_VOL, 16 },
+ { "AC97_PCMOUT_VOL", 0x100 + AC97_PCMOUT_VOL, 16 },
+ { "AC97_RECORD_SELECT", 0x100 + AC97_RECORD_SELECT, 16 },
+ { "AC97_RECORD_GAIN", 0x100 + AC97_RECORD_GAIN, 16 },
+ { "AC97_RECORD_GAIN_MIC", 0x100 + AC97_RECORD_GAIN_MIC, 16 },
+ { "AC97_GENERAL_PURPOSE", 0x100 + AC97_GENERAL_PURPOSE, 16 },
+ { "AC97_3D_CONTROL", 0x100 + AC97_3D_CONTROL, 16 },
+ { "AC97_MODEM_RATE", 0x100 + AC97_MODEM_RATE, 16 },
+ { "AC97_POWER_CONTROL", 0x100 + AC97_POWER_CONTROL, 16 },
+ { NULL }
+ };
+
+ if (dev == NULL)
+ return -ENODEV;
+
+ for (i = 0; regs[i].name != 0; i++)
+ out += sprintf(out, "%s: 0x%0*x\n", regs[i].name,
+ regs[i].width >> 2,
+ (regs[i].width == 16
+ ? AD1889_READW(dev, regs[i].offset)
+ : AD1889_READL(dev, regs[i].offset)));
+
+ for (i = 0; i < AD_MAX_STATES; i++) {
+ out += sprintf(out, "DMA status for %s:\n",
+ (i == AD_WAV_STATE ? "WAV" : "ADC"));
+ out += sprintf(out, "\t\t0x%p (IOVA: 0x%llu)\n",
+ dev->state[i].dmabuf.rawbuf,
+ (unsigned long long)dev->state[i].dmabuf.dma_handle);
+
+ out += sprintf(out, "\tread ptr: offset %u\n",
+ (unsigned int)dev->state[i].dmabuf.rd_ptr);
+ out += sprintf(out, "\twrite ptr: offset %u\n",
+ (unsigned int)dev->state[i].dmabuf.wr_ptr);
+ out += sprintf(out, "\tdma len: offset %u\n",
+ (unsigned int)dev->state[i].dmabuf.dma_len);
+ }
+
+ len = out - page - off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0) return 0;
+ } else {
+ len = count;
+ }
+ *start = page + off;
+ return len;
+}
+
+/***************************** DMA interfaces ************************** */
+#if 0
+static inline unsigned long ad1889_get_dma_addr(ad1889_state_t *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ u32 offset;
+
+ if (!(dmabuf->enable & (DAC_RUNNING | ADC_RUNNING))) {
+ printk(KERN_ERR DEVNAME ": get_dma_addr called without dma enabled\n");
+ return 0;
+ }
+
+ if (dmabuf->enable & DAC_RUNNING)
+ offset = le32_to_cpu(AD1889_READL(state->card, AD_DMAWAVBA));
+ else
+ offset = le32_to_cpu(AD1889_READL(state->card, AD_DMAADCBA));
+
+ return (unsigned long)bus_to_virt((unsigned long)offset) - (unsigned long)dmabuf->rawbuf;
+}
+
+static void ad1889_update_ptr(ad1889_dev_t *dev, int wake)
+{
+ ad1889_state_t *state;
+ struct dmabuf *dmabuf;
+ unsigned long hwptr;
+ int diff;
+
+ /* check ADC first */
+ state = &dev->adc_state;
+ dmabuf = &state->dmabuf;
+ if (dmabuf->enable & ADC_RUNNING) {
+ hwptr = ad1889_get_dma_addr(state);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
+ dmabuf->count += diff;
+ if (dmabuf->count > dmabuf->dmasize)
+ dmabuf->count = dmabuf->dmasize;
+
+ if (dmabuf->mapped) {
+ if (wake & dmabuf->count >= dmabuf->fragsize)
+ wake_up(&dmabuf->wait);
+ } else {
+ if (wake & dmabuf->count > 0)
+ wake_up(&dmabuf->wait);
+ }
+ }
+
+ /* check DAC */
+ state = &dev->wav_state;
+ dmabuf = &state->dmabuf;
+ if (dmabuf->enable & DAC_RUNNING) {
+XXX
+
+}
+#endif
+
+/************************* /dev/dsp interfaces ************************* */
+
+static ssize_t ad1889_read(struct file *file, char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t ad1889_write(struct file *file, const char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data;
+ ad1889_state_t *state = &dev->state[AD_WAV_STATE];
+ volatile struct dmabuf *dmabuf = &state->dmabuf;
+ ssize_t ret = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ down(&state->sem);
+#if 0
+ if (dmabuf->mapped) {
+ ret = -ENXIO;
+ goto err1;
+ }
+#endif
+ if (!access_ok(VERIFY_READ, buffer, count)) {
+ ret = -EFAULT;
+ goto err1;
+ }
+
+ add_wait_queue(&state->dmabuf.wait, &wait);
+
+ /* start filling dma buffer.... */
+ while (count > 0) {
+ long rem;
+ long cnt = count;
+ unsigned long flags;
+
+ for (;;) {
+ long used_bytes;
+ long timeout; /* max time for DMA in jiffies */
+
+ /* buffer is full if wr catches up to rd */
+ spin_lock_irqsave(&state->card->lock, flags);
+ used_bytes = dmabuf->wr_ptr - dmabuf->rd_ptr;
+ timeout = (dmabuf->dma_len * HZ) / dmabuf->rate;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+
+ /* adjust for buffer wrap around */
+ used_bytes = (used_bytes + DMA_SIZE) & (DMA_SIZE - 1);
+
+ /* If at least one page unused */
+ if (used_bytes < (DMA_SIZE - 0x1000))
+ break;
+
+ /* dma buffer full */
+
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto err2;
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout + 1);
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ goto err2;
+ }
+ }
+
+ /* watch out for wrapping around static buffer */
+ spin_lock_irqsave(&state->card->lock, flags);
+ rem = DMA_SIZE - dmabuf->wr_ptr;
+ if (cnt > rem)
+ cnt = rem;
+
+ rem = dmabuf->wr_ptr;
+
+ /* update dma pointers */
+ dmabuf->wr_ptr += cnt;
+ dmabuf->wr_ptr &= DMA_SIZE - 1; /* wrap ptr if necessary */
+ spin_unlock_irqrestore(&state->card->lock, flags);
+
+ /* transfer unwrapped chunk */
+ if (copy_from_user(dmabuf->rawbuf + rem, buffer, cnt)) {
+ ret = -EFAULT;
+ goto err2;
+ }
+
+ DBG("Writing 0x%lx bytes to +0x%lx\n", cnt, rem);
+
+ /* update counters */
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ /* we have something to play - go play it! */
+ ad1889_trigger_playback(dev);
+ }
+
+err2:
+ remove_wait_queue(&state->dmabuf.wait, &wait);
+err1:
+ up(&state->sem);
+ return ret;
+}
+
+static unsigned int ad1889_poll(struct file *file, struct poll_table_struct *wait)
+{
+ unsigned int mask = 0;
+#if 0
+ ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data;
+ ad1889_state_t *state = NULL;
+ struct dmabuf *dmabuf;
+ unsigned long flags;
+
+ if (!(file->f_mode & (FMODE_READ | FMODE_WRITE)))
+ return -EINVAL;
+
+ if (file->f_mode & FMODE_WRITE) {
+ state = &dev->state[AD_WAV_STATE];
+ if (!state) return 0;
+ dmabuf = &state->dmabuf;
+ poll_wait(file, &dmabuf->wait, wait);
+ }
+
+ if (file->f_mode & FMODE_READ) {
+ state = &dev->state[AD_ADC_STATE];
+ if (!state) return 0;
+ dmabuf = &state->dmabuf;
+ poll_wait(file, &dmabuf->wait, wait);
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ ad1889_update_ptr(dev, 0);
+
+ if (file->f_mode & FMODE_WRITE) {
+ state = &dev->state[WAV_STATE];
+ dmabuf = &state->dmabuf;
+ if (dmabuf->mapped) {
+ if (dmabuf->count >= (int)dmabuf->fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ } else {
+ if ((int)dmabuf->dmasize >= dmabuf->count +
+ (int)dmabuf->fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ }
+
+ if (file ->f_mode & FMODE_READ) {
+ state = &dev->state[AD_ADC_STATE];
+ dmabuf = &state->dmabuf;
+ if (dmabuf->count >= (int)dmabuf->fragsize)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+#endif
+ return mask;
+}
+
+static int ad1889_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ return 0;
+}
+
+static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int val = 0;
+ ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data;
+ struct dmabuf *dmabuf;
+ audio_buf_info abinfo;
+ int __user *p = (int __user *)arg;
+
+ DBG("ad1889_ioctl cmd 0x%x arg %lu\n", cmd, arg);
+
+ switch (cmd)
+ {
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, p);
+
+ case SNDCTL_DSP_RESET:
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ break;
+
+ case SNDCTL_DSP_SPEED:
+ /* set sampling rate */
+ if (get_user(val, p))
+ return -EFAULT;
+ if (val > 5400 && val < 48000)
+ {
+ if (file->f_mode & FMODE_WRITE)
+ AD1889_WRITEW(ad1889_dev, AD_DSWAS, val);
+ if (file->f_mode & FMODE_READ)
+ AD1889_WRITEW(ad1889_dev, AD_DSRES, val);
+ }
+ return 0;
+
+ case SNDCTL_DSP_STEREO: /* undocumented? */
+ if (get_user(val, p))
+ return -EFAULT;
+ if (file->f_mode & FMODE_READ) {
+ val = AD1889_READW(ad1889_dev, AD_DSWSMC);
+ if (val) {
+ val |= 0x0200; /* set WAST */
+ } else {
+ val &= ~0x0200; /* clear WAST */
+ }
+ AD1889_WRITEW(ad1889_dev, AD_DSWSMC, val);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ val = AD1889_READW(ad1889_dev, AD_DSRAMC);
+ if (val) {
+ val |= 0x0002; /* set ADST */
+ } else {
+ val &= ~0x0002; /* clear ADST */
+ }
+ AD1889_WRITEW(ad1889_dev, AD_DSRAMC, val);
+ }
+
+ return 0;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ return put_user(DMA_SIZE, p);
+
+ case SNDCTL_DSP_GETFMTS:
+ return put_user(AFMT_S16_LE|AFMT_U8, p);
+
+ case SNDCTL_DSP_SETFMT:
+ if (get_user(val, p))
+ return -EFAULT;
+
+ if (val == 0) {
+ if (file->f_mode & FMODE_READ)
+ ad1889_set_adc_fmt(dev, val);
+
+ if (file->f_mode & FMODE_WRITE)
+ ad1889_set_wav_fmt(dev, val);
+ } else {
+ val = AFMT_S16_LE | AFMT_U8;
+ }
+
+ return put_user(val, p);
+
+ case SNDCTL_DSP_CHANNELS:
+ break;
+
+ case SNDCTL_DSP_POST:
+ /* send all data to device */
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ /* not supported; uses fixed fragment sizes */
+ return put_user(DMA_SIZE, p);
+
+ case SNDCTL_DSP_GETOSPACE:
+ case SNDCTL_DSP_GETISPACE:
+ /* space left in dma buffers */
+ if (cmd == SNDCTL_DSP_GETOSPACE)
+ dmabuf = &dev->state[AD_WAV_STATE].dmabuf;
+ else
+ dmabuf = &dev->state[AD_ADC_STATE].dmabuf;
+ abinfo.fragments = 1;
+ abinfo.fragstotal = 1;
+ abinfo.fragsize = DMA_SIZE;
+ abinfo.bytes = DMA_SIZE;
+ return copy_to_user(p, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+
+ case SNDCTL_DSP_GETCAPS:
+ return put_user(0, p);
+
+ case SNDCTL_DSP_GETTRIGGER:
+ case SNDCTL_DSP_SETTRIGGER:
+ break;
+
+ case SNDCTL_DSP_GETIPTR:
+ case SNDCTL_DSP_GETOPTR:
+ break;
+
+ case SNDCTL_DSP_SETDUPLEX:
+ break;
+
+ case SNDCTL_DSP_GETODELAY:
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ return put_user(AD1889_READW(ad1889_dev, AD_DSWAS), p);
+
+ case SOUND_PCM_READ_CHANNELS:
+ case SOUND_PCM_READ_BITS:
+ break;
+
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
+ case SNDCTL_DSP_SETSYNCRO:
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ break;
+
+ default:
+ break;
+ }
+
+ return -ENOTTY;
+}
+
+static int ad1889_open(struct inode *inode, struct file *file)
+{
+ /* check minor; only support /dev/dsp atm */
+ if (iminor(inode) != 3)
+ return -ENXIO;
+
+ file->private_data = ad1889_dev;
+
+ ad1889_set_wav_rate(ad1889_dev, 48000);
+ ad1889_set_wav_fmt(ad1889_dev, AFMT_S16_LE);
+ AD1889_WRITEW(ad1889_dev, AD_DSWADA, 0x0404); /* attenuation */
+ return nonseekable_open(inode, file);
+}
+
+static int ad1889_release(struct inode *inode, struct file *file)
+{
+ /* if we have state free it here */
+ return 0;
+}
+
+static struct file_operations ad1889_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = ad1889_read,
+ .write = ad1889_write,
+ .poll = ad1889_poll,
+ .ioctl = ad1889_ioctl,
+ .mmap = ad1889_mmap,
+ .open = ad1889_open,
+ .release = ad1889_release,
+};
+
+/************************* /dev/mixer interfaces ************************ */
+static int ad1889_mixer_open(struct inode *inode, struct file *file)
+{
+ if (ad1889_dev->ac97_codec->dev_mixer != iminor(inode))
+ return -ENODEV;
+
+ file->private_data = ad1889_dev->ac97_codec;
+ return 0;
+}
+
+static int ad1889_mixer_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int ad1889_mixer_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+ return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static struct file_operations ad1889_mixer_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = ad1889_mixer_ioctl,
+ .open = ad1889_mixer_open,
+ .release = ad1889_mixer_release,
+};
+
+/************************* AC97 interfaces ****************************** */
+static void ad1889_codec_write(struct ac97_codec *ac97, u8 reg, u16 val)
+{
+ ad1889_dev_t *dev = ac97->private_data;
+
+ //DBG("Writing 0x%x to 0x%lx\n", val, dev->regbase + 0x100 + reg);
+ AD1889_WRITEW(dev, 0x100 + reg, val);
+}
+
+static u16 ad1889_codec_read(struct ac97_codec *ac97, u8 reg)
+{
+ ad1889_dev_t *dev = ac97->private_data;
+ //DBG("Reading from 0x%lx\n", dev->regbase + 0x100 + reg);
+ return AD1889_READW(dev, 0x100 + reg);
+}
+
+static int ad1889_ac97_init(ad1889_dev_t *dev, int id)
+{
+ struct ac97_codec *ac97;
+ u16 eid;
+
+ if ((ac97 = ac97_alloc_codec()) == NULL)
+ return -ENOMEM;
+
+ ac97->private_data = dev;
+ ac97->id = id;
+
+ ac97->codec_read = ad1889_codec_read;
+ ac97->codec_write = ad1889_codec_write;
+
+ if (ac97_probe_codec(ac97) == 0) {
+ printk(DEVNAME ": ac97_probe_codec failed\n");
+ goto out_free;
+ }
+
+ eid = ad1889_codec_read(ac97, AC97_EXTENDED_ID);
+ if (eid == 0xffff) {
+ printk(KERN_WARNING DEVNAME ": no codec attached?\n");
+ goto out_free;
+ }
+
+ dev->ac97_features = eid;
+
+ if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1)) < 0) {
+ printk(KERN_ERR DEVNAME ": cannot register mixer\n");
+ goto out_free;
+ }
+
+ dev->ac97_codec = ac97;
+ return 0;
+
+out_free:
+ ac97_release_codec(ac97);
+ return -ENODEV;
+}
+
+static int ad1889_aclink_reset(struct pci_dev * pcidev)
+{
+ u16 stat;
+ int retry = 200;
+ ad1889_dev_t *dev = pci_get_drvdata(pcidev);
+
+ AD1889_WRITEW(dev, AD_DSCCS, 0x8000); /* turn on clock */
+ AD1889_READW(dev, AD_DSCCS);
+
+ WAIT_10MS();
+
+ stat = AD1889_READW(dev, AD_ACIC);
+ stat |= 0x0002; /* Reset Disable */
+ AD1889_WRITEW(dev, AD_ACIC, stat);
+ (void) AD1889_READW(dev, AD_ACIC); /* flush posted write */
+
+ udelay(10);
+
+ stat = AD1889_READW(dev, AD_ACIC);
+ stat |= 0x0001; /* Interface Enable */
+ AD1889_WRITEW(dev, AD_ACIC, stat);
+
+ do {
+ if (AD1889_READW(dev, AD_ACIC) & 0x8000) /* Ready */
+ break;
+ WAIT_10MS();
+ retry--;
+ } while (retry > 0);
+
+ if (!retry) {
+ printk(KERN_ERR "ad1889_aclink_reset: codec is not ready [0x%x]\n",
+ AD1889_READW(dev, AD_ACIC));
+ return -EBUSY;
+ }
+
+ /* TODO reset AC97 codec */
+ /* TODO set wave/adc pci ctrl status */
+
+ stat = AD1889_READW(dev, AD_ACIC);
+ stat |= 0x0004; /* Audio Stream Output Enable */
+ AD1889_WRITEW(dev, AD_ACIC, stat);
+ return 0;
+}
+
+/************************* PCI interfaces ****************************** */
+/* PCI device table */
+static struct pci_device_id ad1889_id_tbl[] = {
+ { PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS, PCI_ANY_ID,
+ PCI_ANY_ID, 0, 0, (unsigned long)DEVNAME },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, ad1889_id_tbl);
+
+static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 stat;
+ ad1889_dev_t *dev = (ad1889_dev_t *)dev_id;
+
+ stat = AD1889_READL(dev, AD_DMADISR);
+
+ /* clear ISR */
+ AD1889_WRITEL(dev, AD_DMADISR, stat);
+
+ if (stat & 0x8) { /* WAVI */
+ DBG("WAV interrupt\n");
+ dev->stats.wav_intrs++;
+ if (dev->state[AD_WAV_STATE].dmabuf.ready) {
+ ad1889_stop_wav(&dev->state[AD_WAV_STATE]); /* clean up */
+ ad1889_start_wav(&dev->state[AD_WAV_STATE]); /* start new */
+ }
+ }
+
+ if ((stat & 0x2) && dev->state[AD_ADC_STATE].dmabuf.ready) { /* ADCI */
+ DBG("ADC interrupt\n");
+ dev->stats.adc_intrs++;
+ }
+ if(stat)
+ return IRQ_HANDLED;
+ return IRQ_NONE;
+}
+
+static void ad1889_initcfg(ad1889_dev_t *dev)
+{
+ u16 tmp16;
+ u32 tmp32;
+
+ /* make sure the interrupt bits are setup the way we want */
+ tmp32 = AD1889_READL(dev, AD_DMAWAVCTRL);
+ tmp32 &= ~0xff; /* flat dma, no sg, mask out the intr bits */
+ tmp32 |= 0x6; /* intr on count, loop */
+ AD1889_WRITEL(dev, AD_DMAWAVCTRL, tmp32);
+
+ /* unmute... */
+ tmp16 = AD1889_READW(dev, AD_DSWADA);
+ tmp16 &= ~0x8080;
+ AD1889_WRITEW(dev, AD_DSWADA, tmp16);
+}
+
+static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
+{
+ int err;
+ ad1889_dev_t *dev;
+ unsigned long bar;
+ struct proc_dir_entry *proc_root = NULL;
+
+ if ((err = pci_enable_device(pcidev)) != 0) {
+ printk(KERN_ERR DEVNAME ": pci_enable_device failed\n");
+ return err;
+ }
+
+ pci_set_master(pcidev);
+ if ((dev = ad1889_alloc_dev(pcidev)) == NULL) {
+ printk(KERN_ERR DEVNAME ": cannot allocate memory for device\n");
+ return -ENOMEM;
+ }
+ pci_set_drvdata(pcidev, dev);
+ bar = pci_resource_start(pcidev, 0);
+
+ if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM)) {
+ printk(KERN_ERR DEVNAME ": memory region not assigned\n");
+ goto out1;
+ }
+
+ if (pci_request_region(pcidev, 0, DEVNAME)) {
+ printk(KERN_ERR DEVNAME ": unable to request memory region\n");
+ goto out1;
+ }
+
+ dev->regbase = ioremap_nocache(bar, AD_DSIOMEMSIZE);
+ if (!dev->regbase) {
+ printk(KERN_ERR DEVNAME ": unable to remap iomem\n");
+ goto out2;
+ }
+
+ if (request_irq(pcidev->irq, ad1889_interrupt, SA_SHIRQ, DEVNAME, dev) != 0) {
+ printk(KERN_ERR DEVNAME ": unable to request interrupt\n");
+ goto out3;
+ }
+
+ printk(KERN_INFO DEVNAME ": %s at %p IRQ %d\n",
+ (char *)ent->driver_data, dev->regbase, pcidev->irq);
+
+ if (ad1889_aclink_reset(pcidev) != 0)
+ goto out4;
+
+ /* register /dev/dsp */
+ if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1)) < 0) {
+ printk(KERN_ERR DEVNAME ": cannot register /dev/dsp\n");
+ goto out4;
+ }
+
+ if ((err = ad1889_ac97_init(dev, 0)) != 0)
+ goto out5;
+
+ /* XXX: cleanups */
+ if (((proc_root = proc_mkdir("driver/ad1889", NULL)) == NULL) ||
+ create_proc_read_entry("ac97", S_IFREG|S_IRUGO, proc_root, ac97_read_proc, dev->ac97_codec) == NULL ||
+ create_proc_read_entry("info", S_IFREG|S_IRUGO, proc_root, ad1889_read_proc, dev) == NULL)
+ goto out5;
+
+ ad1889_initcfg(dev);
+
+ //DBG(DEVNAME ": Driver initialization done!\n");
+
+ ad1889_dev = dev;
+
+ return 0;
+
+out5:
+ unregister_sound_dsp(dev->dev_audio);
+out4:
+ free_irq(pcidev->irq, dev);
+out3:
+ iounmap(dev->regbase);
+out2:
+ pci_release_region(pcidev, 0);
+out1:
+ ad1889_free_dev(dev);
+ pci_set_drvdata(pcidev, NULL);
+
+ return -ENODEV;
+}
+
+static void __devexit ad1889_remove(struct pci_dev *pcidev)
+{
+ ad1889_dev_t *dev = pci_get_drvdata(pcidev);
+
+ if (dev == NULL) return;
+
+ unregister_sound_mixer(dev->ac97_codec->dev_mixer);
+ unregister_sound_dsp(dev->dev_audio);
+ free_irq(pcidev->irq, dev);
+ iounmap(dev->regbase);
+ pci_release_region(pcidev, 0);
+
+ /* any hw programming needed? */
+ ad1889_free_dev(dev);
+ pci_set_drvdata(pcidev, NULL);
+}
+
+MODULE_AUTHOR("Randolph Chung");
+MODULE_DESCRIPTION("Analog Devices AD1889 PCI Audio");
+MODULE_LICENSE("GPL");
+
+static struct pci_driver ad1889_driver = {
+ .name = DEVNAME,
+ .id_table = ad1889_id_tbl,
+ .probe = ad1889_probe,
+ .remove = __devexit_p(ad1889_remove),
+};
+
+static int __init ad1889_init_module(void)
+{
+ return pci_module_init(&ad1889_driver);
+}
+
+static void ad1889_exit_module(void)
+{
+ pci_unregister_driver(&ad1889_driver);
+ return;
+}
+
+module_init(ad1889_init_module);
+module_exit(ad1889_exit_module);
diff --git a/sound/oss/ad1889.h b/sound/oss/ad1889.h
new file mode 100644
index 000000000000..e04affce1dd1
--- /dev/null
+++ b/sound/oss/ad1889.h
@@ -0,0 +1,134 @@
+#ifndef _AD1889_H_
+#define _AD1889_H_
+
+#define AD_DSWSMC 0x00 /* DMA input wave/syn mixer control */
+#define AD_DSRAMC 0x02 /* DMA output resamp/ADC mixer control */
+#define AD_DSWADA 0x04 /* DMA input wave attenuation */
+#define AD_DSSYDA 0x06 /* DMA input syn attentuation */
+#define AD_DSWAS 0x08 /* wave input sample rate */
+#define AD_DSRES 0x0a /* resampler output sample rate */
+#define AD_DSCCS 0x0c /* chip control/status */
+
+#define AD_DMARESBA 0x40 /* RES base addr */
+#define AD_DMARESCA 0x44 /* RES current addr */
+#define AD_DMARESBC 0x48 /* RES base cnt */
+#define AD_DMARESCC 0x4c /* RES current count */
+#define AD_DMAADCBA 0x50 /* ADC */
+#define AD_DMAADCCA 0x54
+#define AD_DMAADCBC 0x58
+#define AD_DMAADCCC 0x5c
+#define AD_DMASYNBA 0x60 /* SYN */
+#define AD_DMASYNCA 0x64
+#define AD_DMASYNBC 0x68
+#define AD_DMASYNCC 0x6c
+#define AD_DMAWAVBA 0x70 /* WAV */
+#define AD_DMAWAVCA 0x74
+#define AD_DMAWAVBC 0x78
+#define AD_DMAWAVCC 0x7c
+#define AD_DMARESICC 0x80 /* RES interrupt current count */
+#define AD_DMARESIBC 0x84 /* RES interrupt base count */
+#define AD_DMAADCICC 0x88 /* ADC interrupt current count */
+#define AD_DMAADCIBC 0x8c /* ADC interrupt base count */
+#define AD_DMASYNICC 0x90 /* SYN interrupt current count */
+#define AD_DMASYNIBC 0x94 /* SYN interrupt base count */
+#define AD_DMAWAVICC 0x98 /* WAV interrupt current count */
+#define AD_DMAWAVIBC 0x9c /* WAV interrupt base count */
+#define AD_DMARESCTRL 0xa0 /* RES PCI control/status */
+#define AD_DMAADCCTRL 0xa8 /* ADC PCI control/status */
+#define AD_DMASYNCTRL 0xb0 /* SYN PCI control/status */
+#define AD_DMAWAVCTRL 0xb8 /* WAV PCI control/status */
+#define AD_DMADISR 0xc0 /* PCI DMA intr status */
+#define AD_DMACHSS 0xc4 /* PCI DMA channel stop status */
+
+#define AD_GPIOIPC 0xc8 /* IO port ctrl */
+#define AD_GPIOOP 0xca /* IO output status */
+#define AD_GPIOIP 0xcc /* IO input status */
+
+/* AC97 registers, 0x100 - 0x17f; see ac97.h */
+#define AD_ACIC 0x180 /* AC Link interface ctrl */
+
+/* OPL3; BAR1 */
+#define AD_OPLM0AS 0x00 /* Music0 address/status */
+#define AD_OPLM0DATA 0x01 /* Music0 data */
+#define AD_OPLM1A 0x02 /* Music1 address */
+#define AD_OPLM1DATA 0x03 /* Music1 data */
+/* 0x04-0x0f reserved */
+
+/* MIDI; BAR2 */
+#define AD_MIDA 0x00 /* MIDI data */
+#define AD_MISC 0x01 /* MIDI status/cmd */
+/* 0x02-0xff reserved */
+
+#define AD_DSIOMEMSIZE 512
+#define AD_OPLMEMSIZE 16
+#define AD_MIDIMEMSIZE 16
+
+#define AD_WAV_STATE 0
+#define AD_ADC_STATE 1
+#define AD_MAX_STATES 2
+
+#define DMA_SIZE (128*1024)
+
+#define DMA_FLAG_MAPPED 1
+
+struct ad1889_dev;
+
+typedef struct ad1889_state {
+ struct ad1889_dev *card;
+
+ mode_t open_mode;
+ struct dmabuf {
+ unsigned int rate;
+ unsigned char fmt, enable;
+
+ /* buf management */
+ size_t rawbuf_size;
+ void *rawbuf;
+ dma_addr_t dma_handle; /* mapped address */
+ unsigned long dma_len; /* number of bytes mapped */
+
+ /* indexes into rawbuf for setting up DMA engine */
+ volatile unsigned long rd_ptr, wr_ptr;
+
+ wait_queue_head_t wait; /* to wait for buf servicing */
+
+ /* OSS bits */
+ unsigned int mapped:1;
+ unsigned int ready:1;
+ unsigned int ossfragshift;
+ int ossmaxfrags;
+ unsigned int subdivision;
+ } dmabuf;
+
+ struct semaphore sem;
+} ad1889_state_t;
+
+typedef struct ad1889_dev {
+ void __iomem *regbase;
+ struct pci_dev *pci;
+
+ spinlock_t lock;
+
+ int dev_audio;
+
+ /* states; one per channel; right now only WAV and ADC */
+ struct ad1889_state state[AD_MAX_STATES];
+
+ /* AC97 codec */
+ struct ac97_codec *ac97_codec;
+ u16 ac97_features;
+
+ /* debugging stuff */
+ struct stats {
+ unsigned int wav_intrs, adc_intrs;
+ unsigned int blocks, underrun, error;
+ } stats;
+} ad1889_dev_t;
+
+typedef struct ad1889_reg {
+ const char *name;
+ int offset;
+ int width;
+} ad1889_reg_t;
+
+#endif
diff --git a/sound/oss/adlib_card.c b/sound/oss/adlib_card.c
new file mode 100644
index 000000000000..6414ceb8f072
--- /dev/null
+++ b/sound/oss/adlib_card.c
@@ -0,0 +1,73 @@
+/*
+ * sound/adlib_card.c
+ *
+ * Detection routine for the AdLib card.
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "sound_config.h"
+
+#include "opl3.h"
+
+static void __init attach_adlib_card(struct address_info *hw_config)
+{
+ hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp, THIS_MODULE);
+}
+
+static int __init probe_adlib(struct address_info *hw_config)
+{
+ return opl3_detect(hw_config->io_base, hw_config->osp);
+}
+
+static struct address_info cfg;
+
+static int __initdata io = -1;
+
+module_param(io, int, 0);
+
+static int __init init_adlib(void)
+{
+ cfg.io_base = io;
+
+ if (cfg.io_base == -1) {
+ printk(KERN_ERR "adlib: must specify I/O address.\n");
+ return -EINVAL;
+ }
+ if (probe_adlib(&cfg) == 0)
+ return -ENODEV;
+ attach_adlib_card(&cfg);
+
+ return 0;
+}
+
+static void __exit cleanup_adlib(void)
+{
+ sound_unload_synthdev(cfg.slots[0]);
+
+}
+
+module_init(init_adlib);
+module_exit(cleanup_adlib);
+
+#ifndef MODULE
+static int __init setup_adlib(char *str)
+{
+ /* io */
+ int ints[2];
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ io = ints[1];
+
+ return 1;
+}
+__setup("adlib=", setup_adlib);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
new file mode 100644
index 000000000000..b556263a57f5
--- /dev/null
+++ b/sound/oss/aedsp16.c
@@ -0,0 +1,1381 @@
+/*
+ sound/oss/aedsp16.c
+
+ Audio Excel DSP 16 software configuration routines
+ Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ */
+/*
+ * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
+ * headers needed by this source.
+ */
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include "sound_config.h"
+
+/*
+ * Sanity checks
+ */
+
+#if defined(CONFIG_SOUND_AEDSP16_SBPRO) && defined(CONFIG_SOUND_AEDSP16_MSS)
+#error You have to enable only one of the MSS and SBPRO emulations.
+#endif
+
+/*
+
+ READ THIS
+
+ This module started to configure the Audio Excel DSP 16 Sound Card.
+ Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
+
+ NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
+ audio card and want to see the kernel support for it, please contact me.
+
+ Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
+ compatible card.
+ It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
+ so before this module, the only way to configure the DSP under linux was
+ boot the MS-DOS loading the sound.sys device driver (this driver soft-
+ configure the sound board hardware by massaging someone of its registers),
+ and then ctrl-alt-del to boot linux with the DSP configured by the DOS
+ driver.
+
+ This module works configuring your Audio Excel DSP 16's irq, dma and
+ mpu-401-irq. The OSS Lite routines rely on the fact that if the
+ hardware is there, they can detect it. The problem with AEDSP16 is
+ that no hardware can be found by the probe routines if the sound card
+ is not configured properly. Sometimes the kernel probe routines can find
+ an SBPRO even when the card is not configured (this is the standard setup
+ of the card), but the SBPRO emulation don't work well if the card is not
+ properly initialized. For this reason
+
+ aedsp16_init_board()
+
+ routine is called before the OSS Lite probe routines try to detect the
+ hardware.
+
+ NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
+
+ NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
+ have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
+ have to be configured by software.
+
+ NOTE: The driver is merged with the new OSS Lite sound driver. It works
+ as a lowlevel driver.
+
+ The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
+ the OSS Lite sound driver can be configured for SBPRO and MSS cards
+ at the same time, but the aedsp16 can't be two cards!!
+ When we configure it, we have to choose the SBPRO or the MSS emulation
+ for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
+
+ NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
+ please let me know if it works.
+
+ The MPU-401 support can be compiled in together with one of the other
+ two operating modes.
+
+ NOTE: This is something like plug-and-play: we have only to plug
+ the AEDSP16 board in the socket, and then configure and compile
+ a kernel that uses the AEDSP16 software configuration capability.
+ No jumper setting is needed!
+
+ For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
+ you have just to make config the OSS Lite package, configuring
+ the AEDSP16 sound card, then activating the SBPro emulation mode
+ and at last configuring IRQ and DMA.
+ Compile the kernel and run it.
+
+ NOTE: This means for SC-6000 cards that you can choose irq and dma,
+ but not the I/O addresses. To change I/O addresses you have to set
+ them with jumpers. For SC-6600 cards you have no jumpers so you have
+ to set up your full card configuration in the make config.
+
+ You can change the irq/dma/mirq settings WITHOUT THE NEED to open
+ your computer and massage the jumpers (there are no irq/dma/mirq
+ jumpers to be configured anyway, only I/O BASE values have to be
+ configured with jumpers)
+
+ For some ununderstandable reason, the card default of irq 7, dma 1,
+ don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
+ HDD work, the kernel start to erupt out a lot of messages like:
+
+ 'Sound: DMA timed out - IRQ/DRQ config error?'
+
+ For what I can say, I have NOT any conflict at irq 7 (under linux I'm
+ using the lp polling driver), and dma line 1 is unused as stated by
+ /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
+ I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
+ Anyway a setting of irq 10, dma 3 works really fine.
+
+ NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
+ the emulation mode, all the installed hardware and the hardware
+ configuration (irq and dma settings of all the hardware).
+
+ This init module should work with SBPRO+MSS, when one of the two is
+ the AEDSP16 emulation and the other the real card. (see [1])
+ For example:
+
+ AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
+ AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
+
+ MPU401 should work. (see [2])
+
+ [1]
+ ---
+ Date: Mon, 29 Jul 1997 08:35:40 +0100
+ From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+
+ [...]
+ Just to let you know got my Audio Excel (emulating a MSS) working
+ with my original SB16, thanks for the driver!
+ [...]
+ ---
+
+ [2] Not tested by me for lack of hardware.
+
+ TODO, WISHES AND TECH
+
+ - About I/O ports allocation -
+
+ Request the 2x0h region (port base) in any case if we are using this card.
+
+ NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
+ port base region (see code) does not mean necessarily that we are emulating
+ sbpro. Even if this region is the sbpro I/O ports region, we use this
+ region to access the control registers of the card, and if emulating
+ sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
+ registers are not used, in no way, to emulate an sbpro: they are
+ used only for configuration purposes.
+
+ Started Fri Mar 17 16:13:18 MET 1995
+
+ v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
+ - Initial code.
+ v0.2 (ALPHA)
+ - Cleanups.
+ - Integrated with Linux voxware v 2.90-2 kernel sound driver.
+ - SoundBlaster Pro mode configuration.
+ - Microsoft Sound System mode configuration.
+ - MPU-401 mode configuration.
+ v0.3 (ALPHA)
+ - Cleanups.
+ - Rearranged the code to let aedsp16_init_board be more general.
+ - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
+ inclusion too. We rely on os.h
+ - Used the to get a variable
+ len string (we are not sure about the len of Copyright string).
+ This works with any SB and compatible.
+ - Added the code to request_region at device init (should go in
+ the main body of voxware).
+ v0.4 (BETA)
+ - Better configure.c patch for aedsp16 configuration (better
+ logic of inclusion of AEDSP16 support)
+ - Modified the conditional compilation to better support more than
+ one sound card of the emulated type (read the NOTES above)
+ - Moved the sb init routine from the attach to the very first
+ probe in sb_card.c
+ - Rearrangements and cleanups
+ - Wiped out some unnecessary code and variables: this is kernel
+ code so it is better save some TEXT and DATA
+ - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
+ I/O ports in any case because they are used to access the DSP
+ configuration registers and we can not allow anyone to get them.
+ v0.5
+ - cleanups on comments
+ - prep for diffs against v3.0-proto-950402
+ v0.6
+ - removed the request_region()s when compiling the MODULE sound.o
+ because we are not allowed (by the actual voxware structure) to
+ release_region()
+ v0.7 (pre ALPHA, not distributed)
+ - started porting this module to kernel 1.3.84. Dummy probe/attach
+ routines.
+ v0.8 (ALPHA)
+ - attached all the init routines.
+ v0.9 (BETA)
+ - Integrated with linux-pre2.0.7
+ - Integrated with configuration scripts.
+ - Cleaned up and beautyfied the code.
+ v0.9.9 (BETA)
+ - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
+ Now only the code configured is compiled in, with some memory saving.
+ v0.9.10
+ - Integration into the sound/lowlevel/ section of the sound driver.
+ - Re-organized the code.
+ v0.9.11 (not distributed)
+ - Rewritten the init interface-routines to initialize the AEDSP16 in
+ one shot.
+ - More cosmetics.
+ - SC-6600 support.
+ - More soft/hard configuration.
+ v0.9.12
+ - Refined the v0.9.11 code with conditional compilation to distinguish
+ between SC-6000 and SC-6600 code.
+ v1.0.0
+ - Prep for merging with OSS Lite and Linux kernel 2.1.13
+ - Corrected a bug in request/check/release region calls (thanks to the
+ new kernel exception handling).
+ v1.1
+ - Revamped for integration with new modularized sound drivers: to enhance
+ the flexibility of modular version, I have removed all the conditional
+ compilation for SBPRO, MPU and MSS code. Now it is all managed with
+ the ae_config structure.
+ v1.2
+ - Module informations added.
+ - Removed aedsp16_delay_10msec(), now using mdelay(10)
+ - All data and funcs moved to .*.init section.
+ v1.3
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/27
+ - got rid of check_region
+
+ Known Problems:
+ - Audio Excel DSP 16 III don't work with this driver.
+
+ Credits:
+ Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
+ lot in testing the 0.9.11 and 0.9.12 versions of this driver.
+
+ */
+
+
+#define VERSION "1.3" /* Version of Audio Excel DSP 16 driver */
+
+#undef AEDSP16_DEBUG /* Define this to 1 to enable debug code */
+#undef AEDSP16_DEBUG_MORE /* Define this to 1 to enable more debug */
+#undef AEDSP16_INFO /* Define this to 1 to enable info code */
+
+#if defined(AEDSP16_DEBUG)
+# define DBG(x) printk x
+# if defined(AEDSP16_DEBUG_MORE)
+# define DBG1(x) printk x
+# else
+# define DBG1(x)
+# endif
+#else
+# define DBG(x)
+# define DBG1(x)
+#endif
+
+/*
+ * Misc definitions
+ */
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Region Size for request/check/release region.
+ */
+#define IOBASE_REGION_SIZE 0x10
+
+/*
+ * Hardware related defaults
+ */
+#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */
+#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */
+#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */
+#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */
+
+/*
+ * Commands of AEDSP16's DSP (SBPRO+special).
+ * Some of them are COMMAND_xx, in the future they may change.
+ */
+#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */
+#define COMMAND_52 0x52 /* */
+#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */
+#define COMMAND_5C 0x5c /* */
+#define COMMAND_60 0x60 /* */
+#define COMMAND_66 0x66 /* */
+#define COMMAND_6C 0x6c /* */
+#define COMMAND_6E 0x6e /* */
+#define COMMAND_88 0x88 /* */
+#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */
+#define COMMAND_C5 0xc5 /* */
+#define GET_DSP_VERSION 0xe1 /* Get DSP Version */
+#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */
+
+/*
+ * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
+ * to have the actual I/O port.
+ * Register permissions are:
+ * (wo) == Write Only
+ * (ro) == Read Only
+ * (w-) == Write
+ * (r-) == Read
+ */
+#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */
+#define DSP_READ 0x0a /* offset of DSP READ (ro) */
+#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */
+#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */
+#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */
+#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */
+
+
+#define RETRY 10 /* Various retry values on I/O opera- */
+#define STATUSRETRY 1000 /* tions. Sometimes we have to */
+#define HARDRETRY 500000 /* wait for previous cmd to complete */
+
+/*
+ * Size of character arrays that store name and version of sound card
+ */
+#define CARDNAMELEN 15 /* Size of the card's name in chars */
+#define CARDVERLEN 2 /* Size of the card's version in chars */
+
+#if defined(CONFIG_SC6600)
+/*
+ * Bitmapped flags of hard configuration
+ */
+/*
+ * Decode macros (xl == low byte, xh = high byte)
+ */
+#define IOBASE(xl) ((xl & 0x01)?0x240:0x220)
+#define JOY(xl) (xl & 0x02)
+#define MPUADDR(xl) ( \
+ (xl & 0x0C)?0x330: \
+ (xl & 0x08)?0x320: \
+ (xl & 0x04)?0x310: \
+ 0x300)
+#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530)
+#define CDROM(xh) (xh & 0x20)
+#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200)
+/*
+ * Encode macros
+ */
+#define BLDIOBASE(xl, val) { \
+ xl &= ~0x01; \
+ if (val == 0x240) \
+ xl |= 0x01; \
+ }
+#define BLDJOY(xl, val) { \
+ xl &= ~0x02; \
+ if (val == 1) \
+ xl |= 0x02; \
+ }
+#define BLDMPUADDR(xl, val) { \
+ xl &= ~0x0C; \
+ switch (val) { \
+ case 0x330: \
+ xl |= 0x0C; \
+ break; \
+ case 0x320: \
+ xl |= 0x08; \
+ break; \
+ case 0x310: \
+ xl |= 0x04; \
+ break; \
+ case 0x300: \
+ xl |= 0x00; \
+ break; \
+ default: \
+ xl |= 0x00; \
+ break; \
+ } \
+ }
+#define BLDWSSADDR(xl, val) { \
+ xl &= ~0x10; \
+ if (val == 0xE80) \
+ xl |= 0x10; \
+ }
+#define BLDCDROM(xh, val) { \
+ xh &= ~0x20; \
+ if (val == 1) \
+ xh |= 0x20; \
+ }
+#define BLDCDROMADDR(xh, val) { \
+ int tmp = val; \
+ tmp -= 0x200; \
+ tmp >>= 4; \
+ tmp &= 0x1F; \
+ xh |= tmp; \
+ xh &= 0x7F; \
+ xh |= 0x40; \
+ }
+#endif /* CONFIG_SC6600 */
+
+/*
+ * Bit mapped flags for calling aedsp16_init_board(), and saving the current
+ * emulation mode.
+ */
+#define INIT_NONE (0 )
+#define INIT_SBPRO (1<<0)
+#define INIT_MSS (1<<1)
+#define INIT_MPU401 (1<<2)
+
+static int soft_cfg __initdata = 0; /* bitmapped config */
+static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */
+static int ver[CARDVERLEN] __initdata = {0, 0}; /* DSP Ver:
+ hi->ver[0] lo->ver[1] */
+
+#if defined(CONFIG_SC6600)
+static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */
+ __initdata = { 0, 0};
+#endif /* CONFIG_SC6600 */
+
+#if defined(CONFIG_SC6600)
+/* Decoded hard configuration */
+struct d_hcfg {
+ int iobase;
+ int joystick;
+ int mpubase;
+ int wssbase;
+ int cdrom;
+ int cdrombase;
+};
+
+static struct d_hcfg decoded_hcfg __initdata = {0, };
+
+#endif /* CONFIG_SC6600 */
+
+/* orVals contain the values to be or'ed */
+struct orVals {
+ int val; /* irq|mirq|dma */
+ int or; /* soft_cfg |= TheStruct.or */
+};
+
+/* aedsp16_info contain the audio card configuration */
+struct aedsp16_info {
+ int base_io; /* base I/O address for accessing card */
+ int irq; /* irq value for DSP I/O */
+ int mpu_irq; /* irq for mpu401 interface I/O */
+ int dma; /* dma value for DSP I/O */
+ int mss_base; /* base I/O for Microsoft Sound System */
+ int mpu_base; /* base I/O for MPU-401 emulation */
+ int init; /* Initialization status of the card */
+};
+
+/*
+ * Magic values that the DSP will eat when configuring irq/mirq/dma
+ */
+/* DSP IRQ conversion array */
+static struct orVals orIRQ[] __initdata = {
+ {0x05, 0x28},
+ {0x07, 0x08},
+ {0x09, 0x10},
+ {0x0a, 0x18},
+ {0x0b, 0x20},
+ {0x00, 0x00}
+};
+
+/* MPU-401 IRQ conversion array */
+static struct orVals orMIRQ[] __initdata = {
+ {0x05, 0x04},
+ {0x07, 0x44},
+ {0x09, 0x84},
+ {0x0a, 0xc4},
+ {0x00, 0x00}
+};
+
+/* DMA Channels conversion array */
+static struct orVals orDMA[] __initdata = {
+ {0x00, 0x01},
+ {0x01, 0x02},
+ {0x03, 0x03},
+ {0x00, 0x00}
+};
+
+static struct aedsp16_info ae_config = {
+ DEF_AEDSP16_IOB,
+ DEF_AEDSP16_IRQ,
+ DEF_AEDSP16_MRQ,
+ DEF_AEDSP16_DMA,
+ -1,
+ -1,
+ INIT_NONE
+};
+
+/*
+ * Buffers to store audio card informations
+ */
+static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, };
+static char DSPVersion[CARDVERLEN + 1] __initdata = {0, };
+
+static int __init aedsp16_wait_data(int port)
+{
+ int loop = STATUSRETRY;
+ unsigned char ret = 0;
+
+ DBG1(("aedsp16_wait_data (0x%x): ", port));
+
+ do {
+ ret = inb(port + DSP_DATAVAIL);
+ /*
+ * Wait for data available (bit 7 of ret == 1)
+ */
+ } while (!(ret & 0x80) && loop--);
+
+ if (ret & 0x80) {
+ DBG1(("success.\n"));
+ return TRUE;
+ }
+
+ DBG1(("failure.\n"));
+ return FALSE;
+}
+
+static int __init aedsp16_read(int port)
+{
+ int inbyte;
+
+ DBG((" Read DSP Byte (0x%x): ", port));
+
+ if (aedsp16_wait_data(port) == FALSE) {
+ DBG(("failure.\n"));
+ return -1;
+ }
+
+ inbyte = inb(port + DSP_READ);
+
+ DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
+
+ return inbyte;
+}
+
+static int __init aedsp16_test_dsp(int port)
+{
+ return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
+}
+
+static int __init aedsp16_dsp_reset(int port)
+{
+ /*
+ * Reset DSP
+ */
+
+ DBG(("Reset DSP:\n"));
+
+ outb(1, (port + DSP_RESET));
+ udelay(10);
+ outb(0, (port + DSP_RESET));
+ udelay(10);
+ udelay(10);
+ if (aedsp16_test_dsp(port) == TRUE) {
+ DBG(("success.\n"));
+ return TRUE;
+ } else
+ DBG(("failure.\n"));
+ return FALSE;
+}
+
+static int __init aedsp16_write(int port, int cmd)
+{
+ unsigned char ret;
+ int loop = HARDRETRY;
+
+ DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
+
+ do {
+ ret = inb(port + DSP_STATUS);
+ /*
+ * DSP ready to receive data if bit 7 of ret == 0
+ */
+ if (!(ret & 0x80)) {
+ outb(cmd, port + DSP_COMMAND);
+ DBG(("success.\n"));
+ return 0;
+ }
+ } while (loop--);
+
+ DBG(("timeout.\n"));
+ printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
+
+ return -1;
+}
+
+#if defined(CONFIG_SC6600)
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+void __init aedsp16_pinfo(void) {
+ DBG(("\n Base address: %x\n", decoded_hcfg.iobase));
+ DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not"));
+ DBG((" WSS addr : %x\n", decoded_hcfg.wssbase));
+ DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase));
+ DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
+ DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase));
+}
+#endif
+
+static void __init aedsp16_hard_decode(void) {
+
+ DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+/*
+ * Decode Cfg Bytes.
+ */
+ decoded_hcfg.iobase = IOBASE(hard_cfg[0]);
+ decoded_hcfg.joystick = JOY(hard_cfg[0]);
+ decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]);
+ decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]);
+ decoded_hcfg.cdrom = CDROM(hard_cfg[1]);
+ decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]);
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+ printk(" Original sound card configuration:\n");
+ aedsp16_pinfo();
+#endif
+
+/*
+ * Now set up the real kernel configuration.
+ */
+ decoded_hcfg.iobase = ae_config.base_io;
+ decoded_hcfg.wssbase = ae_config.mss_base;
+ decoded_hcfg.mpubase = ae_config.mpu_base;
+
+#if defined(CONFIG_SC6600_JOY)
+ decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */
+#endif
+#if defined(CONFIG_SC6600_CDROM)
+ decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
+#endif
+#if defined(CONFIG_SC6600_CDROMBASE)
+ decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */
+#endif
+
+#if defined(AEDSP16_DEBUG)
+ DBG((" New Values:\n"));
+ aedsp16_pinfo();
+#endif
+
+ DBG(("success.\n"));
+}
+
+static void __init aedsp16_hard_encode(void) {
+
+ DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+ hard_cfg[0] = 0;
+ hard_cfg[1] = 0;
+
+ hard_cfg[0] |= 0x20;
+
+ BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
+ BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
+ BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
+ BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
+ BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
+ BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
+
+#if defined(AEDSP16_DEBUG)
+ aedsp16_pinfo();
+#endif
+
+ DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+ DBG(("success.\n"));
+
+}
+
+static int __init aedsp16_hard_write(int port) {
+
+ DBG(("aedsp16_hard_write:\n"));
+
+ if (aedsp16_write(port, COMMAND_6C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, COMMAND_5C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, hard_cfg[0])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, hard_cfg[1])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, COMMAND_C5)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_hard_read(int port) {
+
+ DBG(("aedsp16_hard_read:\n"));
+
+ if (aedsp16_write(port, READ_HARD_CFG)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_read(port) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_ext_cfg_write(int port) {
+
+ int extcfg, val;
+
+ if (aedsp16_write(port, COMMAND_66)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
+ return FALSE;
+ }
+
+ extcfg = 7;
+ if (decoded_hcfg.cdrom != 2)
+ extcfg = 0x0F;
+ if ((decoded_hcfg.cdrom == 4) ||
+ (decoded_hcfg.cdrom == 3))
+ extcfg &= ~2;
+ if (decoded_hcfg.cdrombase == 0)
+ extcfg &= ~2;
+ if (decoded_hcfg.mpubase == 0)
+ extcfg &= ~1;
+
+ if (aedsp16_write(port, extcfg)) {
+ printk("[AEDSP16] Write extcfg: failed!\n");
+ return FALSE;
+ }
+ if (aedsp16_write(port, 0)) {
+ printk("[AEDSP16] Write extcfg: failed!\n");
+ return FALSE;
+ }
+ if (decoded_hcfg.cdrom == 3) {
+ if (aedsp16_write(port, COMMAND_52)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+ return FALSE;
+ }
+ if ((val = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
+ , COMMAND_52);
+ return FALSE;
+ }
+ val &= 0x7F;
+ if (aedsp16_write(port, COMMAND_60)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+ return FALSE;
+ }
+ if (aedsp16_write(port, val)) {
+ printk("[AEDSP16] Write val: failed!\n");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif /* CONFIG_SC6600 */
+
+static int __init aedsp16_cfg_write(int port) {
+ if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+ return FALSE;
+ }
+ if (aedsp16_write(port, soft_cfg)) {
+ printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int __init aedsp16_init_mss(int port)
+{
+ DBG(("aedsp16_init_mss:\n"));
+
+ mdelay(10);
+
+ if (aedsp16_write(port, DSP_INIT_MSS)) {
+ printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
+ DSP_INIT_MSS);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ mdelay(10);
+
+ if (aedsp16_cfg_write(port) == FALSE)
+ return FALSE;
+
+ outb(soft_cfg_mss, ae_config.mss_base);
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_setup_board(int port) {
+ int loop = RETRY;
+
+#if defined(CONFIG_SC6600)
+ int val = 0;
+
+ if (aedsp16_hard_read(port) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_write(port, COMMAND_52)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+ return FALSE;
+ }
+
+ if ((val = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ COMMAND_52);
+ return FALSE;
+ }
+#endif
+
+ do {
+ if (aedsp16_write(port, COMMAND_88)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
+ return FALSE;
+ }
+ mdelay(10);
+ } while ((aedsp16_wait_data(port) == FALSE) && loop--);
+
+ if (aedsp16_read(port) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ COMMAND_88);
+ return FALSE;
+ }
+
+#if !defined(CONFIG_SC6600)
+ if (aedsp16_write(port, COMMAND_5C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+ return FALSE;
+ }
+#endif
+
+ if (aedsp16_cfg_write(port) == FALSE)
+ return FALSE;
+
+#if defined(CONFIG_SC6600)
+ if (aedsp16_write(port, COMMAND_60)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+ return FALSE;
+ }
+ if (aedsp16_write(port, val)) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", val);
+ return FALSE;
+ }
+ if (aedsp16_write(port, COMMAND_6E)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
+ return FALSE;
+ }
+ if (aedsp16_write(port, ver[0])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
+ return FALSE;
+ }
+ if (aedsp16_write(port, ver[1])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
+ return FALSE;
+ }
+
+ if (aedsp16_hard_write(port) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_write(port, COMMAND_5C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+ return FALSE;
+ }
+
+#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
+ if (aedsp16_cfg_write(port) == FALSE)
+ return FALSE;
+#endif
+
+#endif
+
+ return TRUE;
+}
+
+static int __init aedsp16_stdcfg(int port) {
+ if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+ return FALSE;
+ }
+ /*
+ * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
+ */
+ if (aedsp16_write(port, 0x0A)) {
+ printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int __init aedsp16_dsp_version(int port)
+{
+ int len = 0;
+ int ret;
+
+ DBG(("Get DSP Version:\n"));
+
+ if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+
+ do {
+ if ((ret = aedsp16_read(port)) == -1) {
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+ /*
+ * We already know how many int are stored (2), so we know when the
+ * string is finished.
+ */
+ ver[len++] = ret;
+ } while (len < CARDVERLEN);
+ sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_dsp_copyright(int port)
+{
+ int len = 0;
+ int ret;
+
+ DBG(("Get DSP Copyright:\n"));
+
+ if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+
+ do {
+ if ((ret = aedsp16_read(port)) == -1) {
+ /*
+ * If no more data available, return to the caller, no error if len>0.
+ * We have no other way to know when the string is finished.
+ */
+ if (len)
+ break;
+ else {
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+ }
+
+ DSPCopyright[len++] = ret;
+
+ } while (len < CARDNAMELEN);
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static void __init aedsp16_init_tables(void)
+{
+ int i = 0;
+
+ memset(DSPCopyright, 0, CARDNAMELEN + 1);
+ memset(DSPVersion, 0, CARDVERLEN + 1);
+
+ for (i = 0; orIRQ[i].or; i++)
+ if (orIRQ[i].val == ae_config.irq) {
+ soft_cfg |= orIRQ[i].or;
+ soft_cfg_mss |= orIRQ[i].or;
+ }
+
+ for (i = 0; orMIRQ[i].or; i++)
+ if (orMIRQ[i].or == ae_config.mpu_irq)
+ soft_cfg |= orMIRQ[i].or;
+
+ for (i = 0; orDMA[i].or; i++)
+ if (orDMA[i].val == ae_config.dma) {
+ soft_cfg |= orDMA[i].or;
+ soft_cfg_mss |= orDMA[i].or;
+ }
+}
+
+static int __init aedsp16_init_board(void)
+{
+ aedsp16_init_tables();
+
+ if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
+ return FALSE;
+ }
+ if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
+ return FALSE;
+ }
+
+ /*
+ * My AEDSP16 card return SC-6000 in DSPCopyright, so
+ * if we have something different, we have to be warned.
+ */
+ if (strcmp("SC-6000", DSPCopyright))
+ printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
+
+ if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+ return FALSE;
+ }
+
+#if defined(CONFIG_SC6600)
+ if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+ return FALSE;
+ }
+
+ aedsp16_hard_decode();
+
+ aedsp16_hard_encode();
+
+ if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
+ return FALSE;
+ }
+#endif /* CONFIG_SC6600 */
+
+ if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_setup_board: failed!\n");
+ return FALSE;
+ }
+
+ if (ae_config.mss_base != -1) {
+ if (ae_config.init & INIT_MSS) {
+ if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] Can not initialize"
+ "Microsoft Sound System mode.\n");
+ return FALSE;
+ }
+ }
+ }
+
+#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+
+ printk("Audio Excel DSP 16 init v%s (%s %s) [",
+ VERSION, DSPCopyright,
+ DSPVersion);
+
+ if (ae_config.mpu_base != -1) {
+ if (ae_config.init & INIT_MPU401) {
+ printk("MPU401");
+ if ((ae_config.init & INIT_MSS) ||
+ (ae_config.init & INIT_SBPRO))
+ printk(" ");
+ }
+ }
+
+ if (ae_config.mss_base == -1) {
+ if (ae_config.init & INIT_SBPRO) {
+ printk("SBPro");
+ if (ae_config.init & INIT_MSS)
+ printk(" ");
+ }
+ }
+
+ if (ae_config.mss_base != -1)
+ if (ae_config.init & INIT_MSS)
+ printk("MSS");
+
+ printk("]\n");
+#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
+
+ mdelay(10);
+
+ return TRUE;
+}
+
+static int __init init_aedsp16_sb(void)
+{
+ DBG(("init_aedsp16_sb: "));
+
+/*
+ * If the card is already init'ed MSS, we can not init it to SBPRO too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+ if (ae_config.init & INIT_MSS)
+ return FALSE;
+ if (ae_config.init & INIT_SBPRO)
+ return FALSE;
+
+ ae_config.init |= INIT_SBPRO;
+
+ DBG(("done.\n"));
+
+ return TRUE;
+}
+
+static void uninit_aedsp16_sb(void)
+{
+ DBG(("uninit_aedsp16_sb: "));
+
+ ae_config.init &= ~INIT_SBPRO;
+
+ DBG(("done.\n"));
+}
+
+static int __init init_aedsp16_mss(void)
+{
+ DBG(("init_aedsp16_mss: "));
+
+/*
+ * If the card is already init'ed SBPRO, we can not init it to MSS too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+ if (ae_config.init & INIT_SBPRO)
+ return FALSE;
+ if (ae_config.init & INIT_MSS)
+ return FALSE;
+/*
+ * We must allocate the CONFIG_AEDSP16_BASE region too because these are the
+ * I/O ports to access card's control registers.
+ */
+ if (!(ae_config.init & INIT_MPU401)) {
+ if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+ "aedsp16 (base)")) {
+ printk(
+ "AEDSP16 BASE I/O port region is already in use.\n");
+ return FALSE;
+ }
+ }
+
+ ae_config.init |= INIT_MSS;
+
+ DBG(("done.\n"));
+
+ return TRUE;
+}
+
+static void uninit_aedsp16_mss(void)
+{
+ DBG(("uninit_aedsp16_mss: "));
+
+ if ((!(ae_config.init & INIT_MPU401)) &&
+ (ae_config.init & INIT_MSS)) {
+ release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+ DBG(("AEDSP16 base region released.\n"));
+ }
+
+ ae_config.init &= ~INIT_MSS;
+ DBG(("done.\n"));
+}
+
+static int __init init_aedsp16_mpu(void)
+{
+ DBG(("init_aedsp16_mpu: "));
+
+ if (ae_config.init & INIT_MPU401)
+ return FALSE;
+
+/*
+ * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O
+ * ports to access card's control registers.
+ */
+ if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
+ if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+ "aedsp16 (base)")) {
+ printk(
+ "AEDSP16 BASE I/O port region is already in use.\n");
+ return FALSE;
+ }
+ }
+
+ ae_config.init |= INIT_MPU401;
+
+ DBG(("done.\n"));
+
+ return TRUE;
+}
+
+static void uninit_aedsp16_mpu(void)
+{
+ DBG(("uninit_aedsp16_mpu: "));
+
+ if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
+ (ae_config.init & INIT_MPU401)) {
+ release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+ DBG(("AEDSP16 base region released.\n"));
+ }
+
+ ae_config.init &= ~INIT_MPU401;
+
+ DBG(("done.\n"));
+}
+
+static int __init init_aedsp16(void)
+{
+ int initialized = FALSE;
+
+ DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
+ ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
+
+ if (ae_config.mss_base == -1) {
+ if (init_aedsp16_sb() == FALSE) {
+ uninit_aedsp16_sb();
+ } else {
+ initialized = TRUE;
+ }
+ }
+
+ if (ae_config.mpu_base != -1) {
+ if (init_aedsp16_mpu() == FALSE) {
+ uninit_aedsp16_mpu();
+ } else {
+ initialized = TRUE;
+ }
+ }
+
+/*
+ * In the sequence of init routines, the MSS init MUST be the last!
+ * This because of the special register programming the MSS mode needs.
+ * A board reset would disable the MSS mode restoring the default SBPRO
+ * mode.
+ */
+ if (ae_config.mss_base != -1) {
+ if (init_aedsp16_mss() == FALSE) {
+ uninit_aedsp16_mss();
+ } else {
+ initialized = TRUE;
+ }
+ }
+
+ if (initialized)
+ initialized = aedsp16_init_board();
+ return initialized;
+}
+
+static void __exit uninit_aedsp16(void)
+{
+ if (ae_config.mss_base != -1)
+ uninit_aedsp16_mss();
+ else
+ uninit_aedsp16_sb();
+ if (ae_config.mpu_base != -1)
+ uninit_aedsp16_mpu();
+}
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata mpu_irq = -1;
+static int __initdata mss_base = -1;
+static int __initdata mpu_base = -1;
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
+module_param(irq, int, 0);
+MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
+module_param(dma, int, 0);
+MODULE_PARM_DESC(dma, "dma line (0 1 3)");
+module_param(mpu_irq, int, 0);
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
+module_param(mss_base, int, 0);
+MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
+module_param(mpu_base, int, 0);
+MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
+MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
+MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
+MODULE_LICENSE("GPL");
+
+static int __init do_init_aedsp16(void) {
+ printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
+ if (io == -1 || dma == -1 || irq == -1) {
+ printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
+ return -EINVAL;
+ }
+
+ ae_config.base_io = io;
+ ae_config.irq = irq;
+ ae_config.dma = dma;
+
+ ae_config.mss_base = mss_base;
+ ae_config.mpu_base = mpu_base;
+ ae_config.mpu_irq = mpu_irq;
+
+ if (init_aedsp16() == FALSE) {
+ printk(KERN_ERR "aedsp16: initialization failed\n");
+ /*
+ * XXX
+ * What error should we return here ?
+ */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void __exit cleanup_aedsp16(void) {
+ uninit_aedsp16();
+}
+
+module_init(do_init_aedsp16);
+module_exit(cleanup_aedsp16);
+
+#ifndef MODULE
+static int __init setup_aedsp16(char *str)
+{
+ /* io, irq, dma, mss_io, mpu_io, mpu_irq */
+ int ints[7];
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ io = ints[1];
+ irq = ints[2];
+ dma = ints[3];
+ mss_base = ints[4];
+ mpu_base = ints[5];
+ mpu_irq = ints[6];
+ return 1;
+}
+
+__setup("aedsp16=", setup_aedsp16);
+#endif
diff --git a/sound/oss/ali5455.c b/sound/oss/ali5455.c
new file mode 100644
index 000000000000..9c9e6c0410f2
--- /dev/null
+++ b/sound/oss/ali5455.c
@@ -0,0 +1,3733 @@
+/*
+ * ALI ali5455 and friends ICH driver for Linux
+ * LEI HU <Lei_Hu@ali.com.tw>
+ *
+ * Built from:
+ * drivers/sound/i810_audio
+ *
+ * The ALi 5455 is similar but not quite identical to the Intel ICH
+ * series of controllers. Its easier to keep the driver separated from
+ * the i810 driver.
+ *
+ * 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.
+ *
+ *
+ * ALi 5455 theory of operation
+ *
+ * The chipset provides three DMA channels that talk to an AC97
+ * CODEC (AC97 is a digital/analog mixer standard). At its simplest
+ * you get 48Khz audio with basic volume and mixer controls. At the
+ * best you get rate adaption in the codec. We set the card up so
+ * that we never take completion interrupts but instead keep the card
+ * chasing its tail around a ring buffer. This is needed for mmap
+ * mode audio and happens to work rather well for non-mmap modes too.
+ *
+ * The board has one output channel for PCM audio (supported) and
+ * a stereo line in and mono microphone input. Again these are normally
+ * locked to 48Khz only. Right now recording is not finished.
+ *
+ * There is no midi support, no synth support. Use timidity. To get
+ * esd working you need to use esd -r 48000 as it won't probe 48KHz
+ * by default. mpg123 can't handle 48Khz only audio so use xmms.
+ *
+ * If you need to force a specific rate set the clocking= option
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+
+#ifndef PCI_DEVICE_ID_ALI_5455
+#define PCI_DEVICE_ID_ALI_5455 0x5455
+#endif
+
+#ifndef PCI_VENDOR_ID_ALI
+#define PCI_VENDOR_ID_ALI 0x10b9
+#endif
+
+static int strict_clocking = 0;
+static unsigned int clocking = 0;
+static unsigned int codec_pcmout_share_spdif_locked = 0;
+static unsigned int codec_independent_spdif_locked = 0;
+static unsigned int controller_pcmout_share_spdif_locked = 0;
+static unsigned int controller_independent_spdif_locked = 0;
+static unsigned int globel = 0;
+
+#define ADC_RUNNING 1
+#define DAC_RUNNING 2
+#define CODEC_SPDIFOUT_RUNNING 8
+#define CONTROLLER_SPDIFOUT_RUNNING 4
+
+#define SPDIF_ENABLE_OUTPUT 4 /* bits 0,1 are PCM */
+
+#define ALI5455_FMT_16BIT 1
+#define ALI5455_FMT_STEREO 2
+#define ALI5455_FMT_MASK 3
+
+#define SPDIF_ON 0x0004
+#define SURR_ON 0x0010
+#define CENTER_LFE_ON 0x0020
+#define VOL_MUTED 0x8000
+
+
+#define ALI_SPDIF_OUT_CH_STATUS 0xbf
+/* the 810's array of pointers to data buffers */
+
+struct sg_item {
+#define BUSADDR_MASK 0xFFFFFFFE
+ u32 busaddr;
+#define CON_IOC 0x80000000 /* interrupt on completion */
+#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */
+#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */
+ u32 control;
+};
+
+/* an instance of the ali channel */
+#define SG_LEN 32
+struct ali_channel {
+ /* these sg guys should probably be allocated
+ separately as nocache. Must be 8 byte aligned */
+ struct sg_item sg[SG_LEN]; /* 32*8 */
+ u32 offset; /* 4 */
+ u32 port; /* 4 */
+ u32 used;
+ u32 num;
+};
+
+/*
+ * we have 3 separate dma engines. pcm in, pcm out, and mic.
+ * each dma engine has controlling registers. These goofy
+ * names are from the datasheet, but make it easy to write
+ * code while leafing through it.
+ */
+
+#define ENUM_ENGINE(PRE,DIG) \
+enum { \
+ PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \
+ PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \
+ PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \
+ PRE##_SR = 0x##DIG##6, /* Status Register */ \
+ PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \
+ PRE##_CR = 0x##DIG##b /* Control Register */ \
+}
+
+ENUM_ENGINE(OFF, 0); /* Offsets */
+ENUM_ENGINE(PI, 4); /* PCM In */
+ENUM_ENGINE(PO, 5); /* PCM Out */
+ENUM_ENGINE(MC, 6); /* Mic In */
+ENUM_ENGINE(CODECSPDIFOUT, 7); /* CODEC SPDIF OUT */
+ENUM_ENGINE(CONTROLLERSPDIFIN, A); /* CONTROLLER SPDIF In */
+ENUM_ENGINE(CONTROLLERSPDIFOUT, B); /* CONTROLLER SPDIF OUT */
+
+
+enum {
+ ALI_SCR = 0x00, /* System Control Register */
+ ALI_SSR = 0x04, /* System Status Register */
+ ALI_DMACR = 0x08, /* DMA Control Register */
+ ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */
+ ALI_INTERFACECR = 0x10, /* Interface Control Register */
+ ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */
+ ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */
+ ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */
+ ALI_CPR = 0x20, /* Command Port Register */
+ ALI_SPR = 0x24, /* Status Port Register */
+ ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */
+ ALI_TTSR = 0x30, /* Transmit Tag Slot Register */
+ ALI_RTSR = 0x34, /* Receive Tag Slot Register */
+ ALI_CSPSR = 0x38, /* Command/Status Port Status Register */
+ ALI_CAS = 0x3c, /* Codec Write Semaphore Register */
+ ALI_SPDIFCSR = 0xf8, /* spdif channel status register */
+ ALI_SPDIFICS = 0xfc /* spdif interface control/status */
+};
+
+// x-status register(x:pcm in ,pcm out, mic in,)
+/* interrupts for a dma engine */
+#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */
+#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */
+#define DMA_INT_LVI (1<<2) /* last valid done */
+#define DMA_INT_CELV (1<<1) /* last valid is current */
+#define DMA_INT_DCH (1) /* DMA Controller Halted (happens on LVI interrupts) */ //not eqult intel
+#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI)
+
+/* interrupts for the whole chip */// by interrupt status register finish
+
+#define INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */
+#define INT_SPDIFIN (1<<22)
+#define INT_CODECSPDIFOUT (1<<19)
+#define INT_MICIN (1<<18)
+#define INT_PCMOUT (1<<17)
+#define INT_PCMIN (1<<16)
+#define INT_CPRAIS (1<<7)
+#define INT_SPRAIS (1<<5)
+#define INT_GPIO (1<<1)
+#define INT_MASK (INT_SPDIFOUT|INT_CODECSPDIFOUT|INT_MICIN|INT_PCMOUT|INT_PCMIN)
+
+#define DRIVER_VERSION "0.02ac"
+
+/* magic numbers to protect our data structures */
+#define ALI5455_CARD_MAGIC 0x5072696E /* "Prin" */
+#define ALI5455_STATE_MAGIC 0x63657373 /* "cess" */
+#define ALI5455_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */
+#define NR_HW_CH 5 //I think 5 channel
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define NR_AC97 2
+
+/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */
+/* stream at a minimum for this card to be happy */
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */
+/* values are one less than might be expected */
+static const unsigned sample_shift[] = { -1, 0, 0, 1 };
+
+#define ALI5455
+static char *card_names[] = {
+ "ALI 5455"
+};
+
+static struct pci_device_id ali_pci_tbl[] = {
+ {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5455,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI5455},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, ali_pci_tbl);
+
+#ifdef CONFIG_PM
+#define PM_SUSPENDED(card) (card->pm_suspended)
+#else
+#define PM_SUSPENDED(card) (0)
+#endif
+
+/* "software" or virtual channel, an instance of opened /dev/dsp */
+struct ali_state {
+ unsigned int magic;
+ struct ali_card *card; /* Card info */
+
+ /* single open lock mechanism, only used for recording */
+ struct semaphore open_sem;
+ wait_queue_head_t open_wait;
+
+ /* file mode */
+ mode_t open_mode;
+
+ /* virtual channel number */
+ int virt;
+
+#ifdef CONFIG_PM
+ unsigned int pm_saved_dac_rate, pm_saved_adc_rate;
+#endif
+ struct dmabuf {
+ /* wave sample stuff */
+ unsigned int rate;
+ unsigned char fmt, enable, trigger;
+
+ /* hardware channel */
+ struct ali_channel *read_channel;
+ struct ali_channel *write_channel;
+ struct ali_channel *codec_spdifout_channel;
+ struct ali_channel *controller_spdifout_channel;
+
+ /* OSS buffer management stuff */
+ void *rawbuf;
+ dma_addr_t dma_handle;
+ unsigned buforder;
+ unsigned numfrag;
+ unsigned fragshift;
+
+ /* our buffer acts like a circular ring */
+ unsigned hwptr; /* where dma last started, updated by update_ptr */
+ unsigned swptr; /* where driver last clear/filled, updated by read/write */
+ int count; /* bytes to be consumed or been generated by dma machine */
+ unsigned total_bytes; /* total bytes dmaed by hardware */
+
+ unsigned error; /* number of over/underruns */
+ wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */
+
+ /* redundant, but makes calculations easier */
+ /* what the hardware uses */
+ unsigned dmasize;
+ unsigned fragsize;
+ unsigned fragsamples;
+
+ /* what we tell the user to expect */
+ unsigned userfrags;
+ unsigned userfragsize;
+
+ /* OSS stuff */
+ unsigned mapped:1;
+ unsigned ready:1;
+ unsigned update_flag;
+ unsigned ossfragsize;
+ unsigned ossmaxfrags;
+ unsigned subdivision;
+ } dmabuf;
+};
+
+
+struct ali_card {
+ struct ali_channel channel[5];
+ unsigned int magic;
+
+ /* We keep ali5455 cards in a linked list */
+ struct ali_card *next;
+
+ /* The ali has a certain amount of cross channel interaction
+ so we use a single per card lock */
+ spinlock_t lock;
+ spinlock_t ac97_lock;
+
+ /* PCI device stuff */
+ struct pci_dev *pci_dev;
+ u16 pci_id;
+#ifdef CONFIG_PM
+ u16 pm_suspended;
+ int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97];
+#endif
+ /* soundcore stuff */
+ int dev_audio;
+
+ /* structures for abstraction of hardware facilities, codecs, banks and channels */
+ struct ac97_codec *ac97_codec[NR_AC97];
+ struct ali_state *states[NR_HW_CH];
+
+ u16 ac97_features;
+ u16 ac97_status;
+ u16 channels;
+
+ /* hardware resources */
+ unsigned long iobase;
+
+ u32 irq;
+
+ /* Function support */
+ struct ali_channel *(*alloc_pcm_channel) (struct ali_card *);
+ struct ali_channel *(*alloc_rec_pcm_channel) (struct ali_card *);
+ struct ali_channel *(*alloc_rec_mic_channel) (struct ali_card *);
+ struct ali_channel *(*alloc_codec_spdifout_channel) (struct ali_card *);
+ struct ali_channel *(*alloc_controller_spdifout_channel) (struct ali_card *);
+ void (*free_pcm_channel) (struct ali_card *, int chan);
+
+ /* We have a *very* long init time possibly, so use this to block */
+ /* attempts to open our devices before we are ready (stops oops'es) */
+ int initializing;
+};
+
+
+static struct ali_card *devs = NULL;
+
+static int ali_open_mixdev(struct inode *inode, struct file *file);
+static int ali_ioctl_mixdev(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg);
+static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
+
+static struct ali_channel *ali_alloc_pcm_channel(struct ali_card *card)
+{
+ if (card->channel[1].used == 1)
+ return NULL;
+ card->channel[1].used = 1;
+ return &card->channel[1];
+}
+
+static struct ali_channel *ali_alloc_rec_pcm_channel(struct ali_card *card)
+{
+ if (card->channel[0].used == 1)
+ return NULL;
+ card->channel[0].used = 1;
+ return &card->channel[0];
+}
+
+static struct ali_channel *ali_alloc_rec_mic_channel(struct ali_card *card)
+{
+ if (card->channel[2].used == 1)
+ return NULL;
+ card->channel[2].used = 1;
+ return &card->channel[2];
+}
+
+static struct ali_channel *ali_alloc_codec_spdifout_channel(struct ali_card *card)
+{
+ if (card->channel[3].used == 1)
+ return NULL;
+ card->channel[3].used = 1;
+ return &card->channel[3];
+}
+
+static struct ali_channel *ali_alloc_controller_spdifout_channel(struct ali_card *card)
+{
+ if (card->channel[4].used == 1)
+ return NULL;
+ card->channel[4].used = 1;
+ return &card->channel[4];
+}
+static void ali_free_pcm_channel(struct ali_card *card, int channel)
+{
+ card->channel[channel].used = 0;
+}
+
+
+//add support codec spdif out
+static int ali_valid_spdif_rate(struct ac97_codec *codec, int rate)
+{
+ unsigned long id = 0L;
+
+ id = (ali_ac97_get(codec, AC97_VENDOR_ID1) << 16);
+ id |= ali_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff;
+ switch (id) {
+ case 0x41445361: /* AD1886 */
+ if (rate == 48000) {
+ return 1;
+ }
+ break;
+ case 0x414c4720: /* ALC650 */
+ if (rate == 48000) {
+ return 1;
+ }
+ break;
+ default: /* all other codecs, until we know otherwiae */
+ if (rate == 48000 || rate == 44100 || rate == 32000) {
+ return 1;
+ }
+ break;
+ }
+ return (0);
+}
+
+/* ali_set_spdif_output
+ *
+ * Configure the S/PDIF output transmitter. When we turn on
+ * S/PDIF, we turn off the analog output. This may not be
+ * the right thing to do.
+ *
+ * Assumptions:
+ * The DSP sample rate must already be set to a supported
+ * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort.
+ */
+static void ali_set_spdif_output(struct ali_state *state, int slots,
+ int rate)
+{
+ int vol;
+ int aud_reg;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
+
+ if (!(state->card->ac97_features & 4)) {
+ state->card->ac97_status &= ~SPDIF_ON;
+ } else {
+ if (slots == -1) { /* Turn off S/PDIF */
+ aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS);
+ ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+
+ /* If the volume wasn't muted before we turned on S/PDIF, unmute it */
+ if (!(state->card->ac97_status & VOL_MUTED)) {
+ aud_reg = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO);
+ ali_ac97_set(codec, AC97_MASTER_VOL_STEREO,
+ (aud_reg & ~VOL_MUTED));
+ }
+ state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON);
+ return;
+ }
+
+ vol = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO);
+ state->card->ac97_status = vol & VOL_MUTED;
+
+ /* Set S/PDIF transmitter sample rate */
+ aud_reg = ali_ac97_get(codec, AC97_SPDIF_CONTROL);
+ switch (rate) {
+ case 32000:
+ aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
+ break;
+ case 44100:
+ aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
+ break;
+ case 48000:
+ aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
+ break;
+ default:
+ /* turn off S/PDIF */
+ aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS);
+ ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+ state->card->ac97_status &= ~SPDIF_ON;
+ return;
+ }
+
+ ali_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg);
+
+ aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS);
+ aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_SPDIF;
+ ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);
+
+ aud_reg = ali_ac97_get(codec, AC97_POWER_CONTROL);
+ aud_reg |= 0x0002;
+ ali_ac97_set(codec, AC97_POWER_CONTROL, aud_reg);
+ udelay(1);
+
+ state->card->ac97_status |= SPDIF_ON;
+
+ /* Check to make sure the configuration is valid */
+ aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS);
+ if (!(aud_reg & 0x0400)) {
+ /* turn off S/PDIF */
+ ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+ state->card->ac97_status &= ~SPDIF_ON;
+ return;
+ }
+ if (codec_independent_spdif_locked > 0) {
+ aud_reg = ali_ac97_get(codec, 0x6a);
+ ali_ac97_set(codec, 0x6a, (aud_reg & 0xefff));
+ }
+ /* Mute the analog output */
+ /* Should this only mute the PCM volume??? */
+ }
+}
+
+/* ali_set_dac_channels
+ *
+ * Configure the codec's multi-channel DACs
+ *
+ * The logic is backwards. Setting the bit to 1 turns off the DAC.
+ *
+ * What about the ICH? We currently configure it using the
+ * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC,
+ * does that imply that we want the ICH set to support
+ * these channels?
+ *
+ * TODO:
+ * vailidate that the codec really supports these DACs
+ * before turning them on.
+ */
+static void ali_set_dac_channels(struct ali_state *state, int channel)
+{
+ int aud_reg;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
+
+ aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS);
+ aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK;
+ state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON);
+
+ switch (channel) {
+ case 2: /* always enabled */
+ break;
+ case 4:
+ aud_reg &= ~AC97_EA_PRJ;
+ state->card->ac97_status |= SURR_ON;
+ break;
+ case 6:
+ aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK);
+ state->card->ac97_status |= SURR_ON | CENTER_LFE_ON;
+ break;
+ default:
+ break;
+ }
+ ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);
+
+}
+
+/* set playback sample rate */
+static unsigned int ali_set_dac_rate(struct ali_state *state,
+ unsigned int rate)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ u32 new_rate;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
+
+ if (!(state->card->ac97_features & 0x0001)) {
+ dmabuf->rate = clocking;
+ return clocking;
+ }
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 8000)
+ rate = 8000;
+ dmabuf->rate = rate;
+
+ /*
+ * Adjust for misclocked crap
+ */
+
+ rate = (rate * clocking) / 48000;
+
+ if (strict_clocking && rate < 8000) {
+ rate = 8000;
+ dmabuf->rate = (rate * 48000) / clocking;
+ }
+
+ new_rate = ac97_set_dac_rate(codec, rate);
+ if (new_rate != rate) {
+ dmabuf->rate = (new_rate * 48000) / clocking;
+ }
+ rate = new_rate;
+ return dmabuf->rate;
+}
+
+/* set recording sample rate */
+static unsigned int ali_set_adc_rate(struct ali_state *state,
+ unsigned int rate)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ u32 new_rate;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
+
+ if (!(state->card->ac97_features & 0x0001)) {
+ dmabuf->rate = clocking;
+ return clocking;
+ }
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 8000)
+ rate = 8000;
+ dmabuf->rate = rate;
+
+ /*
+ * Adjust for misclocked crap
+ */
+
+ rate = (rate * clocking) / 48000;
+ if (strict_clocking && rate < 8000) {
+ rate = 8000;
+ dmabuf->rate = (rate * 48000) / clocking;
+ }
+
+ new_rate = ac97_set_adc_rate(codec, rate);
+
+ if (new_rate != rate) {
+ dmabuf->rate = (new_rate * 48000) / clocking;
+ rate = new_rate;
+ }
+ return dmabuf->rate;
+}
+
+/* set codec independent spdifout sample rate */
+static unsigned int ali_set_codecspdifout_rate(struct ali_state *state,
+ unsigned int rate)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+
+ if (!(state->card->ac97_features & 0x0001)) {
+ dmabuf->rate = clocking;
+ return clocking;
+ }
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 8000)
+ rate = 8000;
+ dmabuf->rate = rate;
+
+ return dmabuf->rate;
+}
+
+/* set controller independent spdif out function sample rate */
+static void ali_set_spdifout_rate(struct ali_state *state,
+ unsigned int rate)
+{
+ unsigned char ch_st_sel;
+ unsigned short status_rate;
+
+ switch (rate) {
+ case 44100:
+ status_rate = 0;
+ break;
+ case 32000:
+ status_rate = 0x300;
+ break;
+ case 48000:
+ default:
+ status_rate = 0x200;
+ break;
+ }
+
+ ch_st_sel = inb(state->card->iobase + ALI_SPDIFICS) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out
+
+ ch_st_sel |= 0x80; //select right
+ outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS));
+ outb(status_rate | 0x20, (state->card->iobase + ALI_SPDIFCSR + 2));
+
+ ch_st_sel &= (~0x80); //select left
+ outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS));
+ outw(status_rate | 0x10, (state->card->iobase + ALI_SPDIFCSR + 2));
+}
+
+/* get current playback/recording dma buffer pointer (byte offset from LBA),
+ called with spinlock held! */
+
+static inline unsigned ali_get_dma_addr(struct ali_state *state, int rec)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned int civ, offset, port, port_picb;
+ unsigned int data;
+
+ if (!dmabuf->enable)
+ return 0;
+
+ if (rec == 1)
+ port = state->card->iobase + dmabuf->read_channel->port;
+ else if (rec == 2)
+ port = state->card->iobase + dmabuf->codec_spdifout_channel->port;
+ else if (rec == 3)
+ port = state->card->iobase + dmabuf->controller_spdifout_channel->port;
+ else
+ port = state->card->iobase + dmabuf->write_channel->port;
+
+ port_picb = port + OFF_PICB;
+
+ do {
+ civ = inb(port + OFF_CIV) & 31;
+ offset = inw(port_picb);
+ /* Must have a delay here! */
+ if (offset == 0)
+ udelay(1);
+
+ /* Reread both registers and make sure that that total
+ * offset from the first reading to the second is 0.
+ * There is an issue with SiS hardware where it will count
+ * picb down to 0, then update civ to the next value,
+ * then set the new picb to fragsize bytes. We can catch
+ * it between the civ update and the picb update, making
+ * it look as though we are 1 fragsize ahead of where we
+ * are. The next to we get the address though, it will
+ * be back in thdelay is more than long enough
+ * that we won't have to worry about the chip still being
+ * out of sync with reality ;-)
+ */
+ } while (civ != (inb(port + OFF_CIV) & 31) || offset != inw(port_picb));
+
+ data = ((civ + 1) * dmabuf->fragsize - (2 * offset)) % dmabuf->dmasize;
+ if (inw(port_picb) == 0)
+ data -= 2048;
+
+ return data;
+}
+
+/* Stop recording (lock held) */
+static inline void __stop_adc(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ struct ali_card *card = state->card;
+
+ dmabuf->enable &= ~ADC_RUNNING;
+
+ outl((1 << 18) | (1 << 16), card->iobase + ALI_DMACR);
+ udelay(1);
+
+ outb(0, card->iobase + PI_CR);
+ while (inb(card->iobase + PI_CR) != 0);
+
+ // now clear any latent interrupt bits (like the halt bit)
+ outb(inb(card->iobase + PI_SR) | 0x001e, card->iobase + PI_SR);
+ outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMIN, card->iobase + ALI_INTERRUPTSR);
+}
+
+static void stop_adc(struct ali_state *state)
+{
+ struct ali_card *card = state->card;
+ unsigned long flags;
+ spin_lock_irqsave(&card->lock, flags);
+ __stop_adc(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static inline void __start_adc(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+
+ if (dmabuf->count < dmabuf->dmasize && dmabuf->ready
+ && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
+ dmabuf->enable |= ADC_RUNNING;
+ outb((1 << 4) | (1 << 2), state->card->iobase + PI_CR);
+ if (state->card->channel[0].used == 1)
+ outl(1, state->card->iobase + ALI_DMACR); // DMA CONTROL REGISTRER
+ udelay(100);
+ if (state->card->channel[2].used == 1)
+ outl((1 << 2), state->card->iobase + ALI_DMACR); //DMA CONTROL REGISTER
+ udelay(100);
+ }
+}
+
+static void start_adc(struct ali_state *state)
+{
+ struct ali_card *card = state->card;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ __start_adc(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* stop playback (lock held) */
+static inline void __stop_dac(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ struct ali_card *card = state->card;
+
+ dmabuf->enable &= ~DAC_RUNNING;
+ outl(0x00020000, card->iobase + 0x08);
+ outb(0, card->iobase + PO_CR);
+ while (inb(card->iobase + PO_CR) != 0)
+ cpu_relax();
+
+ outb(inb(card->iobase + PO_SR) | 0x001e, card->iobase + PO_SR);
+
+ outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMOUT, card->iobase + ALI_INTERRUPTSR);
+}
+
+static void stop_dac(struct ali_state *state)
+{
+ struct ali_card *card = state->card;
+ unsigned long flags;
+ spin_lock_irqsave(&card->lock, flags);
+ __stop_dac(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static inline void __start_dac(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable &&
+ (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
+ dmabuf->enable |= DAC_RUNNING;
+ outb((1 << 4) | (1 << 2), state->card->iobase + PO_CR);
+ outl((1 << 1), state->card->iobase + 0x08); //dma control register
+ }
+}
+
+static void start_dac(struct ali_state *state)
+{
+ struct ali_card *card = state->card;
+ unsigned long flags;
+ spin_lock_irqsave(&card->lock, flags);
+ __start_dac(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* stop codec and controller spdif out (lock held) */
+static inline void __stop_spdifout(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ struct ali_card *card = state->card;
+
+ if (codec_independent_spdif_locked > 0) {
+ dmabuf->enable &= ~CODEC_SPDIFOUT_RUNNING;
+ outl((1 << 19), card->iobase + 0x08);
+ outb(0, card->iobase + CODECSPDIFOUT_CR);
+
+ while (inb(card->iobase + CODECSPDIFOUT_CR) != 0)
+ cpu_relax();
+
+ outb(inb(card->iobase + CODECSPDIFOUT_SR) | 0x001e, card->iobase + CODECSPDIFOUT_SR);
+ outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_CODECSPDIFOUT, card->iobase + ALI_INTERRUPTSR);
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ dmabuf->enable &= ~CONTROLLER_SPDIFOUT_RUNNING;
+ outl((1 << 23), card->iobase + 0x08);
+ outb(0, card->iobase + CONTROLLERSPDIFOUT_CR);
+ while (inb(card->iobase + CONTROLLERSPDIFOUT_CR) != 0)
+ cpu_relax();
+ outb(inb(card->iobase + CONTROLLERSPDIFOUT_SR) | 0x001e, card->iobase + CONTROLLERSPDIFOUT_SR);
+ outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_SPDIFOUT, card->iobase + ALI_INTERRUPTSR);
+ }
+ }
+}
+
+static void stop_spdifout(struct ali_state *state)
+{
+ struct ali_card *card = state->card;
+ unsigned long flags;
+ spin_lock_irqsave(&card->lock, flags);
+ __stop_spdifout(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static inline void __start_spdifout(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable &&
+ (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
+ if (codec_independent_spdif_locked > 0) {
+ dmabuf->enable |= CODEC_SPDIFOUT_RUNNING;
+ outb((1 << 4) | (1 << 2), state->card->iobase + CODECSPDIFOUT_CR);
+ outl((1 << 3), state->card->iobase + 0x08); //dma control register
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ dmabuf->enable |= CONTROLLER_SPDIFOUT_RUNNING;
+ outb((1 << 4) | (1 << 2), state->card->iobase + CONTROLLERSPDIFOUT_CR);
+ outl((1 << 7), state->card->iobase + 0x08); //dma control register
+ }
+ }
+ }
+}
+
+static void start_spdifout(struct ali_state *state)
+{
+ struct ali_card *card = state->card;
+ unsigned long flags;
+ spin_lock_irqsave(&card->lock, flags);
+ __start_spdifout(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+/* allocate DMA buffer, playback , recording,spdif out buffer should be allocated separately */
+static int alloc_dmabuf(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ void *rawbuf = NULL;
+ int order, size;
+ struct page *page, *pend;
+
+ /* If we don't have any oss frag params, then use our default ones */
+ if (dmabuf->ossmaxfrags == 0)
+ dmabuf->ossmaxfrags = 4;
+ if (dmabuf->ossfragsize == 0)
+ dmabuf->ossfragsize = (PAGE_SIZE << DMABUF_DEFAULTORDER) / dmabuf->ossmaxfrags;
+ size = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
+
+ if (dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size)
+ return 0;
+ /* alloc enough to satisfy the oss params */
+ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {
+ if ((PAGE_SIZE << order) > size)
+ continue;
+ if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,
+ PAGE_SIZE << order,
+ &dmabuf->dma_handle)))
+ break;
+ }
+ if (!rawbuf)
+ return -ENOMEM;
+
+ dmabuf->ready = dmabuf->mapped = 0;
+ dmabuf->rawbuf = rawbuf;
+ dmabuf->buforder = order;
+
+ /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */
+ pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
+ for (page = virt_to_page(rawbuf); page <= pend; page++)
+ SetPageReserved(page);
+ return 0;
+}
+
+/* free DMA buffer */
+static void dealloc_dmabuf(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ struct page *page, *pend;
+
+ if (dmabuf->rawbuf) {
+ /* undo marking the pages as reserved */
+ pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
+ for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
+ ClearPageReserved(page);
+ pci_free_consistent(state->card->pci_dev,
+ PAGE_SIZE << dmabuf->buforder,
+ dmabuf->rawbuf, dmabuf->dma_handle);
+ }
+ dmabuf->rawbuf = NULL;
+ dmabuf->mapped = dmabuf->ready = 0;
+}
+
+static int prog_dmabuf(struct ali_state *state, unsigned rec)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ struct ali_channel *c = NULL;
+ struct sg_item *sg;
+ unsigned long flags;
+ int ret;
+ unsigned fragint;
+ int i;
+
+ spin_lock_irqsave(&state->card->lock, flags);
+ if (dmabuf->enable & DAC_RUNNING)
+ __stop_dac(state);
+ if (dmabuf->enable & ADC_RUNNING)
+ __stop_adc(state);
+ if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)
+ __stop_spdifout(state);
+ if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ __stop_spdifout(state);
+
+ dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->error = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+
+ /* allocate DMA buffer, let alloc_dmabuf determine if we are already
+ * allocated well enough or if we should replace the current buffer
+ * (assuming one is already allocated, if it isn't, then allocate it).
+ */
+ if ((ret = alloc_dmabuf(state)))
+ return ret;
+
+ /* FIXME: figure out all this OSS fragment stuff */
+ /* I did, it now does what it should according to the OSS API. DL */
+ /* We may not have realloced our dmabuf, but the fragment size to
+ * fragment number ratio may have changed, so go ahead and reprogram
+ * things
+ */
+
+ dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder;
+ dmabuf->numfrag = SG_LEN;
+ dmabuf->fragsize = dmabuf->dmasize / dmabuf->numfrag;
+ dmabuf->fragsamples = dmabuf->fragsize >> 1;
+ dmabuf->userfragsize = dmabuf->ossfragsize;
+ dmabuf->userfrags = dmabuf->dmasize / dmabuf->ossfragsize;
+
+ memset(dmabuf->rawbuf, 0, dmabuf->dmasize);
+
+ if (dmabuf->ossmaxfrags == 4) {
+ fragint = 8;
+ dmabuf->fragshift = 2;
+ } else if (dmabuf->ossmaxfrags == 8) {
+ fragint = 4;
+ dmabuf->fragshift = 3;
+ } else if (dmabuf->ossmaxfrags == 16) {
+ fragint = 2;
+ dmabuf->fragshift = 4;
+ } else {
+ fragint = 1;
+ dmabuf->fragshift = 5;
+ }
+ /*
+ * Now set up the ring
+ */
+
+ if (rec == 1)
+ c = dmabuf->read_channel;
+ else if (rec == 2)
+ c = dmabuf->codec_spdifout_channel;
+ else if (rec == 3)
+ c = dmabuf->controller_spdifout_channel;
+ else if (rec == 0)
+ c = dmabuf->write_channel;
+ if (c != NULL) {
+ sg = &c->sg[0];
+ /*
+ * Load up 32 sg entries and take an interrupt at half
+ * way (we might want more interrupts later..)
+ */
+ for (i = 0; i < dmabuf->numfrag; i++) {
+ sg->busaddr =
+ virt_to_bus(dmabuf->rawbuf +
+ dmabuf->fragsize * i);
+ // the card will always be doing 16bit stereo
+ sg->control = dmabuf->fragsamples;
+ sg->control |= CON_BUFPAD; //I modify
+ // set us up to get IOC interrupts as often as needed to
+ // satisfy numfrag requirements, no more
+ if (((i + 1) % fragint) == 0) {
+ sg->control |= CON_IOC;
+ }
+ sg++;
+ }
+ spin_lock_irqsave(&state->card->lock, flags);
+ outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */
+ outl(virt_to_bus(&c->sg[0]), state->card->iobase + c->port + OFF_BDBAR);
+ outb(0, state->card->iobase + c->port + OFF_CIV);
+ outb(0, state->card->iobase + c->port + OFF_LVI);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
+ /* set the ready flag for the dma buffer */
+ dmabuf->ready = 1;
+ return 0;
+}
+
+static void __ali_update_lvi(struct ali_state *state, int rec)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ int x, port;
+ port = state->card->iobase;
+ if (rec == 1)
+ port += dmabuf->read_channel->port;
+ else if (rec == 2)
+ port += dmabuf->codec_spdifout_channel->port;
+ else if (rec == 3)
+ port += dmabuf->controller_spdifout_channel->port;
+ else if (rec == 0)
+ port += dmabuf->write_channel->port;
+ /* if we are currently stopped, then our CIV is actually set to our
+ * *last* sg segment and we are ready to wrap to the next. However,
+ * if we set our LVI to the last sg segment, then it won't wrap to
+ * the next sg segment, it won't even get a start. So, instead, when
+ * we are stopped, we set both the LVI value and also we increment
+ * the CIV value to the next sg segment to be played so that when
+ * we call start_{dac,adc}, things will operate properly
+ */
+ if (!dmabuf->enable && dmabuf->ready) {
+ if (rec && dmabuf->count < dmabuf->dmasize && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
+ outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI);
+ __start_adc(state);
+ while (! (inb(port + OFF_CR) & ((1 << 4) | (1 << 2))))
+ cpu_relax();
+ } else if (!rec && dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
+ outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI);
+ __start_dac(state);
+ while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2))))
+ cpu_relax();
+ } else if (rec && dmabuf->count && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
+ if (codec_independent_spdif_locked > 0) {
+ // outb((inb(port+OFF_CIV))&31, port+OFF_LVI);
+ outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI);
+ __start_spdifout(state);
+ while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2))))
+ cpu_relax();
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI);
+ __start_spdifout(state);
+ while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2))))
+ cpu_relax();
+ }
+ }
+ }
+ }
+
+ /* swptr - 1 is the tail of our transfer */
+ x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize;
+ x /= dmabuf->fragsize;
+ outb(x, port + OFF_LVI);
+}
+
+static void ali_update_lvi(struct ali_state *state, int rec)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned long flags;
+ if (!dmabuf->ready)
+ return;
+ spin_lock_irqsave(&state->card->lock, flags);
+ __ali_update_lvi(state, rec);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+}
+
+/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
+static void ali_update_ptr(struct ali_state *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned hwptr;
+ int diff;
+
+ /* error handling and process wake up for DAC */
+ if (dmabuf->enable == ADC_RUNNING) {
+ /* update hardware pointer */
+ hwptr = ali_get_dma_addr(state, 1);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
+ dmabuf->count += diff;
+ if (dmabuf->count > dmabuf->dmasize) {
+ /* buffer underrun or buffer overrun */
+ /* this is normal for the end of a read */
+ /* only give an error if we went past the */
+ /* last valid sg entry */
+ if ((inb(state->card->iobase + PI_CIV) & 31) != (inb(state->card->iobase + PI_LVI) & 31)) {
+ printk(KERN_WARNING "ali_audio: DMA overrun on read\n");
+ dmabuf->error++;
+ }
+ }
+ if (dmabuf->count > dmabuf->userfragsize)
+ wake_up(&dmabuf->wait);
+ }
+ /* error handling and process wake up for DAC */
+ if (dmabuf->enable == DAC_RUNNING) {
+ /* update hardware pointer */
+ hwptr = ali_get_dma_addr(state, 0);
+ diff =
+ (dmabuf->dmasize + hwptr -
+ dmabuf->hwptr) % dmabuf->dmasize;
+#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)
+ printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#endif
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
+ dmabuf->count -= diff;
+ if (dmabuf->count < 0) {
+ /* buffer underrun or buffer overrun */
+ /* this is normal for the end of a write */
+ /* only give an error if we went past the */
+ /* last valid sg entry */
+ if ((inb(state->card->iobase + PO_CIV) & 31) != (inb(state->card->iobase + PO_LVI) & 31)) {
+ printk(KERN_WARNING "ali_audio: DMA overrun on write\n");
+ printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n",
+ inb(state->card->iobase + PO_CIV) & 31,
+ inb(state->card->iobase + PO_LVI) & 31,
+ dmabuf->hwptr,
+ dmabuf->count);
+ dmabuf->error++;
+ }
+ }
+ if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize))
+ wake_up(&dmabuf->wait);
+ }
+
+ /* error handling and process wake up for CODEC SPDIF OUT */
+ if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) {
+ /* update hardware pointer */
+ hwptr = ali_get_dma_addr(state, 2);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
+ dmabuf->count -= diff;
+ if (dmabuf->count < 0) {
+ /* buffer underrun or buffer overrun */
+ /* this is normal for the end of a write */
+ /* only give an error if we went past the */
+ /* last valid sg entry */
+ if ((inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31)) {
+ printk(KERN_WARNING "ali_audio: DMA overrun on write\n");
+ printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n",
+ inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31,
+ inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31,
+ dmabuf->hwptr, dmabuf->count);
+ dmabuf->error++;
+ }
+ }
+ if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize))
+ wake_up(&dmabuf->wait);
+ }
+ /* error handling and process wake up for CONTROLLER SPDIF OUT */
+ if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) {
+ /* update hardware pointer */
+ hwptr = ali_get_dma_addr(state, 3);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
+ dmabuf->count -= diff;
+ if (dmabuf->count < 0) {
+ /* buffer underrun or buffer overrun */
+ /* this is normal for the end of a write */
+ /* only give an error if we went past the */
+ /* last valid sg entry */
+ if ((inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31)) {
+ printk(KERN_WARNING
+ "ali_audio: DMA overrun on write\n");
+ printk("ali_audio: CIV %d, LVI %d, hwptr %x, "
+ "count %d\n",
+ inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31,
+ inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31,
+ dmabuf->hwptr, dmabuf->count);
+ dmabuf->error++;
+ }
+ }
+ if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize))
+ wake_up(&dmabuf->wait);
+ }
+}
+
+static inline int ali_get_free_write_space(struct
+ ali_state
+ *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ int free;
+
+ if (dmabuf->count < 0) {
+ dmabuf->count = 0;
+ dmabuf->swptr = dmabuf->hwptr;
+ }
+ free = dmabuf->dmasize - dmabuf->swptr;
+ if ((dmabuf->count + free) > dmabuf->dmasize){
+ free = dmabuf->dmasize - dmabuf->count;
+ }
+ return free;
+}
+
+static inline int ali_get_available_read_data(struct
+ ali_state
+ *state)
+{
+ struct dmabuf *dmabuf = &state->dmabuf;
+ int avail;
+ ali_update_ptr(state);
+ // catch overruns during record
+ if (dmabuf->count > dmabuf->dmasize) {
+ dmabuf->count = dmabuf->dmasize;
+ dmabuf->swptr = dmabuf->hwptr;
+ }
+ avail = dmabuf->count;
+ avail -= (dmabuf->hwptr % dmabuf->fragsize);
+ if (avail < 0)
+ return (0);
+ return (avail);
+}
+
+static int drain_dac(struct ali_state *state, int signals_allowed)
+{
+
+ DECLARE_WAITQUEUE(wait, current);
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned long flags;
+ unsigned long tmo;
+ int count;
+ if (!dmabuf->ready)
+ return 0;
+ if (dmabuf->mapped) {
+ stop_dac(state);
+ return 0;
+ }
+ add_wait_queue(&dmabuf->wait, &wait);
+ for (;;) {
+
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ count = dmabuf->count;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ if (count <= 0)
+ break;
+ /*
+ * This will make sure that our LVI is correct, that our
+ * pointer is updated, and that the DAC is running. We
+ * have to force the setting of dmabuf->trigger to avoid
+ * any possible deadlocks.
+ */
+ if (!dmabuf->enable) {
+ dmabuf->trigger = PCM_ENABLE_OUTPUT;
+ ali_update_lvi(state, 0);
+ }
+ if (signal_pending(current) && signals_allowed) {
+ break;
+ }
+
+ /* It seems that we have to set the current state to
+ * TASK_INTERRUPTIBLE every time to make the process
+ * really go to sleep. This also has to be *after* the
+ * update_ptr() call because update_ptr is likely to
+ * do a wake_up() which will unset this before we ever
+ * try to sleep, resuling in a tight loop in this code
+ * instead of actually sleeping and waiting for an
+ * interrupt to wake us up!
+ */
+ set_current_state(TASK_INTERRUPTIBLE);
+ /*
+ * set the timeout to significantly longer than it *should*
+ * take for the DAC to drain the DMA buffer
+ */
+ tmo = (count * HZ) / (dmabuf->rate);
+ if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {
+ printk(KERN_ERR "ali_audio: drain_dac, dma timeout?\n");
+ count = 0;
+ break;
+ }
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dmabuf->wait, &wait);
+ if (count > 0 && signal_pending(current) && signals_allowed)
+ return -ERESTARTSYS;
+ stop_dac(state);
+ return 0;
+}
+
+
+static int drain_spdifout(struct ali_state *state, int signals_allowed)
+{
+
+ DECLARE_WAITQUEUE(wait, current);
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned long flags;
+ unsigned long tmo;
+ int count;
+ if (!dmabuf->ready)
+ return 0;
+ if (dmabuf->mapped) {
+ stop_spdifout(state);
+ return 0;
+ }
+ add_wait_queue(&dmabuf->wait, &wait);
+ for (;;) {
+
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ count = dmabuf->count;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ if (count <= 0)
+ break;
+ /*
+ * This will make sure that our LVI is correct, that our
+ * pointer is updated, and that the DAC is running. We
+ * have to force the setting of dmabuf->trigger to avoid
+ * any possible deadlocks.
+ */
+ if (!dmabuf->enable) {
+ if (codec_independent_spdif_locked > 0) {
+ dmabuf->trigger = SPDIF_ENABLE_OUTPUT;
+ ali_update_lvi(state, 2);
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ dmabuf->trigger = SPDIF_ENABLE_OUTPUT;
+ ali_update_lvi(state, 3);
+ }
+ }
+ }
+ if (signal_pending(current) && signals_allowed) {
+ break;
+ }
+
+ /* It seems that we have to set the current state to
+ * TASK_INTERRUPTIBLE every time to make the process
+ * really go to sleep. This also has to be *after* the
+ * update_ptr() call because update_ptr is likely to
+ * do a wake_up() which will unset this before we ever
+ * try to sleep, resuling in a tight loop in this code
+ * instead of actually sleeping and waiting for an
+ * interrupt to wake us up!
+ */
+ set_current_state(TASK_INTERRUPTIBLE);
+ /*
+ * set the timeout to significantly longer than it *should*
+ * take for the DAC to drain the DMA buffer
+ */
+ tmo = (count * HZ) / (dmabuf->rate);
+ if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {
+ printk(KERN_ERR "ali_audio: drain_spdifout, dma timeout?\n");
+ count = 0;
+ break;
+ }
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dmabuf->wait, &wait);
+ if (count > 0 && signal_pending(current) && signals_allowed)
+ return -ERESTARTSYS;
+ stop_spdifout(state);
+ return 0;
+}
+
+static void ali_channel_interrupt(struct ali_card *card)
+{
+ int i, count;
+
+ for (i = 0; i < NR_HW_CH; i++) {
+ struct ali_state *state = card->states[i];
+ struct ali_channel *c = NULL;
+ struct dmabuf *dmabuf;
+ unsigned long port = card->iobase;
+ u16 status;
+ if (!state)
+ continue;
+ if (!state->dmabuf.ready)
+ continue;
+ dmabuf = &state->dmabuf;
+ if (codec_independent_spdif_locked > 0) {
+ if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {
+ c = dmabuf->codec_spdifout_channel;
+ }
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ c = dmabuf->controller_spdifout_channel;
+ } else {
+ if (dmabuf->enable & DAC_RUNNING) {
+ c = dmabuf->write_channel;
+ } else if (dmabuf->enable & ADC_RUNNING) {
+ c = dmabuf->read_channel;
+ } else
+ continue;
+ }
+ }
+ port += c->port;
+
+ status = inw(port + OFF_SR);
+
+ if (status & DMA_INT_COMPLETE) {
+ /* only wake_up() waiters if this interrupt signals
+ * us being beyond a userfragsize of data open or
+ * available, and ali_update_ptr() does that for
+ * us
+ */
+ ali_update_ptr(state);
+ }
+
+ if (status & DMA_INT_LVI) {
+ ali_update_ptr(state);
+ wake_up(&dmabuf->wait);
+
+ if (dmabuf->enable & DAC_RUNNING)
+ count = dmabuf->count;
+ else if (dmabuf->enable & ADC_RUNNING)
+ count = dmabuf->dmasize - dmabuf->count;
+ else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)
+ count = dmabuf->count;
+ else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ count = dmabuf->count;
+ else count = 0;
+
+ if (count > 0) {
+ if (dmabuf->enable & DAC_RUNNING)
+ outl((1 << 1), state->card->iobase + ALI_DMACR);
+ else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)
+ outl((1 << 3), state->card->iobase + ALI_DMACR);
+ else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ outl((1 << 7), state->card->iobase + ALI_DMACR);
+ } else {
+ if (dmabuf->enable & DAC_RUNNING)
+ __stop_dac(state);
+ if (dmabuf->enable & ADC_RUNNING)
+ __stop_adc(state);
+ if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)
+ __stop_spdifout(state);
+ if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ __stop_spdifout(state);
+ dmabuf->enable = 0;
+ wake_up(&dmabuf->wait);
+ }
+
+ }
+ if (!(status & DMA_INT_DCH)) {
+ ali_update_ptr(state);
+ wake_up(&dmabuf->wait);
+ if (dmabuf->enable & DAC_RUNNING)
+ count = dmabuf->count;
+ else if (dmabuf->enable & ADC_RUNNING)
+ count = dmabuf->dmasize - dmabuf->count;
+ else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)
+ count = dmabuf->count;
+ else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ count = dmabuf->count;
+ else
+ count = 0;
+
+ if (count > 0) {
+ if (dmabuf->enable & DAC_RUNNING)
+ outl((1 << 1), state->card->iobase + ALI_DMACR);
+ else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)
+ outl((1 << 3), state->card->iobase + ALI_DMACR);
+ else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ outl((1 << 7), state->card->iobase + ALI_DMACR);
+ } else {
+ if (dmabuf->enable & DAC_RUNNING)
+ __stop_dac(state);
+ if (dmabuf->enable & ADC_RUNNING)
+ __stop_adc(state);
+ if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)
+ __stop_spdifout(state);
+ if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)
+ __stop_spdifout(state);
+ dmabuf->enable = 0;
+ wake_up(&dmabuf->wait);
+ }
+ }
+ outw(status & DMA_INT_MASK, port + OFF_SR);
+ }
+}
+
+static irqreturn_t ali_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct ali_card *card = (struct ali_card *) dev_id;
+ u32 status;
+ u16 status2;
+
+ spin_lock(&card->lock);
+ status = inl(card->iobase + ALI_INTERRUPTSR);
+ if (!(status & INT_MASK)) {
+ spin_unlock(&card->lock);
+ return IRQ_NONE; /* not for us */
+ }
+
+ if (codec_independent_spdif_locked > 0) {
+ if (globel == 0) {
+ globel += 1;
+ status2 = inw(card->iobase + 0x76);
+ outw(status2 | 0x000c, card->iobase + 0x76);
+ } else {
+ if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT))
+ ali_channel_interrupt(card);
+ }
+ } else {
+ if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT))
+ ali_channel_interrupt(card);
+ }
+
+ /* clear 'em */
+ outl(status & INT_MASK, card->iobase + ALI_INTERRUPTSR);
+ spin_unlock(&card->lock);
+ return IRQ_HANDLED;
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is
+ waiting to be copied to the user's buffer. It is filled by the dma
+ machine and drained by this loop. */
+
+static ssize_t ali_read(struct file *file, char __user *buffer,
+ size_t count, loff_t * ppos)
+{
+ struct ali_state *state = (struct ali_state *) file->private_data;
+ struct ali_card *card = state ? state->card : NULL;
+ struct dmabuf *dmabuf = &state->dmabuf;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned int swptr;
+ int cnt;
+ DECLARE_WAITQUEUE(waita, current);
+#ifdef DEBUG2
+ printk("ali_audio: ali_read called, count = %d\n", count);
+#endif
+ if (dmabuf->mapped)
+ return -ENXIO;
+ if (dmabuf->enable & DAC_RUNNING)
+ return -ENODEV;
+ if (!dmabuf->read_channel) {
+ dmabuf->ready = 0;
+ dmabuf->read_channel = card->alloc_rec_pcm_channel(card);
+ if (!dmabuf->read_channel) {
+ return -EBUSY;
+ }
+ }
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+ return ret;
+ if (!access_ok(VERIFY_WRITE, buffer, count))
+ return -EFAULT;
+ ret = 0;
+ add_wait_queue(&dmabuf->wait, &waita);
+ while (count > 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&card->lock, flags);
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ continue;
+ }
+ swptr = dmabuf->swptr;
+ cnt = ali_get_available_read_data(state);
+ // this is to make the copy_to_user simpler below
+ if (cnt > (dmabuf->dmasize - swptr))
+ cnt = dmabuf->dmasize - swptr;
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (cnt > count)
+ cnt = count;
+ /* Lop off the last two bits to force the code to always
+ * write in full samples. This keeps software that sets
+ * O_NONBLOCK but doesn't check the return value of the
+ * write call from getting things out of state where they
+ * think a full 4 byte sample was written when really only
+ * a portion was, resulting in odd sound and stereo
+ * hysteresis.
+ */
+ cnt &= ~0x3;
+ if (cnt <= 0) {
+ unsigned long tmo;
+ /*
+ * Don't let us deadlock. The ADC won't start if
+ * dmabuf->trigger isn't set. A call to SETTRIGGER
+ * could have turned it off after we set it to on
+ * previously.
+ */
+ dmabuf->trigger = PCM_ENABLE_INPUT;
+ /*
+ * This does three things. Updates LVI to be correct,
+ * makes sure the ADC is running, and updates the
+ * hwptr.
+ */
+ ali_update_lvi(state, 1);
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ goto done;
+ }
+ /* Set the timeout to how long it would take to fill
+ * two of our buffers. If we haven't been woke up
+ * by then, then we know something is wrong.
+ */
+ tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
+
+ /* There are two situations when sleep_on_timeout returns, one is when
+ the interrupt is serviced correctly and the process is waked up by
+ ISR ON TIME. Another is when timeout is expired, which means that
+ either interrupt is NOT serviced correctly (pending interrupt) or it
+ is TOO LATE for the process to be scheduled to run (scheduler latency)
+ which results in a (potential) buffer overrun. And worse, there is
+ NOTHING we can do to prevent it. */
+ if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {
+ printk(KERN_ERR
+ "ali_audio: recording schedule timeout, "
+ "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+ dmabuf->dmasize, dmabuf->fragsize,
+ dmabuf->count, dmabuf->hwptr,
+ dmabuf->swptr);
+ /* a buffer overrun, we delay the recovery until next time the
+ while loop begin and we REALLY have space to record */
+ }
+ if (signal_pending(current)) {
+ ret = ret ? ret : -ERESTARTSYS;
+ goto done;
+ }
+ continue;
+ }
+
+ if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ goto done;
+ }
+
+ swptr = (swptr + cnt) % dmabuf->dmasize;
+ spin_lock_irqsave(&card->lock, flags);
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ continue;
+ }
+ dmabuf->swptr = swptr;
+ dmabuf->count -= cnt;
+ spin_unlock_irqrestore(&card->lock, flags);
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ }
+done:
+ ali_update_lvi(state, 1);
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dmabuf->wait, &waita);
+ return ret;
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
+ the soundcard. it is drained by the dma machine and filled by this loop. */
+static ssize_t ali_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t * ppos)
+{
+ struct ali_state *state = (struct ali_state *) file->private_data;
+ struct ali_card *card = state ? state->card : NULL;
+ struct dmabuf *dmabuf = &state->dmabuf;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned int swptr = 0;
+ int cnt, x;
+ DECLARE_WAITQUEUE(waita, current);
+#ifdef DEBUG2
+ printk("ali_audio: ali_write called, count = %d\n", count);
+#endif
+ if (dmabuf->mapped)
+ return -ENXIO;
+ if (dmabuf->enable & ADC_RUNNING)
+ return -ENODEV;
+ if (codec_independent_spdif_locked > 0) {
+ if (!dmabuf->codec_spdifout_channel) {
+ dmabuf->ready = 0;
+ dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card);
+ if (!dmabuf->codec_spdifout_channel)
+ return -EBUSY;
+ }
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (!dmabuf->controller_spdifout_channel) {
+ dmabuf->ready = 0;
+ dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card);
+ if (!dmabuf->controller_spdifout_channel)
+ return -EBUSY;
+ }
+ } else {
+ if (!dmabuf->write_channel) {
+ dmabuf->ready = 0;
+ dmabuf->write_channel =
+ card->alloc_pcm_channel(card);
+ if (!dmabuf->write_channel)
+ return -EBUSY;
+ }
+ }
+ }
+
+ if (codec_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 2)))
+ return ret;
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 3)))
+ return ret;
+ } else {
+
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+ return ret;
+ }
+ }
+ if (!access_ok(VERIFY_READ, buffer, count))
+ return -EFAULT;
+ ret = 0;
+ add_wait_queue(&dmabuf->wait, &waita);
+ while (count > 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&state->card->lock, flags);
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ continue;
+ }
+
+ swptr = dmabuf->swptr;
+ cnt = ali_get_free_write_space(state);
+ /* Bound the maximum size to how much we can copy to the
+ * dma buffer before we hit the end. If we have more to
+ * copy then it will get done in a second pass of this
+ * loop starting from the beginning of the buffer.
+ */
+ if (cnt > (dmabuf->dmasize - swptr))
+ cnt = dmabuf->dmasize - swptr;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG2
+ printk(KERN_INFO
+ "ali_audio: ali_write: %d bytes available space\n",
+ cnt);
+#endif
+ if (cnt > count)
+ cnt = count;
+ /* Lop off the last two bits to force the code to always
+ * write in full samples. This keeps software that sets
+ * O_NONBLOCK but doesn't check the return value of the
+ * write call from getting things out of state where they
+ * think a full 4 byte sample was written when really only
+ * a portion was, resulting in odd sound and stereo
+ * hysteresis.
+ */
+ cnt &= ~0x3;
+ if (cnt <= 0) {
+ unsigned long tmo;
+ // There is data waiting to be played
+ /*
+ * Force the trigger setting since we would
+ * deadlock with it set any other way
+ */
+ if (codec_independent_spdif_locked > 0) {
+ dmabuf->trigger = SPDIF_ENABLE_OUTPUT;
+ ali_update_lvi(state, 2);
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ dmabuf->trigger = SPDIF_ENABLE_OUTPUT;
+ ali_update_lvi(state, 3);
+ } else {
+
+ dmabuf->trigger = PCM_ENABLE_OUTPUT;
+ ali_update_lvi(state, 0);
+ }
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ goto ret;
+ }
+ /* Not strictly correct but works */
+ tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
+ /* There are two situations when sleep_on_timeout returns, one is when
+ the interrupt is serviced correctly and the process is waked up by
+ ISR ON TIME. Another is when timeout is expired, which means that
+ either interrupt is NOT serviced correctly (pending interrupt) or it
+ is TOO LATE for the process to be scheduled to run (scheduler latency)
+ which results in a (potential) buffer underrun. And worse, there is
+ NOTHING we can do to prevent it. */
+
+ /* FIXME - do timeout handling here !! */
+ schedule_timeout(tmo >= 2 ? tmo : 2);
+
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ goto ret;
+ }
+ continue;
+ }
+ if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ goto ret;
+ }
+
+ swptr = (swptr + cnt) % dmabuf->dmasize;
+ spin_lock_irqsave(&state->card->lock, flags);
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ continue;
+ }
+
+ dmabuf->swptr = swptr;
+ dmabuf->count += cnt;
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
+ if (swptr % dmabuf->fragsize) {
+ x = dmabuf->fragsize - (swptr % dmabuf->fragsize);
+ memset(dmabuf->rawbuf + swptr, '\0', x);
+ }
+ret:
+ if (codec_independent_spdif_locked > 0) {
+ ali_update_lvi(state, 2);
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ ali_update_lvi(state, 3);
+ } else {
+ ali_update_lvi(state, 0);
+ }
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dmabuf->wait, &waita);
+ return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int ali_poll(struct file *file, struct poll_table_struct
+ *wait)
+{
+ struct ali_state *state = (struct ali_state *) file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned long flags;
+ unsigned int mask = 0;
+ if (!dmabuf->ready)
+ return 0;
+ poll_wait(file, &dmabuf->wait, wait);
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) {
+ if (dmabuf->count >= (signed) dmabuf->fragsize)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if (file->f_mode & FMODE_WRITE && (dmabuf->enable & (DAC_RUNNING|CODEC_SPDIFOUT_RUNNING|CONTROLLER_SPDIFOUT_RUNNING))) {
+ if ((signed) dmabuf->dmasize >= dmabuf->count + (signed) dmabuf->fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ return mask;
+}
+
+static int ali_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct ali_state *state = (struct ali_state *) file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
+ int ret = -EINVAL;
+ unsigned long size;
+ lock_kernel();
+ if (vma->vm_flags & VM_WRITE) {
+ if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+ if (vma->vm_flags & VM_READ) {
+ if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+ if ((ret = prog_dmabuf(state, 0)) != 0)
+ goto out;
+ ret = -EINVAL;
+ if (vma->vm_pgoff != 0)
+ goto out;
+ size = vma->vm_end - vma->vm_start;
+ if (size > (PAGE_SIZE << dmabuf->buforder))
+ goto out;
+ ret = -EAGAIN;
+ if (remap_pfn_range(vma, vma->vm_start,
+ virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT,
+ size, vma->vm_page_prot))
+ goto out;
+ dmabuf->mapped = 1;
+ dmabuf->trigger = 0;
+ ret = 0;
+out:
+ unlock_kernel();
+ return ret;
+}
+
+static int ali_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct ali_state *state = (struct ali_state *) file->private_data;
+ struct ali_channel *c = NULL;
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned long flags;
+ audio_buf_info abinfo;
+ count_info cinfo;
+ unsigned int i_scr;
+ int val = 0, ret;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+
+#ifdef DEBUG
+ printk("ali_audio: ali_ioctl, arg=0x%x, cmd=",
+ arg ? *p : 0);
+#endif
+ switch (cmd) {
+ case OSS_GETVERSION:
+#ifdef DEBUG
+ printk("OSS_GETVERSION\n");
+#endif
+ return put_user(SOUND_VERSION, p);
+ case SNDCTL_DSP_RESET:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_RESET\n");
+#endif
+ spin_lock_irqsave(&state->card->lock, flags);
+ if (dmabuf->enable == DAC_RUNNING) {
+ c = dmabuf->write_channel;
+ __stop_dac(state);
+ }
+ if (dmabuf->enable == ADC_RUNNING) {
+ c = dmabuf->read_channel;
+ __stop_adc(state);
+ }
+ if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) {
+ c = dmabuf->codec_spdifout_channel;
+ __stop_spdifout(state);
+ }
+ if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) {
+ c = dmabuf->controller_spdifout_channel;
+ __stop_spdifout(state);
+ }
+ if (c != NULL) {
+ outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */
+ outl(virt_to_bus(&c->sg[0]),
+ state->card->iobase + c->port + OFF_BDBAR);
+ outb(0, state->card->iobase + c->port + OFF_CIV);
+ outb(0, state->card->iobase + c->port + OFF_LVI);
+ }
+
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ synchronize_irq(state->card->pci_dev->irq);
+ dmabuf->ready = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->count = dmabuf->total_bytes = 0;
+ return 0;
+ case SNDCTL_DSP_SYNC:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SYNC\n");
+#endif
+ if (codec_independent_spdif_locked > 0) {
+ if (dmabuf->enable != CODEC_SPDIFOUT_RUNNING
+ || file->f_flags & O_NONBLOCK)
+ return 0;
+ if ((val = drain_spdifout(state, 1)))
+ return val;
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (dmabuf->enable !=
+ CONTROLLER_SPDIFOUT_RUNNING
+ || file->f_flags & O_NONBLOCK)
+ return 0;
+ if ((val = drain_spdifout(state, 1)))
+ return val;
+ } else {
+ if (dmabuf->enable != DAC_RUNNING
+ || file->f_flags & O_NONBLOCK)
+ return 0;
+ if ((val = drain_dac(state, 1)))
+ return val;
+ }
+ }
+ dmabuf->total_bytes = 0;
+ return 0;
+ case SNDCTL_DSP_SPEED: /* set smaple rate */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SPEED\n");
+#endif
+ if (get_user(val, p))
+ return -EFAULT;
+ if (val >= 0) {
+ if (file->f_mode & FMODE_WRITE) {
+ if ((state->card->ac97_status & SPDIF_ON)) { /* S/PDIF Enabled */
+ /* RELTEK ALC650 only support 48000, need to check that */
+ if (ali_valid_spdif_rate(codec, val)) {
+ if (codec_independent_spdif_locked > 0) {
+ ali_set_spdif_output(state, -1, 0);
+ stop_spdifout(state);
+ dmabuf->ready = 0;
+ /* I add test codec independent spdif out */
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_set_codecspdifout_rate(state, val); // I modified
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ /* Set S/PDIF transmitter rate. */
+ i_scr = inl(state->card->iobase + ALI_SCR);
+ if ((i_scr & 0x00300000) == 0x00100000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
+ } else {
+ if ((i_scr&0x00300000) == 0x00200000)
+ {
+ ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked);
+ } else {
+ if ((i_scr & 0x00300000) == 0x00300000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked);
+ } else {
+ ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
+ }
+ }
+ }
+
+ if (!(state->card->ac97_status & SPDIF_ON)) {
+ val = dmabuf->rate;
+ }
+ } else {
+ if (controller_independent_spdif_locked > 0)
+ {
+ stop_spdifout(state);
+ dmabuf->ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_set_spdifout_rate(state, controller_independent_spdif_locked);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ } else {
+ /* Set DAC rate */
+ ali_set_spdif_output(state, -1, 0);
+ stop_dac(state);
+ dmabuf->ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_set_dac_rate(state, val);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ /* Set S/PDIF transmitter rate. */
+ ali_set_spdif_output(state, AC97_EA_SPSA_3_4, val);
+ if (!(state->card->ac97_status & SPDIF_ON))
+ {
+ val = dmabuf->rate;
+ }
+ }
+ }
+ } else { /* Not a valid rate for S/PDIF, ignore it */
+ val = dmabuf->rate;
+ }
+ } else {
+ stop_dac(state);
+ dmabuf->ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_set_dac_rate(state, val);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
+ }
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(state);
+ dmabuf->ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_set_adc_rate(state, val);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
+ }
+ return put_user(dmabuf->rate, p);
+ case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_STEREO\n");
+#endif
+ if (dmabuf->enable & DAC_RUNNING) {
+ stop_dac(state);
+ }
+ if (dmabuf->enable & ADC_RUNNING) {
+ stop_adc(state);
+ }
+ if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {
+ stop_spdifout(state);
+ }
+ if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) {
+ stop_spdifout(state);
+ }
+ return put_user(1, p);
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (file->f_mode & FMODE_WRITE) {
+ if (codec_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 2)))
+ return val;
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 3)))
+ return val;
+ } else {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
+ return val;
+ }
+ }
+ }
+
+ if (file->f_mode & FMODE_READ) {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
+ return val;
+ }
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize);
+#endif
+ return put_user(dmabuf->userfragsize, p);
+ case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETFMTS\n");
+#endif
+ return put_user(AFMT_S16_LE, p);
+ case SNDCTL_DSP_SETFMT: /* Select sample format */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SETFMT\n");
+#endif
+ return put_user(AFMT_S16_LE, p);
+ case SNDCTL_DSP_CHANNELS: // add support 4,6 channel
+#ifdef DEBUG
+ printk("SNDCTL_DSP_CHANNELS\n");
+#endif
+ if (get_user(val, p))
+ return -EFAULT;
+ if (val > 0) {
+ if (dmabuf->enable & DAC_RUNNING) {
+ stop_dac(state);
+ }
+ if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {
+ stop_spdifout(state);
+ }
+ if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) {
+ stop_spdifout(state);
+ }
+ if (dmabuf->enable & ADC_RUNNING) {
+ stop_adc(state);
+ }
+ } else {
+ return put_user(state->card->channels, p);
+ }
+
+ i_scr = inl(state->card->iobase + ALI_SCR);
+ /* Current # of channels enabled */
+ if (i_scr & 0x00000100)
+ ret = 4;
+ else if (i_scr & 0x00000200)
+ ret = 6;
+ else
+ ret = 2;
+ switch (val) {
+ case 2: /* 2 channels is always supported */
+ if (codec_independent_spdif_locked > 0) {
+ outl(((i_scr & 0xfffffcff) | 0x00100000), (state->card->iobase + ALI_SCR));
+ } else
+ outl((i_scr & 0xfffffcff), (state->card->iobase + ALI_SCR));
+ /* Do we need to change mixer settings???? */
+ break;
+ case 4: /* Supported on some chipsets, better check first */
+ if (codec_independent_spdif_locked > 0) {
+ outl(((i_scr & 0xfffffcff) | 0x00000100 | 0x00200000), (state->card->iobase + ALI_SCR));
+ } else
+ outl(((i_scr & 0xfffffcff) | 0x00000100), (state->card->iobase + ALI_SCR));
+ break;
+ case 6: /* Supported on some chipsets, better check first */
+ if (codec_independent_spdif_locked > 0) {
+ outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000 | 0x00300000), (state->card->iobase + ALI_SCR));
+ } else
+ outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000), (state->card->iobase + ALI_SCR));
+ break;
+ default: /* nothing else is ever supported by the chipset */
+ val = ret;
+ break;
+ }
+ return put_user(val, p);
+ case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */
+ /* we update the swptr to the end of the last sg segment then return */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_POST\n");
+#endif
+ if (codec_independent_spdif_locked > 0) {
+ if (!dmabuf->ready || (dmabuf->enable != CODEC_SPDIFOUT_RUNNING))
+ return 0;
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (!dmabuf->ready || (dmabuf->enable != CONTROLLER_SPDIFOUT_RUNNING))
+ return 0;
+ } else {
+ if (!dmabuf->ready || (dmabuf->enable != DAC_RUNNING))
+ return 0;
+ }
+ }
+ if ((dmabuf->swptr % dmabuf->fragsize) != 0) {
+ val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize);
+ dmabuf->swptr += val;
+ dmabuf->count += val;
+ }
+ return 0;
+ case SNDCTL_DSP_SUBDIVIDE:
+ if (dmabuf->subdivision)
+ return -EINVAL;
+ if (get_user(val, p))
+ return -EFAULT;
+ if (val != 1 && val != 2 && val != 4)
+ return -EINVAL;
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SUBDIVIDE %d\n", val);
+#endif
+ dmabuf->subdivision = val;
+ dmabuf->ready = 0;
+ return 0;
+ case SNDCTL_DSP_SETFRAGMENT:
+ if (get_user(val, p))
+ return -EFAULT;
+ dmabuf->ossfragsize = 1 << (val & 0xffff);
+ dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+ if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags)
+ return -EINVAL;
+ /*
+ * Bound the frag size into our allowed range of 256 - 4096
+ */
+ if (dmabuf->ossfragsize < 256)
+ dmabuf->ossfragsize = 256;
+ else if (dmabuf->ossfragsize > 4096)
+ dmabuf->ossfragsize = 4096;
+ /*
+ * The numfrags could be something reasonable, or it could
+ * be 0xffff meaning "Give me as much as possible". So,
+ * we check the numfrags * fragsize doesn't exceed our
+ * 64k buffer limit, nor is it less than our 8k minimum.
+ * If it fails either one of these checks, then adjust the
+ * number of fragments, not the size of them. It's OK if
+ * our number of fragments doesn't equal 32 or anything
+ * like our hardware based number now since we are using
+ * a different frag count for the hardware. Before we get
+ * into this though, bound the maxfrags to avoid overflow
+ * issues. A reasonable bound would be 64k / 256 since our
+ * maximum buffer size is 64k and our minimum frag size is
+ * 256. On the other end, our minimum buffer size is 8k and
+ * our maximum frag size is 4k, so the lower bound should
+ * be 2.
+ */
+ if (dmabuf->ossmaxfrags > 256)
+ dmabuf->ossmaxfrags = 256;
+ else if (dmabuf->ossmaxfrags < 2)
+ dmabuf->ossmaxfrags = 2;
+ val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
+ while (val < 8192) {
+ val <<= 1;
+ dmabuf->ossmaxfrags <<= 1;
+ }
+ while (val > 65536) {
+ val >>= 1;
+ dmabuf->ossmaxfrags >>= 1;
+ }
+ dmabuf->ready = 0;
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
+ dmabuf->ossfragsize, dmabuf->ossmaxfrags);
+#endif
+ return 0;
+ case SNDCTL_DSP_GETOSPACE:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ if (codec_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0)
+ return val;
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0)
+ return val;
+ } else {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+ return val;
+ }
+ }
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ abinfo.fragsize = dmabuf->userfragsize;
+ abinfo.fragstotal = dmabuf->userfrags;
+ if (dmabuf->mapped)
+ abinfo.bytes = dmabuf->dmasize;
+ else
+ abinfo.bytes = ali_get_free_write_space(state);
+ abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+ printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n",
+ abinfo.bytes, abinfo.fragsize, abinfo.fragments,
+ abinfo.fragstotal);
+#endif
+ return copy_to_user(argp, &abinfo,
+ sizeof(abinfo)) ? -EFAULT : 0;
+ case SNDCTL_DSP_GETOPTR:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ if (codec_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0)
+ return val;
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0)
+ return val;
+ } else {
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+ return val;
+ }
+ }
+ spin_lock_irqsave(&state->card->lock, flags);
+ val = ali_get_free_write_space(state);
+ cinfo.bytes = dmabuf->total_bytes;
+ cinfo.ptr = dmabuf->hwptr;
+ cinfo.blocks = val / dmabuf->userfragsize;
+ if (codec_independent_spdif_locked > 0) {
+ if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
+ dmabuf->count += val;
+ dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
+ __ali_update_lvi(state, 2);
+ }
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
+ dmabuf->count += val;
+ dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
+ __ali_update_lvi(state, 3);
+ }
+ } else {
+ if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
+ dmabuf->count += val;
+ dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
+ __ali_update_lvi(state, 0);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+ printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
+ cinfo.blocks, cinfo.ptr, dmabuf->count);
+#endif
+ return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT : 0;
+ case SNDCTL_DSP_GETISPACE:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+ return val;
+ spin_lock_irqsave(&state->card->lock, flags);
+ abinfo.bytes = ali_get_available_read_data(state);
+ abinfo.fragsize = dmabuf->userfragsize;
+ abinfo.fragstotal = dmabuf->userfrags;
+ abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+ printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n",
+ abinfo.bytes, abinfo.fragsize, abinfo.fragments,
+ abinfo.fragstotal);
+#endif
+ return copy_to_user(argp, &abinfo,
+ sizeof(abinfo)) ? -EFAULT : 0;
+ case SNDCTL_DSP_GETIPTR:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+ return val;
+ spin_lock_irqsave(&state->card->lock, flags);
+ val = ali_get_available_read_data(state);
+ cinfo.bytes = dmabuf->total_bytes;
+ cinfo.blocks = val / dmabuf->userfragsize;
+ cinfo.ptr = dmabuf->hwptr;
+ if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
+ dmabuf->count -= val;
+ dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
+ __ali_update_lvi(state, 1);
+ }
+ spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+ printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
+ cinfo.blocks, cinfo.ptr, dmabuf->count);
+#endif
+ return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT: 0;
+ case SNDCTL_DSP_NONBLOCK:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_NONBLOCK\n");
+#endif
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+ case SNDCTL_DSP_GETCAPS:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETCAPS\n");
+#endif
+ return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
+ DSP_CAP_MMAP | DSP_CAP_BIND, p);
+ case SNDCTL_DSP_GETTRIGGER:
+ val = 0;
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger);
+#endif
+ return put_user(dmabuf->trigger, p);
+ case SNDCTL_DSP_SETTRIGGER:
+ if (get_user(val, p))
+ return -EFAULT;
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+ printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
+#endif
+ if (!(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
+ stop_adc(state);
+ }
+ if (!(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) {
+ stop_dac(state);
+ }
+ if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CODEC_SPDIFOUT_RUNNING) {
+ stop_spdifout(state);
+ }
+ if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) {
+ stop_spdifout(state);
+ }
+ dmabuf->trigger = val;
+ if (val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) {
+ if (!dmabuf->write_channel) {
+ dmabuf->ready = 0;
+ dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
+ if (!dmabuf->write_channel)
+ return -EBUSY;
+ }
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+ return ret;
+ if (dmabuf->mapped) {
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ dmabuf->count = 0;
+ dmabuf->swptr = dmabuf->hwptr;
+ dmabuf->count = ali_get_free_write_space(state);
+ dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
+ __ali_update_lvi(state, 0);
+ spin_unlock_irqrestore(&state->card->lock,
+ flags);
+ } else
+ start_dac(state);
+ }
+ if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CODEC_SPDIFOUT_RUNNING)) {
+ if (!dmabuf->codec_spdifout_channel) {
+ dmabuf->ready = 0;
+ dmabuf->codec_spdifout_channel = state->card->alloc_codec_spdifout_channel(state->card);
+ if (!dmabuf->codec_spdifout_channel)
+ return -EBUSY;
+ }
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 2)))
+ return ret;
+ if (dmabuf->mapped) {
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ dmabuf->count = 0;
+ dmabuf->swptr = dmabuf->hwptr;
+ dmabuf->count = ali_get_free_write_space(state);
+ dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
+ __ali_update_lvi(state, 2);
+ spin_unlock_irqrestore(&state->card->lock,
+ flags);
+ } else
+ start_spdifout(state);
+ }
+ if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)) {
+ if (!dmabuf->controller_spdifout_channel) {
+ dmabuf->ready = 0;
+ dmabuf->controller_spdifout_channel = state->card->alloc_controller_spdifout_channel(state->card);
+ if (!dmabuf->controller_spdifout_channel)
+ return -EBUSY;
+ }
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 3)))
+ return ret;
+ if (dmabuf->mapped) {
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ dmabuf->count = 0;
+ dmabuf->swptr = dmabuf->hwptr;
+ dmabuf->count = ali_get_free_write_space(state);
+ dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
+ __ali_update_lvi(state, 3);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ } else
+ start_spdifout(state);
+ }
+ if (val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) {
+ if (!dmabuf->read_channel) {
+ dmabuf->ready = 0;
+ dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
+ if (!dmabuf->read_channel)
+ return -EBUSY;
+ }
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+ return ret;
+ if (dmabuf->mapped) {
+ spin_lock_irqsave(&state->card->lock,
+ flags);
+ ali_update_ptr(state);
+ dmabuf->swptr = dmabuf->hwptr;
+ dmabuf->count = 0;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
+ ali_update_lvi(state, 1);
+ start_adc(state);
+ }
+ return 0;
+ case SNDCTL_DSP_SETDUPLEX:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SETDUPLEX\n");
+#endif
+ return -EINVAL;
+ case SNDCTL_DSP_GETODELAY:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ spin_lock_irqsave(&state->card->lock, flags);
+ ali_update_ptr(state);
+ val = dmabuf->count;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count);
+#endif
+ return put_user(val, p);
+ case SOUND_PCM_READ_RATE:
+#ifdef DEBUG
+ printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate);
+#endif
+ return put_user(dmabuf->rate, p);
+ case SOUND_PCM_READ_CHANNELS:
+#ifdef DEBUG
+ printk("SOUND_PCM_READ_CHANNELS\n");
+#endif
+ return put_user(2, p);
+ case SOUND_PCM_READ_BITS:
+#ifdef DEBUG
+ printk("SOUND_PCM_READ_BITS\n");
+#endif
+ return put_user(AFMT_S16_LE, p);
+ case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SETSPDIF\n");
+#endif
+ if (get_user(val, p))
+ return -EFAULT;
+ /* Check to make sure the codec supports S/PDIF transmitter */
+ if ((state->card->ac97_features & 4)) {
+ /* mask out the transmitter speed bits so the user can't set them */
+ val &= ~0x3000;
+ /* Add the current transmitter speed bits to the passed value */
+ ret = ali_ac97_get(codec, AC97_SPDIF_CONTROL);
+ val |= (ret & 0x3000);
+ ali_ac97_set(codec, AC97_SPDIF_CONTROL, val);
+ if (ali_ac97_get(codec, AC97_SPDIF_CONTROL) != val) {
+ printk(KERN_ERR "ali_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val);
+ return -EFAULT;
+ }
+ }
+#ifdef DEBUG
+ else
+ printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n");
+#endif
+ return put_user(val, p);
+ case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETSPDIF\n");
+#endif
+ if (get_user(val, p))
+ return -EFAULT;
+ /* Check to make sure the codec supports S/PDIF transmitter */
+ if (!(state->card->ac97_features & 4)) {
+#ifdef DEBUG
+ printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n");
+#endif
+ val = 0;
+ } else {
+ val = ali_ac97_get(codec, AC97_SPDIF_CONTROL);
+ }
+
+ return put_user(val, p);
+//end add support spdif out
+//add support 4,6 channel
+ case SNDCTL_DSP_GETCHANNELMASK:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETCHANNELMASK\n");
+#endif
+ if (get_user(val, p))
+ return -EFAULT;
+ /* Based on AC'97 DAC support, not ICH hardware */
+ val = DSP_BIND_FRONT;
+ if (state->card->ac97_features & 0x0004)
+ val |= DSP_BIND_SPDIF;
+ if (state->card->ac97_features & 0x0080)
+ val |= DSP_BIND_SURR;
+ if (state->card->ac97_features & 0x0140)
+ val |= DSP_BIND_CENTER_LFE;
+ return put_user(val, p);
+ case SNDCTL_DSP_BIND_CHANNEL:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_BIND_CHANNEL\n");
+#endif
+ if (get_user(val, p))
+ return -EFAULT;
+ if (val == DSP_BIND_QUERY) {
+ val = DSP_BIND_FRONT; /* Always report this as being enabled */
+ if (state->card->ac97_status & SPDIF_ON)
+ val |= DSP_BIND_SPDIF;
+ else {
+ if (state->card->ac97_status & SURR_ON)
+ val |= DSP_BIND_SURR;
+ if (state->card->
+ ac97_status & CENTER_LFE_ON)
+ val |= DSP_BIND_CENTER_LFE;
+ }
+ } else { /* Not a query, set it */
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ if (dmabuf->enable == DAC_RUNNING) {
+ stop_dac(state);
+ }
+ if (val & DSP_BIND_SPDIF) { /* Turn on SPDIF */
+ /* Ok, this should probably define what slots
+ * to use. For now, we'll only set it to the
+ * defaults:
+ *
+ * non multichannel codec maps to slots 3&4
+ * 2 channel codec maps to slots 7&8
+ * 4 channel codec maps to slots 6&9
+ * 6 channel codec maps to slots 10&11
+ *
+ * there should be some way for the app to
+ * select the slot assignment.
+ */
+ i_scr = inl(state->card->iobase + ALI_SCR);
+ if (codec_independent_spdif_locked > 0) {
+
+ if ((i_scr & 0x00300000) == 0x00100000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
+ } else {
+ if ((i_scr & 0x00300000) == 0x00200000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked);
+ } else {
+ if ((i_scr & 0x00300000) == 0x00300000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked);
+ }
+ }
+ }
+ } else { /* codec spdif out (pcm out share ) */
+ ali_set_spdif_output(state, AC97_EA_SPSA_3_4, dmabuf->rate); //I do not modify
+ }
+
+ if (!(state->card->ac97_status & SPDIF_ON))
+ val &= ~DSP_BIND_SPDIF;
+ } else {
+ int mask;
+ int channels;
+ /* Turn off S/PDIF if it was on */
+ if (state->card->ac97_status & SPDIF_ON)
+ ali_set_spdif_output(state, -1, 0);
+ mask =
+ val & (DSP_BIND_FRONT | DSP_BIND_SURR |
+ DSP_BIND_CENTER_LFE);
+ switch (mask) {
+ case DSP_BIND_FRONT:
+ channels = 2;
+ break;
+ case DSP_BIND_FRONT | DSP_BIND_SURR:
+ channels = 4;
+ break;
+ case DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE:
+ channels = 6;
+ break;
+ default:
+ val = DSP_BIND_FRONT;
+ channels = 2;
+ break;
+ }
+ ali_set_dac_channels(state, channels);
+ /* check that they really got turned on */
+ if (!state->card->ac97_status & SURR_ON)
+ val &= ~DSP_BIND_SURR;
+ if (!state->card->
+ ac97_status & CENTER_LFE_ON)
+ val &= ~DSP_BIND_CENTER_LFE;
+ }
+ }
+ return put_user(val, p);
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
+ case SNDCTL_DSP_SETSYNCRO:
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int ali_open(struct inode *inode, struct file *file)
+{
+ int i = 0;
+ struct ali_card *card = devs;
+ struct ali_state *state = NULL;
+ struct dmabuf *dmabuf = NULL;
+ unsigned int i_scr;
+
+ /* find an available virtual channel (instance of /dev/dsp) */
+
+ while (card != NULL) {
+
+ /*
+ * If we are initializing and then fail, card could go
+ * away unuexpectedly while we are in the for() loop.
+ * So, check for card on each iteration before we check
+ * for card->initializing to avoid a possible oops.
+ * This usually only matters for times when the driver is
+ * autoloaded by kmod.
+ */
+ for (i = 0; i < 50 && card && card->initializing; i++) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 20);
+ }
+
+ for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) {
+ if (card->states[i] == NULL) {
+ state = card->states[i] = (struct ali_state *) kmalloc(sizeof(struct ali_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ memset(state, 0, sizeof(struct ali_state));
+ dmabuf = &state->dmabuf;
+ goto found_virt;
+ }
+ }
+ card = card->next;
+ }
+
+ /* no more virtual channel avaiable */
+ if (!state)
+ return -ENODEV;
+found_virt:
+ /* initialize the virtual channel */
+
+ state->virt = i;
+ state->card = card;
+ state->magic = ALI5455_STATE_MAGIC;
+ init_waitqueue_head(&dmabuf->wait);
+ init_MUTEX(&state->open_sem);
+ file->private_data = state;
+ dmabuf->trigger = 0;
+ /* allocate hardware channels */
+ if (file->f_mode & FMODE_READ) {
+ if ((dmabuf->read_channel =
+ card->alloc_rec_pcm_channel(card)) == NULL) {
+ kfree(card->states[i]);
+ card->states[i] = NULL;
+ return -EBUSY;
+ }
+ dmabuf->trigger |= PCM_ENABLE_INPUT;
+ ali_set_adc_rate(state, 8000);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (codec_independent_spdif_locked > 0) {
+ if ((dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card)) == NULL) {
+ kfree(card->states[i]);
+ card->states[i] = NULL;
+ return -EBUSY;
+ }
+ dmabuf->trigger |= SPDIF_ENABLE_OUTPUT;
+ ali_set_codecspdifout_rate(state, codec_independent_spdif_locked); //It must add
+ i_scr = inl(state->card->iobase + ALI_SCR);
+ if ((i_scr & 0x00300000) == 0x00100000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
+ } else {
+ if ((i_scr & 0x00300000) == 0x00200000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked);
+ } else {
+ if ((i_scr & 0x00300000) == 0x00300000) {
+ ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked);
+ } else {
+ ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
+ }
+ }
+
+ }
+ } else {
+ if (controller_independent_spdif_locked > 0) {
+ if ((dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card)) == NULL) {
+ kfree(card->states[i]);
+ card->states[i] = NULL;
+ return -EBUSY;
+ }
+ dmabuf->trigger |= SPDIF_ENABLE_OUTPUT;
+ ali_set_spdifout_rate(state, controller_independent_spdif_locked);
+ } else {
+ if ((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) {
+ kfree(card->states[i]);
+ card->states[i] = NULL;
+ return -EBUSY;
+ }
+ /* Initialize to 8kHz? What if we don't support 8kHz? */
+ /* Let's change this to check for S/PDIF stuff */
+
+ dmabuf->trigger |= PCM_ENABLE_OUTPUT;
+ if (codec_pcmout_share_spdif_locked) {
+ ali_set_dac_rate(state, codec_pcmout_share_spdif_locked);
+ ali_set_spdif_output(state, AC97_EA_SPSA_3_4, codec_pcmout_share_spdif_locked);
+ } else {
+ ali_set_dac_rate(state, 8000);
+ }
+ }
+
+ }
+ }
+
+ /* set default sample format. According to OSS Programmer's Guide /dev/dsp
+ should be default to unsigned 8-bits, mono, with sample rate 8kHz and
+ /dev/dspW will accept 16-bits sample, but we don't support those so we
+ set it immediately to stereo and 16bit, which is all we do support */
+ dmabuf->fmt |= ALI5455_FMT_16BIT | ALI5455_FMT_STEREO;
+ dmabuf->ossfragsize = 0;
+ dmabuf->ossmaxfrags = 0;
+ dmabuf->subdivision = 0;
+ state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+ outl(0x00000000, card->iobase + ALI_INTERRUPTCR);
+ outl(0x00000000, card->iobase + ALI_INTERRUPTSR);
+ return nonseekable_open(inode, file);
+}
+
+static int ali_release(struct inode *inode, struct file *file)
+{
+ struct ali_state *state = (struct ali_state *) file->private_data;
+ struct ali_card *card = state->card;
+ struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned long flags;
+ lock_kernel();
+
+ /* stop DMA state machine and free DMA buffers/channels */
+ if (dmabuf->trigger & PCM_ENABLE_OUTPUT)
+ drain_dac(state, 0);
+
+ if (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)
+ drain_spdifout(state, 0);
+
+ if (dmabuf->trigger & PCM_ENABLE_INPUT)
+ stop_adc(state);
+
+ spin_lock_irqsave(&card->lock, flags);
+ dealloc_dmabuf(state);
+ if (file->f_mode & FMODE_WRITE) {
+ if (codec_independent_spdif_locked > 0) {
+ state->card->free_pcm_channel(state->card, dmabuf->codec_spdifout_channel->num);
+ } else {
+ if (controller_independent_spdif_locked > 0)
+ state->card->free_pcm_channel(state->card,
+ dmabuf->controller_spdifout_channel->num);
+ else state->card->free_pcm_channel(state->card,
+ dmabuf->write_channel->num);
+ }
+ }
+ if (file->f_mode & FMODE_READ)
+ state->card->free_pcm_channel(state->card, dmabuf->read_channel->num);
+
+ state->card->states[state->virt] = NULL;
+ kfree(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+ unlock_kernel();
+ return 0;
+}
+
+static /*const */ struct file_operations ali_audio_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = ali_read,
+ .write = ali_write,
+ .poll = ali_poll,
+ .ioctl = ali_ioctl,
+ .mmap = ali_mmap,
+ .open = ali_open,
+ .release = ali_release,
+};
+
+/* Read AC97 codec registers */
+static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg)
+{
+ struct ali_card *card = dev->private_data;
+ int count1 = 100;
+ char val;
+ unsigned short int data = 0, count, addr1, addr2 = 0;
+
+ spin_lock(&card->ac97_lock);
+ while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000))
+ udelay(1);
+
+ addr1 = reg;
+ reg |= 0x0080;
+ for (count = 0; count < 0x7f; count++) {
+ val = inb(card->iobase + ALI_CSPSR);
+ if (val & 0x08)
+ break;
+ }
+ if (count == 0x7f)
+ {
+ spin_unlock(&card->ac97_lock);
+ return -1;
+ }
+ outw(reg, (card->iobase + ALI_CPR) + 2);
+ for (count = 0; count < 0x7f; count++) {
+ val = inb(card->iobase + ALI_CSPSR);
+ if (val & 0x02) {
+ data = inw(card->iobase + ALI_SPR);
+ addr2 = inw((card->iobase + ALI_SPR) + 2);
+ break;
+ }
+ }
+ spin_unlock(&card->ac97_lock);
+ if (count == 0x7f)
+ return -1;
+ if (addr2 != addr1)
+ return -1;
+ return ((u16) data);
+}
+
+/* write ac97 codec register */
+
+static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data)
+{
+ struct ali_card *card = dev->private_data;
+ int count1 = 100;
+ char val;
+ unsigned short int count;
+
+ spin_lock(&card->ac97_lock);
+ while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000))
+ udelay(1);
+
+ for (count = 0; count < 0x7f; count++) {
+ val = inb(card->iobase + ALI_CSPSR);
+ if (val & 0x08)
+ break;
+ }
+ if (count == 0x7f) {
+ printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n");
+ spin_unlock(&card->ac97_lock);
+ return;
+ }
+ outw(data, (card->iobase + ALI_CPR));
+ outb(reg, (card->iobase + ALI_CPR) + 2);
+ for (count = 0; count < 0x7f; count++) {
+ val = inb(card->iobase + ALI_CSPSR);
+ if (val & 0x01)
+ break;
+ }
+ spin_unlock(&card->ac97_lock);
+ if (count == 0x7f)
+ printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n");
+ return;
+}
+
+/* OSS /dev/mixer file operation methods */
+
+static int ali_open_mixdev(struct inode *inode, struct file *file)
+{
+ int i;
+ int minor = iminor(inode);
+ struct ali_card *card = devs;
+ for (card = devs; card != NULL; card = card->next) {
+ /*
+ * If we are initializing and then fail, card could go
+ * away unuexpectedly while we are in the for() loop.
+ * So, check for card on each iteration before we check
+ * for card->initializing to avoid a possible oops.
+ * This usually only matters for times when the driver is
+ * autoloaded by kmod.
+ */
+ for (i = 0; i < 50 && card && card->initializing; i++) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 20);
+ }
+ for (i = 0; i < NR_AC97 && card && !card->initializing; i++)
+ if (card->ac97_codec[i] != NULL
+ && card->ac97_codec[i]->dev_mixer == minor) {
+ file->private_data = card->ac97_codec[i];
+ return nonseekable_open(inode, file);
+ }
+ }
+ return -ENODEV;
+}
+
+static int ali_ioctl_mixdev(struct inode *inode,
+ struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ac97_codec *codec = (struct ac97_codec *) file->private_data;
+ return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static /*const */ struct file_operations ali_mixer_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = ali_ioctl_mixdev,
+ .open = ali_open_mixdev,
+};
+
+/* AC97 codec initialisation. These small functions exist so we don't
+ duplicate code between module init and apm resume */
+
+static inline int ali_ac97_exists(struct ali_card *card, int ac97_number)
+{
+ unsigned int i = 1;
+ u32 reg = inl(card->iobase + ALI_RTSR);
+ if (ac97_number) {
+ while (i < 100) {
+
+ reg = inl(card->iobase + ALI_RTSR);
+ if (reg & 0x40) {
+ break;
+ } else {
+ outl(reg | 0x00000040,
+ card->iobase + 0x34);
+ udelay(1);
+ }
+ i++;
+ }
+
+ } else {
+ while (i < 100) {
+ reg = inl(card->iobase + ALI_RTSR);
+ if (reg & 0x80) {
+ break;
+ } else {
+ outl(reg | 0x00000080,
+ card->iobase + 0x34);
+ udelay(1);
+ }
+ i++;
+ }
+ }
+
+ if (ac97_number)
+ return reg & 0x40;
+ else
+ return reg & 0x80;
+}
+
+static inline int ali_ac97_enable_variable_rate(struct ac97_codec *codec)
+{
+ ali_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
+ ali_ac97_set(codec, AC97_EXTENDED_STATUS, ali_ac97_get(codec, AC97_EXTENDED_STATUS) | 0xE800);
+ return (ali_ac97_get(codec, AC97_EXTENDED_STATUS) & 1);
+}
+
+
+static int ali_ac97_probe_and_powerup(struct ali_card *card, struct ac97_codec *codec)
+{
+ /* Returns 0 on failure */
+ int i;
+ u16 addr;
+ if (ac97_probe_codec(codec) == 0)
+ return 0;
+ /* ac97_probe_codec is success ,then begin to init codec */
+ ali_ac97_set(codec, AC97_RESET, 0xffff);
+ if (card->channel[0].used == 1) {
+ ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000);
+ ali_ac97_set(codec, AC97_LINEIN_VOL, 0x0808);
+ ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F);
+ }
+
+ if (card->channel[2].used == 1) //if MICin then init codec
+ {
+ ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000);
+ ali_ac97_set(codec, AC97_MIC_VOL, 0x8808);
+ ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F);
+ ali_ac97_set(codec, AC97_RECORD_GAIN_MIC, 0x0000);
+ }
+
+ ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, 0x0000);
+ ali_ac97_set(codec, AC97_HEADPHONE_VOL, 0x0000);
+ ali_ac97_set(codec, AC97_PCMOUT_VOL, 0x0000);
+ ali_ac97_set(codec, AC97_CD_VOL, 0x0808);
+ ali_ac97_set(codec, AC97_VIDEO_VOL, 0x0808);
+ ali_ac97_set(codec, AC97_AUX_VOL, 0x0808);
+ ali_ac97_set(codec, AC97_PHONE_VOL, 0x8048);
+ ali_ac97_set(codec, AC97_PCBEEP_VOL, 0x0000);
+ ali_ac97_set(codec, AC97_GENERAL_PURPOSE, AC97_GP_MIX);
+ ali_ac97_set(codec, AC97_MASTER_VOL_MONO, 0x0000);
+ ali_ac97_set(codec, 0x38, 0x0000);
+ addr = ali_ac97_get(codec, 0x2a);
+ ali_ac97_set(codec, 0x2a, addr | 0x0001);
+ addr = ali_ac97_get(codec, 0x2a);
+ addr = ali_ac97_get(codec, 0x28);
+ ali_ac97_set(codec, 0x2c, 0xbb80);
+ addr = ali_ac97_get(codec, 0x2c);
+ /* power it all up */
+ ali_ac97_set(codec, AC97_POWER_CONTROL,
+ ali_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
+ /* wait for analog ready */
+ for (i = 10; i && ((ali_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 20);
+ }
+ /* FIXME !! */
+ i++;
+ return i;
+}
+
+
+/* I clone ali5455(2.4.7 ) not clone i810_audio(2.4.18) */
+
+static int ali_reset_5455(struct ali_card *card)
+{
+ outl(0x80000003, card->iobase + ALI_SCR);
+ outl(0x83838383, card->iobase + ALI_FIFOCR1);
+ outl(0x83838383, card->iobase + ALI_FIFOCR2);
+ if (controller_pcmout_share_spdif_locked > 0) {
+ outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001),
+ card->iobase + ALI_SPDIFICS);
+ outl(0x0408000a, card->iobase + ALI_INTERFACECR);
+ } else {
+ if (codec_independent_spdif_locked > 0) {
+ outl((inl(card->iobase + ALI_SCR) | 0x00100000), card->iobase + ALI_SCR); // now I select slot 7 & 8
+ outl(0x00200000, card->iobase + ALI_INTERFACECR); //enable codec independent spdifout
+ } else
+ outl(0x04080002, card->iobase + ALI_INTERFACECR);
+ }
+
+ outl(0x00000000, card->iobase + ALI_INTERRUPTCR);
+ outl(0x00000000, card->iobase + ALI_INTERRUPTSR);
+ if (controller_independent_spdif_locked > 0)
+ outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001),
+ card->iobase + ALI_SPDIFICS);
+ return 1;
+}
+
+
+static int ali_ac97_random_init_stuff(struct ali_card
+ *card)
+{
+ u32 reg = inl(card->iobase + ALI_SCR);
+ int i = 0;
+ reg = inl(card->iobase + ALI_SCR);
+ if ((reg & 2) == 0) /* Cold required */
+ reg |= 2;
+ else
+ reg |= 1; /* Warm */
+ reg &= ~0x80000000; /* ACLink on */
+ outl(reg, card->iobase + ALI_SCR);
+
+ while (i < 10) {
+ if ((inl(card->iobase + 0x18) & (1 << 1)) == 0)
+ break;
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ / 20);
+ i++;
+ }
+ if (i == 10) {
+ printk(KERN_ERR "ali_audio: AC'97 reset failed.\n");
+ return 0;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 2);
+ return 1;
+}
+
+/* AC97 codec initialisation. */
+
+static int __devinit ali_ac97_init(struct ali_card *card)
+{
+ int num_ac97 = 0;
+ int total_channels = 0;
+ struct ac97_codec *codec;
+ u16 eid;
+
+ if (!ali_ac97_random_init_stuff(card))
+ return 0;
+
+ /* Number of channels supported */
+ /* What about the codec? Just because the ICH supports */
+ /* multiple channels doesn't mean the codec does. */
+ /* we'll have to modify this in the codec section below */
+ /* to reflect what the codec has. */
+ /* ICH and ICH0 only support 2 channels so don't bother */
+ /* to check.... */
+ inl(card->iobase + ALI_CPR);
+ card->channels = 2;
+
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+
+ /* Assume codec isn't available until we go through the
+ * gauntlet below */
+ card->ac97_codec[num_ac97] = NULL;
+ /* The ICH programmer's reference says you should */
+ /* check the ready status before probing. So we chk */
+ /* What do we do if it's not ready? Wait and try */
+ /* again, or abort? */
+ if (!ali_ac97_exists(card, num_ac97)) {
+ if (num_ac97 == 0)
+ printk(KERN_ERR "ali_audio: Primary codec not ready.\n");
+ break;
+ }
+
+ if ((codec = ac97_alloc_codec()) == NULL)
+ return -ENOMEM;
+ /* initialize some basic codec information, other fields will be filled
+ in ac97_probe_codec */
+ codec->private_data = card;
+ codec->id = num_ac97;
+ codec->codec_read = ali_ac97_get;
+ codec->codec_write = ali_ac97_set;
+ if (!ali_ac97_probe_and_powerup(card, codec)) {
+ printk(KERN_ERR "ali_audio: timed out waiting for codec %d analog ready",
+ num_ac97);
+ kfree(codec);
+ break; /* it didn't work */
+ }
+
+ /* Store state information about S/PDIF transmitter */
+ card->ac97_status = 0;
+ /* Don't attempt to get eid until powerup is complete */
+ eid = ali_ac97_get(codec, AC97_EXTENDED_ID);
+ if (eid == 0xFFFF) {
+ printk(KERN_ERR "ali_audio: no codec attached ?\n");
+ kfree(codec);
+ break;
+ }
+
+ card->ac97_features = eid;
+ /* Now check the codec for useful features to make up for
+ the dumbness of the ali5455 hardware engine */
+ if (!(eid & 0x0001))
+ printk(KERN_WARNING
+ "ali_audio: only 48Khz playback available.\n");
+ else {
+ if (!ali_ac97_enable_variable_rate(codec)) {
+ printk(KERN_WARNING
+ "ali_audio: Codec refused to allow VRA, using 48Khz only.\n");
+ card->ac97_features &= ~1;
+ }
+ }
+
+ /* Determine how many channels the codec(s) support */
+ /* - The primary codec always supports 2 */
+ /* - If the codec supports AMAP, surround DACs will */
+ /* automaticlly get assigned to slots. */
+ /* * Check for surround DACs and increment if */
+ /* found. */
+ /* - Else check if the codec is revision 2.2 */
+ /* * If surround DACs exist, assign them to slots */
+ /* and increment channel count. */
+
+ /* All of this only applies to ICH2 and above. ICH */
+ /* and ICH0 only support 2 channels. ICH2 will only */
+ /* support multiple codecs in a "split audio" config. */
+ /* as described above. */
+
+ /* TODO: Remove all the debugging messages! */
+
+ if ((eid & 0xc000) == 0) /* primary codec */
+ total_channels += 2;
+ if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1)) < 0) {
+ printk(KERN_ERR "ali_audio: couldn't register mixer!\n");
+ kfree(codec);
+ break;
+ }
+ card->ac97_codec[num_ac97] = codec;
+ }
+ /* pick the minimum of channels supported by ICHx or codec(s) */
+ card->channels = (card->channels > total_channels) ? total_channels : card->channels;
+ return num_ac97;
+}
+
+static void __devinit ali_configure_clocking(void)
+{
+ struct ali_card *card;
+ struct ali_state *state;
+ struct dmabuf *dmabuf;
+ unsigned int i, offset, new_offset;
+ unsigned long flags;
+ card = devs;
+
+ /* We could try to set the clocking for multiple cards, but can you even have
+ * more than one ali in a machine? Besides, clocking is global, so unless
+ * someone actually thinks more than one ali in a machine is possible and
+ * decides to rewrite that little bit, setting the rate for more than one card
+ * is a waste of time.
+ */
+ if (card != NULL) {
+ state = card->states[0] = (struct ali_state *)
+ kmalloc(sizeof(struct ali_state), GFP_KERNEL);
+ if (state == NULL)
+ return;
+ memset(state, 0, sizeof(struct ali_state));
+ dmabuf = &state->dmabuf;
+ dmabuf->write_channel = card->alloc_pcm_channel(card);
+ state->virt = 0;
+ state->card = card;
+ state->magic = ALI5455_STATE_MAGIC;
+ init_waitqueue_head(&dmabuf->wait);
+ init_MUTEX(&state->open_sem);
+ dmabuf->fmt = ALI5455_FMT_STEREO | ALI5455_FMT_16BIT;
+ dmabuf->trigger = PCM_ENABLE_OUTPUT;
+ ali_set_dac_rate(state, 48000);
+ if (prog_dmabuf(state, 0) != 0)
+ goto config_out_nodmabuf;
+
+ if (dmabuf->dmasize < 16384)
+ goto config_out;
+
+ dmabuf->count = dmabuf->dmasize;
+ outb(31, card->iobase + dmabuf->write_channel->port + OFF_LVI);
+
+ local_irq_save(flags);
+ start_dac(state);
+ offset = ali_get_dma_addr(state, 0);
+ mdelay(50);
+ new_offset = ali_get_dma_addr(state, 0);
+ stop_dac(state);
+
+ outb(2, card->iobase + dmabuf->write_channel->port + OFF_CR);
+ local_irq_restore(flags);
+
+ i = new_offset - offset;
+
+ if (i == 0)
+ goto config_out;
+ i = i / 4 * 20;
+ if (i > 48500 || i < 47500) {
+ clocking = clocking * clocking / i;
+ }
+config_out:
+ dealloc_dmabuf(state);
+config_out_nodmabuf:
+ state->card->free_pcm_channel(state->card, state->dmabuf. write_channel->num);
+ kfree(state);
+ card->states[0] = NULL;
+ }
+}
+
+/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered
+ until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+
+static int __devinit ali_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct ali_card *card;
+ if (pci_enable_device(pci_dev))
+ return -EIO;
+ if (pci_set_dma_mask(pci_dev, ALI5455_DMA_MASK)) {
+ printk(KERN_ERR "ali5455: architecture does not support"
+ " 32bit PCI busmaster DMA\n");
+ return -ENODEV;
+ }
+
+ if ((card = kmalloc(sizeof(struct ali_card), GFP_KERNEL)) == NULL) {
+ printk(KERN_ERR "ali_audio: out of memory\n");
+ return -ENOMEM;
+ }
+ memset(card, 0, sizeof(*card));
+ card->initializing = 1;
+ card->iobase = pci_resource_start(pci_dev, 0);
+ card->pci_dev = pci_dev;
+ card->pci_id = pci_id->device;
+ card->irq = pci_dev->irq;
+ card->next = devs;
+ card->magic = ALI5455_CARD_MAGIC;
+#ifdef CONFIG_PM
+ card->pm_suspended = 0;
+#endif
+ spin_lock_init(&card->lock);
+ spin_lock_init(&card->ac97_lock);
+ devs = card;
+ pci_set_master(pci_dev);
+ printk(KERN_INFO "ali: %s found at IO 0x%04lx, IRQ %d\n",
+ card_names[pci_id->driver_data], card->iobase, card->irq);
+ card->alloc_pcm_channel = ali_alloc_pcm_channel;
+ card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel;
+ card->alloc_rec_mic_channel = ali_alloc_rec_mic_channel;
+ card->alloc_codec_spdifout_channel = ali_alloc_codec_spdifout_channel;
+ card->alloc_controller_spdifout_channel = ali_alloc_controller_spdifout_channel;
+ card->free_pcm_channel = ali_free_pcm_channel;
+ card->channel[0].offset = 0;
+ card->channel[0].port = 0x40;
+ card->channel[0].num = 0;
+ card->channel[1].offset = 0;
+ card->channel[1].port = 0x50;
+ card->channel[1].num = 1;
+ card->channel[2].offset = 0;
+ card->channel[2].port = 0x60;
+ card->channel[2].num = 2;
+ card->channel[3].offset = 0;
+ card->channel[3].port = 0x70;
+ card->channel[3].num = 3;
+ card->channel[4].offset = 0;
+ card->channel[4].port = 0xb0;
+ card->channel[4].num = 4;
+ /* claim our iospace and irq */
+ request_region(card->iobase, 256, card_names[pci_id->driver_data]);
+ if (request_irq(card->irq, &ali_interrupt, SA_SHIRQ,
+ card_names[pci_id->driver_data], card)) {
+ printk(KERN_ERR "ali_audio: unable to allocate irq %d\n",
+ card->irq);
+ release_region(card->iobase, 256);
+ kfree(card);
+ return -ENODEV;
+ }
+
+ if (ali_reset_5455(card) <= 0) {
+ unregister_sound_dsp(card->dev_audio);
+ release_region(card->iobase, 256);
+ free_irq(card->irq, card);
+ kfree(card);
+ return -ENODEV;
+ }
+
+ /* initialize AC97 codec and register /dev/mixer */
+ if (ali_ac97_init(card) < 0) {
+ release_region(card->iobase, 256);
+ free_irq(card->irq, card);
+ kfree(card);
+ return -ENODEV;
+ }
+
+ pci_set_drvdata(pci_dev, card);
+
+ if (clocking == 0) {
+ clocking = 48000;
+ ali_configure_clocking();
+ }
+
+ /* register /dev/dsp */
+ if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1)) < 0) {
+ int i;
+ printk(KERN_ERR"ali_audio: couldn't register DSP device!\n");
+ release_region(card->iobase, 256);
+ free_irq(card->irq, card);
+ for (i = 0; i < NR_AC97; i++)
+ if (card->ac97_codec[i] != NULL) {
+ unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+ kfree(card->ac97_codec[i]);
+ }
+ kfree(card);
+ return -ENODEV;
+ }
+ card->initializing = 0;
+ return 0;
+}
+
+static void __devexit ali_remove(struct pci_dev *pci_dev)
+{
+ int i;
+ struct ali_card *card = pci_get_drvdata(pci_dev);
+ /* free hardware resources */
+ free_irq(card->irq, devs);
+ release_region(card->iobase, 256);
+ /* unregister audio devices */
+ for (i = 0; i < NR_AC97; i++)
+ if (card->ac97_codec[i] != NULL) {
+ unregister_sound_mixer(card->ac97_codec[i]->
+ dev_mixer);
+ ac97_release_codec(card->ac97_codec[i]);
+ card->ac97_codec[i] = NULL;
+ }
+ unregister_sound_dsp(card->dev_audio);
+ kfree(card);
+}
+
+#ifdef CONFIG_PM
+static int ali_pm_suspend(struct pci_dev *dev, pm_message_t pm_state)
+{
+ struct ali_card *card = pci_get_drvdata(dev);
+ struct ali_state *state;
+ unsigned long flags;
+ struct dmabuf *dmabuf;
+ int i, num_ac97;
+
+ if (!card)
+ return 0;
+ spin_lock_irqsave(&card->lock, flags);
+ card->pm_suspended = 1;
+ for (i = 0; i < NR_HW_CH; i++) {
+ state = card->states[i];
+ if (!state)
+ continue;
+ /* this happens only if there are open files */
+ dmabuf = &state->dmabuf;
+ if (dmabuf->enable & DAC_RUNNING ||
+ (dmabuf->count
+ && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) {
+ state->pm_saved_dac_rate = dmabuf->rate;
+ stop_dac(state);
+ } else {
+ state->pm_saved_dac_rate = 0;
+ }
+ if (dmabuf->enable & ADC_RUNNING) {
+ state->pm_saved_adc_rate = dmabuf->rate;
+ stop_adc(state);
+ } else {
+ state->pm_saved_adc_rate = 0;
+ }
+ dmabuf->ready = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->count = dmabuf->total_bytes = 0;
+ }
+
+ spin_unlock_irqrestore(&card->lock, flags);
+ /* save mixer settings */
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+ struct ac97_codec *codec = card->ac97_codec[num_ac97];
+ if (!codec)
+ continue;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if ((supported_mixer(codec, i)) && (codec->read_mixer)) {
+ card->pm_saved_mixer_settings[i][num_ac97] = codec->read_mixer(codec, i);
+ }
+ }
+ }
+ pci_save_state(dev); /* XXX do we need this? */
+ pci_disable_device(dev); /* disable busmastering */
+ pci_set_power_state(dev, 3); /* Zzz. */
+ return 0;
+}
+
+
+static int ali_pm_resume(struct pci_dev *dev)
+{
+ int num_ac97, i = 0;
+ struct ali_card *card = pci_get_drvdata(dev);
+ pci_enable_device(dev);
+ pci_restore_state(dev);
+ /* observation of a toshiba portege 3440ct suggests that the
+ hardware has to be more or less completely reinitialized from
+ scratch after an apm suspend. Works For Me. -dan */
+ ali_ac97_random_init_stuff(card);
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+ struct ac97_codec *codec = card->ac97_codec[num_ac97];
+ /* check they haven't stolen the hardware while we were
+ away */
+ if (!codec || !ali_ac97_exists(card, num_ac97)) {
+ if (num_ac97)
+ continue;
+ else
+ BUG();
+ }
+ if (!ali_ac97_probe_and_powerup(card, codec))
+ BUG();
+ if ((card->ac97_features & 0x0001)) {
+ /* at probe time we found we could do variable
+ rates, but APM suspend has made it forget
+ its magical powers */
+ if (!ali_ac97_enable_variable_rate(codec))
+ BUG();
+ }
+ /* we lost our mixer settings, so restore them */
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if (supported_mixer(codec, i)) {
+ int val = card->pm_saved_mixer_settings[i][num_ac97];
+ codec->mixer_state[i] = val;
+ codec->write_mixer(codec, i,
+ (val & 0xff),
+ ((val >> 8) & 0xff));
+ }
+ }
+ }
+
+ /* we need to restore the sample rate from whatever it was */
+ for (i = 0; i < NR_HW_CH; i++) {
+ struct ali_state *state = card->states[i];
+ if (state) {
+ if (state->pm_saved_adc_rate)
+ ali_set_adc_rate(state, state->pm_saved_adc_rate);
+ if (state->pm_saved_dac_rate)
+ ali_set_dac_rate(state, state->pm_saved_dac_rate);
+ }
+ }
+
+ card->pm_suspended = 0;
+ /* any processes that were reading/writing during the suspend
+ probably ended up here */
+ for (i = 0; i < NR_HW_CH; i++) {
+ struct ali_state *state = card->states[i];
+ if (state)
+ wake_up(&state->dmabuf.wait);
+ }
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("ALI 5455 audio support");
+MODULE_LICENSE("GPL");
+module_param(clocking, int, 0);
+/* FIXME: bool? */
+module_param(strict_clocking, uint, 0);
+module_param(codec_pcmout_share_spdif_locked, uint, 0);
+module_param(codec_independent_spdif_locked, uint, 0);
+module_param(controller_pcmout_share_spdif_locked, uint, 0);
+module_param(controller_independent_spdif_locked, uint, 0);
+#define ALI5455_MODULE_NAME "ali5455"
+static struct pci_driver ali_pci_driver = {
+ .name = ALI5455_MODULE_NAME,
+ .id_table = ali_pci_tbl,
+ .probe = ali_probe,
+ .remove = __devexit_p(ali_remove),
+#ifdef CONFIG_PM
+ .suspend = ali_pm_suspend,
+ .resume = ali_pm_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init ali_init_module(void)
+{
+ printk(KERN_INFO "ALI 5455 + AC97 Audio, version "
+ DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
+
+ if (codec_independent_spdif_locked > 0) {
+ if (codec_independent_spdif_locked == 32000
+ || codec_independent_spdif_locked == 44100
+ || codec_independent_spdif_locked == 48000) {
+ printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_independent_spdif_locked);
+ } else {
+ printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n");
+ codec_independent_spdif_locked = 0;
+ }
+ }
+ if (controller_independent_spdif_locked > 0) {
+ if (controller_independent_spdif_locked == 32000
+ || controller_independent_spdif_locked == 44100
+ || controller_independent_spdif_locked == 48000) {
+ printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", controller_independent_spdif_locked);
+ } else {
+ printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n");
+ controller_independent_spdif_locked = 0;
+ }
+ }
+
+ if (codec_pcmout_share_spdif_locked > 0) {
+ if (codec_pcmout_share_spdif_locked == 32000
+ || codec_pcmout_share_spdif_locked == 44100
+ || codec_pcmout_share_spdif_locked == 48000) {
+ printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_pcmout_share_spdif_locked);
+ } else {
+ printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n");
+ codec_pcmout_share_spdif_locked = 0;
+ }
+ }
+ if (controller_pcmout_share_spdif_locked > 0) {
+ if (controller_pcmout_share_spdif_locked == 32000
+ || controller_pcmout_share_spdif_locked == 44100
+ || controller_pcmout_share_spdif_locked == 48000) {
+ printk(KERN_INFO "ali_audio: Enabling controller S/PDIF at sample rate %dHz.\n", controller_pcmout_share_spdif_locked);
+ } else {
+ printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n");
+ controller_pcmout_share_spdif_locked = 0;
+ }
+ }
+ return pci_register_driver(&ali_pci_driver);
+}
+
+static void __exit ali_cleanup_module(void)
+{
+ pci_unregister_driver(&ali_pci_driver);
+}
+
+module_init(ali_init_module);
+module_exit(ali_cleanup_module);
+/*
+Local Variables:
+c-basic-offset: 8
+End:
+*/
diff --git a/sound/oss/au1000.c b/sound/oss/au1000.c
new file mode 100644
index 000000000000..4491733c9e4e
--- /dev/null
+++ b/sound/oss/au1000.c
@@ -0,0 +1,2214 @@
+/*
+ * au1000.c -- Sound driver for Alchemy Au1000 MIPS Internet Edge
+ * Processor.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * stevel@mvista.com or source@mvista.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ *
+ * Module command line parameters:
+ *
+ * Supported devices:
+ * /dev/dsp standard OSS /dev/dsp device
+ * /dev/mixer standard OSS /dev/mixer device
+ *
+ * Notes:
+ *
+ * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are
+ * taken, slightly modified or not at all, from the ES1371 driver,
+ * so refer to the credits in es1371.c for those. The rest of the
+ * code (probe, open, read, write, the ISR, etc.) is new.
+ *
+ * Revision history
+ * 06.27.2001 Initial version
+ * 03.20.2002 Added mutex locks around read/write methods, to prevent
+ * simultaneous access on SMP or preemptible kernels. Also
+ * removed the counter/pointer fragment aligning at the end
+ * of read/write methods [stevel].
+ * 03.21.2002 Add support for coherent DMA on the audio read/write DMA
+ * channels [stevel].
+ *
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/init.h>
+#include <linux/page-flags.h>
+#include <linux/poll.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1000_dma.h>
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#undef AU1000_DEBUG
+#undef AU1000_VERBOSE_DEBUG
+
+#define AU1000_MODULE_NAME "Au1000 audio"
+#define PFX AU1000_MODULE_NAME
+
+#ifdef AU1000_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
+
+
+/* misc stuff */
+#define POLL_COUNT 0x5000
+#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
+
+/* Boot options */
+static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports it
+MODULE_PARM(vra, "i");
+MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
+
+
+/* --------------------------------------------------------------------- */
+
+struct au1000_state {
+ /* soundcore stuff */
+ int dev_audio;
+
+#ifdef AU1000_DEBUG
+ /* debug /proc entry */
+ struct proc_dir_entry *ps;
+ struct proc_dir_entry *ac97_ps;
+#endif /* AU1000_DEBUG */
+
+ struct ac97_codec codec;
+ unsigned codec_base_caps;// AC'97 reg 00h, "Reset Register"
+ unsigned codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID"
+ int no_vra; // do not use VRA
+
+ spinlock_t lock;
+ struct semaphore open_sem;
+ struct semaphore sem;
+ mode_t open_mode;
+ wait_queue_head_t open_wait;
+
+ struct dmabuf {
+ unsigned int dmanr; // DMA Channel number
+ unsigned sample_rate; // Hz
+ unsigned src_factor; // SRC interp/decimation (no vra)
+ unsigned sample_size; // 8 or 16
+ int num_channels; // 1 = mono, 2 = stereo, 4, 6
+ int dma_bytes_per_sample;// DMA bytes per audio sample frame
+ int user_bytes_per_sample;// User bytes per audio sample frame
+ int cnt_factor; // user-to-DMA bytes per audio
+ // sample frame
+ void *rawbuf;
+ dma_addr_t dmaaddr;
+ unsigned buforder;
+ unsigned numfrag; // # of DMA fragments in DMA buffer
+ unsigned fragshift;
+ void *nextIn; // ptr to next-in to DMA buffer
+ void *nextOut;// ptr to next-out from DMA buffer
+ int count; // current byte count in DMA buffer
+ unsigned total_bytes; // total bytes written or read
+ unsigned error; // over/underrun
+ wait_queue_head_t wait;
+ /* redundant, but makes calculations easier */
+ unsigned fragsize; // user perception of fragment size
+ unsigned dma_fragsize; // DMA (real) fragment size
+ unsigned dmasize; // Total DMA buffer size
+ // (mult. of DMA fragsize)
+ /* OSS stuff */
+ unsigned mapped:1;
+ unsigned ready:1;
+ unsigned stopped:1;
+ unsigned ossfragshift;
+ int ossmaxfrags;
+ unsigned subdivision;
+ } dma_dac , dma_adc;
+} au1000_state;
+
+/* --------------------------------------------------------------------- */
+
+
+static inline unsigned ld2(unsigned int x)
+{
+ unsigned r = 0;
+
+ if (x >= 0x10000) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x >= 0x100) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x >= 0x10) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x >= 4) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x >= 2)
+ r++;
+ return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void au1000_delay(int msec)
+{
+ unsigned long tmo;
+ signed long tmo2;
+
+ if (in_interrupt())
+ return;
+
+ tmo = jiffies + (msec * HZ) / 1000;
+ for (;;) {
+ tmo2 = tmo - jiffies;
+ if (tmo2 <= 0)
+ break;
+ schedule_timeout(tmo2);
+ }
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static u16 rdcodec(struct ac97_codec *codec, u8 addr)
+{
+ struct au1000_state *s = (struct au1000_state *)codec->private_data;
+ unsigned long flags;
+ u32 cmd;
+ u16 data;
+ int i;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ for (i = 0; i < POLL_COUNT; i++)
+ if (!(au_readl(AC97C_STATUS) & AC97C_CP))
+ break;
+ if (i == POLL_COUNT)
+ err("rdcodec: codec cmd pending expired!");
+
+ cmd = (u32) addr & AC97C_INDEX_MASK;
+ cmd |= AC97C_READ; // read command
+ au_writel(cmd, AC97C_CMD);
+
+ /* now wait for the data */
+ for (i = 0; i < POLL_COUNT; i++)
+ if (!(au_readl(AC97C_STATUS) & AC97C_CP))
+ break;
+ if (i == POLL_COUNT) {
+ err("rdcodec: read poll expired!");
+ return 0;
+ }
+
+ data = au_readl(AC97C_CMD) & 0xffff;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return data;
+}
+
+
+static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
+{
+ struct au1000_state *s = (struct au1000_state *)codec->private_data;
+ unsigned long flags;
+ u32 cmd;
+ int i;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ for (i = 0; i < POLL_COUNT; i++)
+ if (!(au_readl(AC97C_STATUS) & AC97C_CP))
+ break;
+ if (i == POLL_COUNT)
+ err("wrcodec: codec cmd pending expired!");
+
+ cmd = (u32) addr & AC97C_INDEX_MASK;
+ cmd &= ~AC97C_READ; // write command
+ cmd |= ((u32) data << AC97C_WD_BIT); // OR in the data word
+ au_writel(cmd, AC97C_CMD);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void waitcodec(struct ac97_codec *codec)
+{
+ u16 temp;
+ int i;
+
+ /* codec_wait is used to wait for a ready state after
+ an AC97C_RESET. */
+ au1000_delay(10);
+
+ // first poll the CODEC_READY tag bit
+ for (i = 0; i < POLL_COUNT; i++)
+ if (au_readl(AC97C_STATUS) & AC97C_READY)
+ break;
+ if (i == POLL_COUNT) {
+ err("waitcodec: CODEC_READY poll expired!");
+ return;
+ }
+ // get AC'97 powerdown control/status register
+ temp = rdcodec(codec, AC97_POWER_CONTROL);
+
+ // If anything is powered down, power'em up
+ if (temp & 0x7f00) {
+ // Power on
+ wrcodec(codec, AC97_POWER_CONTROL, 0);
+ au1000_delay(100);
+ // Reread
+ temp = rdcodec(codec, AC97_POWER_CONTROL);
+ }
+
+ // Check if Codec REF,ANL,DAC,ADC ready
+ if ((temp & 0x7f0f) != 0x000f)
+ err("codec reg 26 status (0x%x) not ready!!", temp);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/* stop the ADC before calling */
+static void set_adc_rate(struct au1000_state *s, unsigned rate)
+{
+ struct dmabuf *adc = &s->dma_adc;
+ struct dmabuf *dac = &s->dma_dac;
+ unsigned adc_rate, dac_rate;
+ u16 ac97_extstat;
+
+ if (s->no_vra) {
+ // calc SRC factor
+ adc->src_factor = ((96000 / rate) + 1) >> 1;
+ adc->sample_rate = 48000 / adc->src_factor;
+ return;
+ }
+
+ adc->src_factor = 1;
+
+ ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS);
+
+ rate = rate > 48000 ? 48000 : rate;
+
+ // enable VRA
+ wrcodec(&s->codec, AC97_EXTENDED_STATUS,
+ ac97_extstat | AC97_EXTSTAT_VRA);
+ // now write the sample rate
+ wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
+ // read it back for actual supported rate
+ adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE);
+
+#ifdef AU1000_VERBOSE_DEBUG
+ dbg("%s: set to %d Hz", __FUNCTION__, adc_rate);
+#endif
+
+ // some codec's don't allow unequal DAC and ADC rates, in which case
+ // writing one rate reg actually changes both.
+ dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE);
+ if (dac->num_channels > 2)
+ wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
+ if (dac->num_channels > 4)
+ wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
+
+ adc->sample_rate = adc_rate;
+ dac->sample_rate = dac_rate;
+}
+
+/* stop the DAC before calling */
+static void set_dac_rate(struct au1000_state *s, unsigned rate)
+{
+ struct dmabuf *dac = &s->dma_dac;
+ struct dmabuf *adc = &s->dma_adc;
+ unsigned adc_rate, dac_rate;
+ u16 ac97_extstat;
+
+ if (s->no_vra) {
+ // calc SRC factor
+ dac->src_factor = ((96000 / rate) + 1) >> 1;
+ dac->sample_rate = 48000 / dac->src_factor;
+ return;
+ }
+
+ dac->src_factor = 1;
+
+ ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS);
+
+ rate = rate > 48000 ? 48000 : rate;
+
+ // enable VRA
+ wrcodec(&s->codec, AC97_EXTENDED_STATUS,
+ ac97_extstat | AC97_EXTSTAT_VRA);
+ // now write the sample rate
+ wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
+ // I don't support different sample rates for multichannel,
+ // so make these channels the same.
+ if (dac->num_channels > 2)
+ wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
+ if (dac->num_channels > 4)
+ wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
+ // read it back for actual supported rate
+ dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE);
+
+#ifdef AU1000_VERBOSE_DEBUG
+ dbg("%s: set to %d Hz", __FUNCTION__, dac_rate);
+#endif
+
+ // some codec's don't allow unequal DAC and ADC rates, in which case
+ // writing one rate reg actually changes both.
+ adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE);
+
+ dac->sample_rate = dac_rate;
+ adc->sample_rate = adc_rate;
+}
+
+static void stop_dac(struct au1000_state *s)
+{
+ struct dmabuf *db = &s->dma_dac;
+ unsigned long flags;
+
+ if (db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ disable_dma(db->dmanr);
+
+ db->stopped = 1;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void stop_adc(struct au1000_state *s)
+{
+ struct dmabuf *db = &s->dma_adc;
+ unsigned long flags;
+
+ if (db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ disable_dma(db->dmanr);
+
+ db->stopped = 1;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void set_xmit_slots(int num_channels)
+{
+ u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_XMIT_SLOTS_MASK;
+
+ switch (num_channels) {
+ case 1: // mono
+ case 2: // stereo, slots 3,4
+ ac97_config |= (0x3 << AC97C_XMIT_SLOTS_BIT);
+ break;
+ case 4: // stereo with surround, slots 3,4,7,8
+ ac97_config |= (0x33 << AC97C_XMIT_SLOTS_BIT);
+ break;
+ case 6: // stereo with surround and center/LFE, slots 3,4,6,7,8,9
+ ac97_config |= (0x7b << AC97C_XMIT_SLOTS_BIT);
+ break;
+ }
+
+ au_writel(ac97_config, AC97C_CONFIG);
+}
+
+static void set_recv_slots(int num_channels)
+{
+ u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_RECV_SLOTS_MASK;
+
+ /*
+ * Always enable slots 3 and 4 (stereo). Slot 6 is
+ * optional Mic ADC, which I don't support yet.
+ */
+ ac97_config |= (0x3 << AC97C_RECV_SLOTS_BIT);
+
+ au_writel(ac97_config, AC97C_CONFIG);
+}
+
+static void start_dac(struct au1000_state *s)
+{
+ struct dmabuf *db = &s->dma_dac;
+ unsigned long flags;
+ unsigned long buf1, buf2;
+
+ if (!db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ au_readl(AC97C_STATUS); // read status to clear sticky bits
+
+ // reset Buffer 1 and 2 pointers to nextOut and nextOut+dma_fragsize
+ buf1 = virt_to_phys(db->nextOut);
+ buf2 = buf1 + db->dma_fragsize;
+ if (buf2 >= db->dmaaddr + db->dmasize)
+ buf2 -= db->dmasize;
+
+ set_xmit_slots(db->num_channels);
+
+ init_dma(db->dmanr);
+ if (get_dma_active_buffer(db->dmanr) == 0) {
+ clear_dma_done0(db->dmanr); // clear DMA done bit
+ set_dma_addr0(db->dmanr, buf1);
+ set_dma_addr1(db->dmanr, buf2);
+ } else {
+ clear_dma_done1(db->dmanr); // clear DMA done bit
+ set_dma_addr1(db->dmanr, buf1);
+ set_dma_addr0(db->dmanr, buf2);
+ }
+ set_dma_count(db->dmanr, db->dma_fragsize>>1);
+ enable_dma_buffers(db->dmanr);
+
+ start_dma(db->dmanr);
+
+#ifdef AU1000_VERBOSE_DEBUG
+ dump_au1000_dma_channel(db->dmanr);
+#endif
+
+ db->stopped = 0;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_adc(struct au1000_state *s)
+{
+ struct dmabuf *db = &s->dma_adc;
+ unsigned long flags;
+ unsigned long buf1, buf2;
+
+ if (!db->stopped)
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ au_readl(AC97C_STATUS); // read status to clear sticky bits
+
+ // reset Buffer 1 and 2 pointers to nextIn and nextIn+dma_fragsize
+ buf1 = virt_to_phys(db->nextIn);
+ buf2 = buf1 + db->dma_fragsize;
+ if (buf2 >= db->dmaaddr + db->dmasize)
+ buf2 -= db->dmasize;
+
+ set_recv_slots(db->num_channels);
+
+ init_dma(db->dmanr);
+ if (get_dma_active_buffer(db->dmanr) == 0) {
+ clear_dma_done0(db->dmanr); // clear DMA done bit
+ set_dma_addr0(db->dmanr, buf1);
+ set_dma_addr1(db->dmanr, buf2);
+ } else {
+ clear_dma_done1(db->dmanr); // clear DMA done bit
+ set_dma_addr1(db->dmanr, buf1);
+ set_dma_addr0(db->dmanr, buf2);
+ }
+ set_dma_count(db->dmanr, db->dma_fragsize>>1);
+ enable_dma_buffers(db->dmanr);
+
+ start_dma(db->dmanr);
+
+#ifdef AU1000_VERBOSE_DEBUG
+ dump_au1000_dma_channel(db->dmanr);
+#endif
+
+ db->stopped = 0;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+extern inline void dealloc_dmabuf(struct au1000_state *s, struct dmabuf *db)
+{
+ struct page *page, *pend;
+
+ if (db->rawbuf) {
+ /* undo marking the pages as reserved */
+ pend = virt_to_page(db->rawbuf +
+ (PAGE_SIZE << db->buforder) - 1);
+ for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+ ClearPageReserved(page);
+ dma_free_noncoherent(NULL,
+ PAGE_SIZE << db->buforder,
+ db->rawbuf,
+ db->dmaaddr);
+ }
+ db->rawbuf = db->nextIn = db->nextOut = NULL;
+ db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct au1000_state *s, struct dmabuf *db)
+{
+ int order;
+ unsigned user_bytes_per_sec;
+ unsigned bufs;
+ struct page *page, *pend;
+ unsigned rate = db->sample_rate;
+
+ if (!db->rawbuf) {
+ db->ready = db->mapped = 0;
+ for (order = DMABUF_DEFAULTORDER;
+ order >= DMABUF_MINORDER; order--)
+ if ((db->rawbuf = dma_alloc_noncoherent(NULL,
+ PAGE_SIZE << order,
+ &db->dmaaddr,
+ 0)))
+ break;
+ if (!db->rawbuf)
+ return -ENOMEM;
+ db->buforder = order;
+ /* now mark the pages as reserved;
+ otherwise remap_pfn_range doesn't do what we want */
+ pend = virt_to_page(db->rawbuf +
+ (PAGE_SIZE << db->buforder) - 1);
+ for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+ SetPageReserved(page);
+ }
+
+ db->cnt_factor = 1;
+ if (db->sample_size == 8)
+ db->cnt_factor *= 2;
+ if (db->num_channels == 1)
+ db->cnt_factor *= 2;
+ db->cnt_factor *= db->src_factor;
+
+ db->count = 0;
+ db->nextIn = db->nextOut = db->rawbuf;
+
+ db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;
+ db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?
+ 2 : db->num_channels);
+
+ user_bytes_per_sec = rate * db->user_bytes_per_sample;
+ bufs = PAGE_SIZE << db->buforder;
+ if (db->ossfragshift) {
+ if ((1000 << db->ossfragshift) < user_bytes_per_sec)
+ db->fragshift = ld2(user_bytes_per_sec/1000);
+ else
+ db->fragshift = db->ossfragshift;
+ } else {
+ db->fragshift = ld2(user_bytes_per_sec / 100 /
+ (db->subdivision ? db->subdivision : 1));
+ if (db->fragshift < 3)
+ db->fragshift = 3;
+ }
+
+ db->fragsize = 1 << db->fragshift;
+ db->dma_fragsize = db->fragsize * db->cnt_factor;
+ db->numfrag = bufs / db->dma_fragsize;
+
+ while (db->numfrag < 4 && db->fragshift > 3) {
+ db->fragshift--;
+ db->fragsize = 1 << db->fragshift;
+ db->dma_fragsize = db->fragsize * db->cnt_factor;
+ db->numfrag = bufs / db->dma_fragsize;
+ }
+
+ if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+ db->numfrag = db->ossmaxfrags;
+
+ db->dmasize = db->dma_fragsize * db->numfrag;
+ memset(db->rawbuf, 0, bufs);
+
+#ifdef AU1000_VERBOSE_DEBUG
+ dbg("rate=%d, samplesize=%d, channels=%d",
+ rate, db->sample_size, db->num_channels);
+ dbg("fragsize=%d, cnt_factor=%d, dma_fragsize=%d",
+ db->fragsize, db->cnt_factor, db->dma_fragsize);
+ dbg("numfrag=%d, dmasize=%d", db->numfrag, db->dmasize);
+#endif
+
+ db->ready = 1;
+ return 0;
+}
+
+extern inline int prog_dmabuf_adc(struct au1000_state *s)
+{
+ stop_adc(s);
+ return prog_dmabuf(s, &s->dma_adc);
+
+}
+
+extern inline int prog_dmabuf_dac(struct au1000_state *s)
+{
+ stop_dac(s);
+ return prog_dmabuf(s, &s->dma_dac);
+}
+
+
+/* hold spinlock for the following */
+static irqreturn_t dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct au1000_state *s = (struct au1000_state *) dev_id;
+ struct dmabuf *dac = &s->dma_dac;
+ unsigned long newptr;
+ u32 ac97c_stat, buff_done;
+
+ ac97c_stat = au_readl(AC97C_STATUS);
+#ifdef AU1000_VERBOSE_DEBUG
+ if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
+ dbg("AC97C status = 0x%08x", ac97c_stat);
+#endif
+
+ if ((buff_done = get_dma_buffer_done(dac->dmanr)) == 0) {
+ /* fastpath out, to ease interrupt sharing */
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&s->lock);
+
+ if (buff_done != (DMA_D0 | DMA_D1)) {
+ dac->nextOut += dac->dma_fragsize;
+ if (dac->nextOut >= dac->rawbuf + dac->dmasize)
+ dac->nextOut -= dac->dmasize;
+
+ /* update playback pointers */
+ newptr = virt_to_phys(dac->nextOut) + dac->dma_fragsize;
+ if (newptr >= dac->dmaaddr + dac->dmasize)
+ newptr -= dac->dmasize;
+
+ dac->count -= dac->dma_fragsize;
+ dac->total_bytes += dac->dma_fragsize;
+
+ if (dac->count <= 0) {
+#ifdef AU1000_VERBOSE_DEBUG
+ dbg("dac underrun");
+#endif
+ spin_unlock(&s->lock);
+ stop_dac(s);
+ spin_lock(&s->lock);
+ dac->count = 0;
+ dac->nextIn = dac->nextOut;
+ } else if (buff_done == DMA_D0) {
+ clear_dma_done0(dac->dmanr); // clear DMA done bit
+ set_dma_count0(dac->dmanr, dac->dma_fragsize>>1);
+ set_dma_addr0(dac->dmanr, newptr);
+ enable_dma_buffer0(dac->dmanr); // reenable
+ } else {
+ clear_dma_done1(dac->dmanr); // clear DMA done bit
+ set_dma_count1(dac->dmanr, dac->dma_fragsize>>1);
+ set_dma_addr1(dac->dmanr, newptr);
+ enable_dma_buffer1(dac->dmanr); // reenable
+ }
+ } else {
+ // both done bits set, we missed an interrupt
+ spin_unlock(&s->lock);
+ stop_dac(s);
+ spin_lock(&s->lock);
+
+ dac->nextOut += 2*dac->dma_fragsize;
+ if (dac->nextOut >= dac->rawbuf + dac->dmasize)
+ dac->nextOut -= dac->dmasize;
+
+ dac->count -= 2*dac->dma_fragsize;
+ dac->total_bytes += 2*dac->dma_fragsize;
+
+ if (dac->count > 0) {
+ spin_unlock(&s->lock);
+ start_dac(s);
+ spin_lock(&s->lock);
+ }
+ }
+
+ /* wake up anybody listening */
+ if (waitqueue_active(&dac->wait))
+ wake_up(&dac->wait);
+
+ spin_unlock(&s->lock);
+
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct au1000_state *s = (struct au1000_state *) dev_id;
+ struct dmabuf *adc = &s->dma_adc;
+ unsigned long newptr;
+ u32 ac97c_stat, buff_done;
+
+ ac97c_stat = au_readl(AC97C_STATUS);
+#ifdef AU1000_VERBOSE_DEBUG
+ if (ac97c_stat & (AC97C_RU | AC97C_RO))
+ dbg("AC97C status = 0x%08x", ac97c_stat);
+#endif
+
+ if ((buff_done = get_dma_buffer_done(adc->dmanr)) == 0) {
+ /* fastpath out, to ease interrupt sharing */
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&s->lock);
+
+ if (buff_done != (DMA_D0 | DMA_D1)) {
+ if (adc->count + adc->dma_fragsize > adc->dmasize) {
+ // Overrun. Stop ADC and log the error
+ spin_unlock(&s->lock);
+ stop_adc(s);
+ adc->error++;
+ err("adc overrun");
+ return IRQ_NONE;
+ }
+
+ adc->nextIn += adc->dma_fragsize;
+ if (adc->nextIn >= adc->rawbuf + adc->dmasize)
+ adc->nextIn -= adc->dmasize;
+
+ /* update capture pointers */
+ newptr = virt_to_phys(adc->nextIn) + adc->dma_fragsize;
+ if (newptr >= adc->dmaaddr + adc->dmasize)
+ newptr -= adc->dmasize;
+
+ adc->count += adc->dma_fragsize;
+ adc->total_bytes += adc->dma_fragsize;
+
+ if (buff_done == DMA_D0) {
+ clear_dma_done0(adc->dmanr); // clear DMA done bit
+ set_dma_count0(adc->dmanr, adc->dma_fragsize>>1);
+ set_dma_addr0(adc->dmanr, newptr);
+ enable_dma_buffer0(adc->dmanr); // reenable
+ } else {
+ clear_dma_done1(adc->dmanr); // clear DMA done bit
+ set_dma_count1(adc->dmanr, adc->dma_fragsize>>1);
+ set_dma_addr1(adc->dmanr, newptr);
+ enable_dma_buffer1(adc->dmanr); // reenable
+ }
+ } else {
+ // both done bits set, we missed an interrupt
+ spin_unlock(&s->lock);
+ stop_adc(s);
+ spin_lock(&s->lock);
+
+ if (adc->count + 2*adc->dma_fragsize > adc->dmasize) {
+ // Overrun. Log the error
+ adc->error++;
+ err("adc overrun");
+ spin_unlock(&s->lock);
+ return IRQ_NONE;
+ }
+
+ adc->nextIn += 2*adc->dma_fragsize;
+ if (adc->nextIn >= adc->rawbuf + adc->dmasize)
+ adc->nextIn -= adc->dmasize;
+
+ adc->count += 2*adc->dma_fragsize;
+ adc->total_bytes += 2*adc->dma_fragsize;
+
+ spin_unlock(&s->lock);
+ start_adc(s);
+ spin_lock(&s->lock);
+ }
+
+ /* wake up anybody listening */
+ if (waitqueue_active(&adc->wait))
+ wake_up(&adc->wait);
+
+ spin_unlock(&s->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t au1000_llseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+
+static int au1000_open_mixdev(struct inode *inode, struct file *file)
+{
+ file->private_data = &au1000_state;
+ return nonseekable_open(inode, file);
+}
+
+static int au1000_release_mixdev(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
+ unsigned long arg)
+{
+ return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static int au1000_ioctl_mixdev(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct au1000_state *s = (struct au1000_state *)file->private_data;
+ struct ac97_codec *codec = &s->codec;
+
+ return mixdev_ioctl(codec, cmd, arg);
+}
+
+static /*const */ struct file_operations au1000_mixer_fops = {
+ .owner = THIS_MODULE,
+ .llseek = au1000_llseek,
+ .ioctl = au1000_ioctl_mixdev,
+ .open = au1000_open_mixdev,
+ .release = au1000_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct au1000_state *s, int nonblock)
+{
+ unsigned long flags;
+ int count, tmo;
+
+ if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
+ return 0;
+
+ for (;;) {
+ spin_lock_irqsave(&s->lock, flags);
+ count = s->dma_dac.count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count <= 0)
+ break;
+ if (signal_pending(current))
+ break;
+ if (nonblock)
+ return -EBUSY;
+ tmo = 1000 * count / (s->no_vra ?
+ 48000 : s->dma_dac.sample_rate);
+ tmo /= s->dma_dac.dma_bytes_per_sample;
+ au1000_delay(tmo);
+ }
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline u8 S16_TO_U8(s16 ch)
+{
+ return (u8) (ch >> 8) + 0x80;
+}
+static inline s16 U8_TO_S16(u8 ch)
+{
+ return (s16) (ch - 0x80) << 8;
+}
+
+/*
+ * Translates user samples to dma buffer suitable for AC'97 DAC data:
+ * If mono, copy left channel to right channel in dma buffer.
+ * If 8 bit samples, cvt to 16-bit before writing to dma buffer.
+ * If interpolating (no VRA), duplicate every audio frame src_factor times.
+ */
+static int translate_from_user(struct dmabuf *db,
+ char* dmabuf,
+ char* userbuf,
+ int dmacount)
+{
+ int sample, i;
+ int interp_bytes_per_sample;
+ int num_samples;
+ int mono = (db->num_channels == 1);
+ char usersample[12];
+ s16 ch, dmasample[6];
+
+ if (db->sample_size == 16 && !mono && db->src_factor == 1) {
+ // no translation necessary, just copy
+ if (copy_from_user(dmabuf, userbuf, dmacount))
+ return -EFAULT;
+ return dmacount;
+ }
+
+ interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
+ num_samples = dmacount / interp_bytes_per_sample;
+
+ for (sample = 0; sample < num_samples; sample++) {
+ if (copy_from_user(usersample, userbuf,
+ db->user_bytes_per_sample)) {
+ dbg("%s: fault", __FUNCTION__);
+ return -EFAULT;
+ }
+
+ for (i = 0; i < db->num_channels; i++) {
+ if (db->sample_size == 8)
+ ch = U8_TO_S16(usersample[i]);
+ else
+ ch = *((s16 *) (&usersample[i * 2]));
+ dmasample[i] = ch;
+ if (mono)
+ dmasample[i + 1] = ch; // right channel
+ }
+
+ // duplicate every audio frame src_factor times
+ for (i = 0; i < db->src_factor; i++)
+ memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
+
+ userbuf += db->user_bytes_per_sample;
+ dmabuf += interp_bytes_per_sample;
+ }
+
+ return num_samples * interp_bytes_per_sample;
+}
+
+/*
+ * Translates AC'97 ADC samples to user buffer:
+ * If mono, send only left channel to user buffer.
+ * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer.
+ * If decimating (no VRA), skip over src_factor audio frames.
+ */
+static int translate_to_user(struct dmabuf *db,
+ char* userbuf,
+ char* dmabuf,
+ int dmacount)
+{
+ int sample, i;
+ int interp_bytes_per_sample;
+ int num_samples;
+ int mono = (db->num_channels == 1);
+ char usersample[12];
+
+ if (db->sample_size == 16 && !mono && db->src_factor == 1) {
+ // no translation necessary, just copy
+ if (copy_to_user(userbuf, dmabuf, dmacount))
+ return -EFAULT;
+ return dmacount;
+ }
+
+ interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
+ num_samples = dmacount / interp_bytes_per_sample;
+
+ for (sample = 0; sample < num_samples; sample++) {
+ for (i = 0; i < db->num_channels; i++) {
+ if (db->sample_size == 8)
+ usersample[i] =
+ S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));
+ else
+ *((s16 *) (&usersample[i * 2])) =
+ *((s16 *) (&dmabuf[i * 2]));
+ }
+
+ if (copy_to_user(userbuf, usersample,
+ db->user_bytes_per_sample)) {
+ dbg("%s: fault", __FUNCTION__);
+ return -EFAULT;
+ }
+
+ userbuf += db->user_bytes_per_sample;
+ dmabuf += interp_bytes_per_sample;
+ }
+
+ return num_samples * interp_bytes_per_sample;
+}
+
+/*
+ * Copy audio data to/from user buffer from/to dma buffer, taking care
+ * that we wrap when reading/writing the dma buffer. Returns actual byte
+ * count written to or read from the dma buffer.
+ */
+static int copy_dmabuf_user(struct dmabuf *db, char* userbuf,
+ int count, int to_user)
+{
+ char *bufptr = to_user ? db->nextOut : db->nextIn;
+ char *bufend = db->rawbuf + db->dmasize;
+ int cnt, ret;
+
+ if (bufptr + count > bufend) {
+ int partial = (int) (bufend - bufptr);
+ if (to_user) {
+ if ((cnt = translate_to_user(db, userbuf,
+ bufptr, partial)) < 0)
+ return cnt;
+ ret = cnt;
+ if ((cnt = translate_to_user(db, userbuf + partial,
+ db->rawbuf,
+ count - partial)) < 0)
+ return cnt;
+ ret += cnt;
+ } else {
+ if ((cnt = translate_from_user(db, bufptr, userbuf,
+ partial)) < 0)
+ return cnt;
+ ret = cnt;
+ if ((cnt = translate_from_user(db, db->rawbuf,
+ userbuf + partial,
+ count - partial)) < 0)
+ return cnt;
+ ret += cnt;
+ }
+ } else {
+ if (to_user)
+ ret = translate_to_user(db, userbuf, bufptr, count);
+ else
+ ret = translate_from_user(db, bufptr, userbuf, count);
+ }
+
+ return ret;
+}
+
+
+static ssize_t au1000_read(struct file *file, char *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct au1000_state *s = (struct au1000_state *)file->private_data;
+ struct dmabuf *db = &s->dma_adc;
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t ret;
+ unsigned long flags;
+ int cnt, usercnt, avail;
+
+ if (db->mapped)
+ return -ENXIO;
+ if (!access_ok(VERIFY_WRITE, buffer, count))
+ return -EFAULT;
+ ret = 0;
+
+ count *= db->cnt_factor;
+
+ down(&s->sem);
+ add_wait_queue(&db->wait, &wait);
+
+ while (count > 0) {
+ // wait for samples in ADC dma buffer
+ do {
+ if (db->stopped)
+ start_adc(s);
+ spin_lock_irqsave(&s->lock, flags);
+ avail = db->count;
+ if (avail <= 0)
+ __set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (avail <= 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ goto out;
+ }
+ up(&s->sem);
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ goto out2;
+ }
+ down(&s->sem);
+ }
+ } while (avail <= 0);
+
+ // copy from nextOut to user
+ if ((cnt = copy_dmabuf_user(db, buffer,
+ count > avail ?
+ avail : count, 1)) < 0) {
+ if (!ret)
+ ret = -EFAULT;
+ goto out;
+ }
+
+ spin_lock_irqsave(&s->lock, flags);
+ db->count -= cnt;
+ db->nextOut += cnt;
+ if (db->nextOut >= db->rawbuf + db->dmasize)
+ db->nextOut -= db->dmasize;
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ count -= cnt;
+ usercnt = cnt / db->cnt_factor;
+ buffer += usercnt;
+ ret += usercnt;
+ } // while (count > 0)
+
+out:
+ up(&s->sem);
+out2:
+ remove_wait_queue(&db->wait, &wait);
+ set_current_state(TASK_RUNNING);
+ return ret;
+}
+
+static ssize_t au1000_write(struct file *file, const char *buffer,
+ size_t count, loff_t * ppos)
+{
+ struct au1000_state *s = (struct au1000_state *)file->private_data;
+ struct dmabuf *db = &s->dma_dac;
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t ret = 0;
+ unsigned long flags;
+ int cnt, usercnt, avail;
+
+#ifdef AU1000_VERBOSE_DEBUG
+ dbg("write: count=%d", count);
+#endif
+
+ if (db->mapped)
+ return -ENXIO;
+ if (!access_ok(VERIFY_READ, buffer, count))
+ return -EFAULT;
+
+ count *= db->cnt_factor;
+
+ down(&s->sem);
+ add_wait_queue(&db->wait, &wait);
+
+ while (count > 0) {
+ // wait for space in playback buffer
+ do {
+ spin_lock_irqsave(&s->lock, flags);
+ avail = (int) db->dmasize - db->count;
+ if (avail <= 0)
+ __set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (avail <= 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ goto out;
+ }
+ up(&s->sem);
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ goto out2;
+ }
+ down(&s->sem);
+ }
+ } while (avail <= 0);
+
+ // copy from user to nextIn
+ if ((cnt = copy_dmabuf_user(db, (char *) buffer,
+ count > avail ?
+ avail : count, 0)) < 0) {
+ if (!ret)
+ ret = -EFAULT;
+ goto out;
+ }
+
+ spin_lock_irqsave(&s->lock, flags);
+ db->count += cnt;
+ db->nextIn += cnt;
+ if (db->nextIn >= db->rawbuf + db->dmasize)
+ db->nextIn -= db->dmasize;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (db->stopped)
+ start_dac(s);
+
+ count -= cnt;
+ usercnt = cnt / db->cnt_factor;
+ buffer += usercnt;
+ ret += usercnt;
+ } // while (count > 0)
+
+out:
+ up(&s->sem);
+out2:
+ remove_wait_queue(&db->wait, &wait);
+ set_current_state(TASK_RUNNING);
+ return ret;
+}
+
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int au1000_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct au1000_state *s = (struct au1000_state *)file->private_data;
+ unsigned long flags;
+ unsigned int mask = 0;
+
+ if (file->f_mode & FMODE_WRITE) {
+ if (!s->dma_dac.ready)
+ return 0;
+ poll_wait(file, &s->dma_dac.wait, wait);
+ }
+ if (file->f_mode & FMODE_READ) {
+ if (!s->dma_adc.ready)
+ return 0;
+ poll_wait(file, &s->dma_adc.wait, wait);
+ }
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ if (file->f_mode & FMODE_READ) {
+ if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (s->dma_dac.mapped) {
+ if (s->dma_dac.count >=
+ (signed)s->dma_dac.dma_fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ } else {
+ if ((signed) s->dma_dac.dmasize >=
+ s->dma_dac.count + (signed)s->dma_dac.dma_fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ return mask;
+}
+
+static int au1000_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct au1000_state *s = (struct au1000_state *)file->private_data;
+ struct dmabuf *db;
+ unsigned long size;
+ int ret = 0;
+
+ dbg(__FUNCTION__);
+
+ lock_kernel();
+ down(&s->sem);
+ if (vma->vm_flags & VM_WRITE)
+ db = &s->dma_dac;
+ else if (vma->vm_flags & VM_READ)
+ db = &s->dma_adc;
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (vma->vm_pgoff != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ size = vma->vm_end - vma->vm_start;
+ if (size > (PAGE_SIZE << db->buforder)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(db->rawbuf),
+ size, vma->vm_page_prot)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ vma->vm_flags &= ~VM_IO;
+ db->mapped = 1;
+out:
+ up(&s->sem);
+ unlock_kernel();
+ return ret;
+}
+
+
+#ifdef AU1000_VERBOSE_DEBUG
+static struct ioctl_str_t {
+ unsigned int cmd;
+ const char *str;
+} ioctl_str[] = {
+ {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
+ {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
+ {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
+ {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
+ {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
+ {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
+ {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
+ {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
+ {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
+ {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
+ {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
+ {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
+ {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
+ {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
+ {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
+ {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
+ {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
+ {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
+ {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
+ {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
+ {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
+ {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
+ {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
+ {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
+ {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
+ {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
+ {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
+ {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
+ {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
+ {OSS_GETVERSION, "OSS_GETVERSION"},
+ {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
+ {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
+ {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
+ {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
+};
+#endif
+
+// Need to hold a spin-lock before calling this!
+static int dma_count_done(struct dmabuf *db)
+{
+ if (db->stopped)
+ return 0;
+
+ return db->dma_fragsize - get_dma_residue(db->dmanr);
+}
+
+
+static int au1000_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct au1000_state *s = (struct au1000_state *)file->private_data;
+ unsigned long flags;
+ audio_buf_info abinfo;
+ count_info cinfo;
+ int count;
+ int val, mapped, ret, diff;
+
+ mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+ ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+
+#ifdef AU1000_VERBOSE_DEBUG
+ for (count=0; count<sizeof(ioctl_str)/sizeof(ioctl_str[0]); count++) {
+ if (ioctl_str[count].cmd == cmd)
+ break;
+ }
+ if (count < sizeof(ioctl_str) / sizeof(ioctl_str[0]))
+ dbg("ioctl %s, arg=0x%lx", ioctl_str[count].str, arg);
+ else
+ dbg("ioctl 0x%x unknown, arg=0x%lx", cmd, arg);
+#endif
+