From 871480933a1c28f8a9fed4c4d34d06c439a7a422 Mon Sep 17 00:00:00 2001
From: Srikant Patnaik
Date: Sun, 11 Jan 2015 12:28:04 +0530
Subject: Moved, renamed, and deleted files

The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
---
 sound/oss/CHANGELOG                 |  369 ++++
 sound/oss/Kconfig                   |  541 ++++++
 sound/oss/Makefile                  |  108 ++
 sound/oss/README.FIRST              |    6 +
 sound/oss/ad1848.c                  | 3069 ++++++++++++++++++++++++++++++
 sound/oss/ad1848.h                  |   24 +
 sound/oss/ad1848_mixer.h            |  253 +++
 sound/oss/aedsp16.c                 | 1373 ++++++++++++++
 sound/oss/audio.c                   |  985 ++++++++++
 sound/oss/bin2hex.c                 |   39 +
 sound/oss/coproc.h                  |   12 +
 sound/oss/dev_table.c               |  256 +++
 sound/oss/dev_table.h               |  390 ++++
 sound/oss/dmabuf.c                  | 1268 +++++++++++++
 sound/oss/dmasound/Kconfig          |   45 +
 sound/oss/dmasound/Makefile         |    7 +
 sound/oss/dmasound/dmasound.h       |  262 +++
 sound/oss/dmasound/dmasound_atari.c | 1620 ++++++++++++++++
 sound/oss/dmasound/dmasound_core.c  | 1589 ++++++++++++++++
 sound/oss/dmasound/dmasound_paula.c |  751 ++++++++
 sound/oss/dmasound/dmasound_q40.c   |  638 +++++++
 sound/oss/hex2hex.c                 |  101 +
 sound/oss/kahlua.c                  |  231 +++
 sound/oss/midi_ctrl.h               |   22 +
 sound/oss/midi_synth.c              |  712 +++++++
 sound/oss/midi_synth.h              |   47 +
 sound/oss/midibuf.c                 |  425 +++++
 sound/oss/mpu401.c                  | 1806 ++++++++++++++++++
 sound/oss/mpu401.h                  |   11 +
 sound/oss/msnd.c                    |  413 +++++
 sound/oss/msnd.h                    |  278 +++
 sound/oss/msnd_classic.c            |    3 +
 sound/oss/msnd_classic.h            |  185 ++
 sound/oss/msnd_pinnacle.c           | 1935 +++++++++++++++++++
 sound/oss/msnd_pinnacle.h           |  246 +++
 sound/oss/opl3.c                    | 1258 +++++++++++++
 sound/oss/opl3_hw.h                 |  246 +++
 sound/oss/os.h                      |   45 +
 sound/oss/pas2.h                    |   17 +
 sound/oss/pas2_card.c               |  455 +++++
 sound/oss/pas2_midi.c               |  262 +++
 sound/oss/pas2_mixer.c              |  336 ++++
 sound/oss/pas2_pcm.c                |  437 +++++
 sound/oss/pss.c                     | 1268 +++++++++++++
 sound/oss/sb.h                      |  185 ++
 sound/oss/sb_audio.c                | 1098 +++++++++++
 sound/oss/sb_card.c                 |  354 ++++
 sound/oss/sb_card.h                 |  149 ++
 sound/oss/sb_common.c               | 1292 +++++++++++++
 sound/oss/sb_ess.c                  | 1831 ++++++++++++++++++
 sound/oss/sb_ess.h                  |   34 +
 sound/oss/sb_midi.c                 |  206 +++
 sound/oss/sb_mixer.c                |  770 ++++++++
 sound/oss/sb_mixer.h                |  105 ++
 sound/oss/sequencer.c               | 1671 +++++++++++++++++
 sound/oss/sound_calls.h             |   87 +
 sound/oss/sound_config.h            |  147 ++
 sound/oss/sound_firmware.h          |    2 +
 sound/oss/sound_timer.c             |  327 ++++
 sound/oss/soundcard.c               |  739 ++++++++
 sound/oss/soundvers.h               |    2 +
 sound/oss/swarm_cs4297a.c           | 2768 +++++++++++++++++++++++++++
 sound/oss/sys_timer.c               |  285 +++
 sound/oss/trix.c                    |  525 ++++++
 sound/oss/tuning.h                  |   23 +
 sound/oss/uart401.c                 |  482 +++++
 sound/oss/uart6850.c                |  361 ++++
 sound/oss/ulaw.h                    |   69 +
 sound/oss/v_midi.c                  |  290 +++
 sound/oss/v_midi.h                  |   14 +
 sound/oss/vidc.c                    |  557 ++++++
 sound/oss/vidc.h                    |   63 +
 sound/oss/vidc_fill.S               |  218 +++
 sound/oss/vwsnd.c                   | 3498 +++++++++++++++++++++++++++++++++++
 sound/oss/waveartist.c              | 2024 ++++++++++++++++++++
 sound/oss/waveartist.h              |   92 +
 76 files changed, 44612 insertions(+)
 create mode 100644 sound/oss/CHANGELOG
 create mode 100644 sound/oss/Kconfig
 create mode 100644 sound/oss/Makefile
 create mode 100644 sound/oss/README.FIRST
 create mode 100644 sound/oss/ad1848.c
 create mode 100644 sound/oss/ad1848.h
 create mode 100644 sound/oss/ad1848_mixer.h
 create mode 100644 sound/oss/aedsp16.c
 create mode 100644 sound/oss/audio.c
 create mode 100644 sound/oss/bin2hex.c
 create mode 100644 sound/oss/coproc.h
 create mode 100644 sound/oss/dev_table.c
 create mode 100644 sound/oss/dev_table.h
 create mode 100644 sound/oss/dmabuf.c
 create mode 100644 sound/oss/dmasound/Kconfig
 create mode 100644 sound/oss/dmasound/Makefile
 create mode 100644 sound/oss/dmasound/dmasound.h
 create mode 100644 sound/oss/dmasound/dmasound_atari.c
 create mode 100644 sound/oss/dmasound/dmasound_core.c
 create mode 100644 sound/oss/dmasound/dmasound_paula.c
 create mode 100644 sound/oss/dmasound/dmasound_q40.c
 create mode 100644 sound/oss/hex2hex.c
 create mode 100644 sound/oss/kahlua.c
 create mode 100644 sound/oss/midi_ctrl.h
 create mode 100644 sound/oss/midi_synth.c
 create mode 100644 sound/oss/midi_synth.h
 create mode 100644 sound/oss/midibuf.c
 create mode 100644 sound/oss/mpu401.c
 create mode 100644 sound/oss/mpu401.h
 create mode 100644 sound/oss/msnd.c
 create mode 100644 sound/oss/msnd.h
 create mode 100644 sound/oss/msnd_classic.c
 create mode 100644 sound/oss/msnd_classic.h
 create mode 100644 sound/oss/msnd_pinnacle.c
 create mode 100644 sound/oss/msnd_pinnacle.h
 create mode 100644 sound/oss/opl3.c
 create mode 100644 sound/oss/opl3_hw.h
 create mode 100644 sound/oss/os.h
 create mode 100644 sound/oss/pas2.h
 create mode 100644 sound/oss/pas2_card.c
 create mode 100644 sound/oss/pas2_midi.c
 create mode 100644 sound/oss/pas2_mixer.c
 create mode 100644 sound/oss/pas2_pcm.c
 create mode 100644 sound/oss/pss.c
 create mode 100644 sound/oss/sb.h
 create mode 100644 sound/oss/sb_audio.c
 create mode 100644 sound/oss/sb_card.c
 create mode 100644 sound/oss/sb_card.h
 create mode 100644 sound/oss/sb_common.c
 create mode 100644 sound/oss/sb_ess.c
 create mode 100644 sound/oss/sb_ess.h
 create mode 100644 sound/oss/sb_midi.c
 create mode 100644 sound/oss/sb_mixer.c
 create mode 100644 sound/oss/sb_mixer.h
 create mode 100644 sound/oss/sequencer.c
 create mode 100644 sound/oss/sound_calls.h
 create mode 100644 sound/oss/sound_config.h
 create mode 100644 sound/oss/sound_firmware.h
 create mode 100644 sound/oss/sound_timer.c
 create mode 100644 sound/oss/soundcard.c
 create mode 100644 sound/oss/soundvers.h
 create mode 100644 sound/oss/swarm_cs4297a.c
 create mode 100644 sound/oss/sys_timer.c
 create mode 100644 sound/oss/trix.c
 create mode 100644 sound/oss/tuning.h
 create mode 100644 sound/oss/uart401.c
 create mode 100644 sound/oss/uart6850.c
 create mode 100644 sound/oss/ulaw.h
 create mode 100644 sound/oss/v_midi.c
 create mode 100644 sound/oss/v_midi.h
 create mode 100644 sound/oss/vidc.c
 create mode 100644 sound/oss/vidc.h
 create mode 100644 sound/oss/vidc_fill.S
 create mode 100644 sound/oss/vwsnd.c
 create mode 100644 sound/oss/waveartist.c
 create mode 100644 sound/oss/waveartist.h

(limited to 'sound/oss')

diff --git a/sound/oss/CHANGELOG b/sound/oss/CHANGELOG
new file mode 100644
index 00000000..8706cd66
--- /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/Kconfig b/sound/oss/Kconfig
new file mode 100644
index 00000000..5849b129
--- /dev/null
+++ b/sound/oss/Kconfig
@@ -0,0 +1,541 @@
+# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+# More hacking for modularisation.
+#
+# Prompt user for primary drivers.
+
+config SOUND_BCM_CS4297A
+	tristate "Crystal Sound CS4297a (for Swarm)"
+	depends on SIBYTE_SWARM
+	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_VWSND
+	tristate "SGI Visual Workstation Sound"
+	depends on X86_VISWS
+	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_MSNDCLAS
+	tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
+	depends on (m || !STANDALONE) && ISA
+	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 (m || !STANDALONE) && ISA
+	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_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_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_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.
+
+menuconfig SOUND_OSS
+	tristate "OSS sound modules"
+	depends on ISA_DMA_API && VIRT_TO_BUS
+	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.
+
+if SOUND_OSS
+
+config SOUND_TRACEINIT
+	bool "Verbose initialisation"
+	help
+	  Verbose soundcard initialization -- affects the format of autoprobe
+	  and initialization messages at boot time.
+
+config SOUND_DMAP
+	bool "Persistent DMA buffers"
+	---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_VMIDI
+	tristate "Loopback MIDI device support"
+	help
+	  Support for MIDI loopback on port 1 or 2.
+
+config SOUND_TRIX
+	tristate "MediaTrix AudioTrix Pro support"
+	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"
+	---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)"
+	---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_PAS
+	tristate "ProAudioSpectrum 16 support"
+	---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"
+	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 compatible)"
+	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"
+	---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 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_YM3812
+	tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
+	---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_UART6850
+	tristate "6850 UART support"
+	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)"
+	---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:sound/oss/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.
+
+config SOUND_VIDC
+	tristate "VIDC 16-bit sound"
+	depends on ARM && ARCH_ACORN
+	help
+	  16-bit support for the VIDC onboard sound hardware found on Acorn
+	  machines.
+
+config SOUND_WAVEARTIST
+	tristate "Netwinder WaveArtist"
+	depends on ARM && ARCH_NETWINDER
+	help
+	  Say Y here to include support for the Rockwell WaveArtist sound
+	  system.  This driver is mainly for the NetWinder.
+
+config SOUND_KAHLUA
+	tristate "XpressAudio Sound Blaster emulation"
+	depends on SOUND_SB
+
+endif	# SOUND_OSS
+
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
new file mode 100644
index 00000000..77f21b68
--- /dev/null
+++ b/sound/oss/Makefile
@@ -0,0 +1,108 @@
+# 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
+
+# Please leave it as is, cause the link order is significant !
+
+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_MSS)		+= ad1848.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_MPU401)	+= mpu401.o
+obj-$(CONFIG_SOUND_UART6850)	+= uart6850.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_MSNDCLAS)	+= msnd.o msnd_classic.o
+obj-$(CONFIG_SOUND_MSNDPIN)	+= msnd.o msnd_pinnacle.o
+obj-$(CONFIG_SOUND_VWSND)	+= vwsnd.o
+obj-$(CONFIG_SOUND_BCM_CS4297A)	+= swarm_cs4297a.o
+
+obj-$(CONFIG_DMASOUND)		+= dmasound/
+
+# Declare multi-part drivers.
+
+sound-objs	:= 							\
+    dev_table.o soundcard.o 		\
+    audio.o dmabuf.o					\
+    midi_synth.o midibuf.o					\
+    sequencer.o sound_timer.o sys_timer.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
+
+hostprogs-y	:= bin2hex hex2hex
+
+# Files generated that shall be removed upon make clean
+clean-files := 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 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:
+	$(Q)(							\
+	    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:
+	$(Q)(							\
+	    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 00000000..90fdcf06
--- /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/ad1848.c b/sound/oss/ad1848.c
new file mode 100644
index 00000000..98d23bdc
--- /dev/null
+++ b/sound/oss/ad1848.c
@@ -0,0 +1,3069 @@
+/*
+ * sound/oss/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/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/slab.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;
+} 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 bool deskpro_xl;
+static bool deskpro_m;
+static bool 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 bool 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 irqreturn_t adintr(int irq, void *dev_id);
+
+#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 oss_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);
+		oss_change_bits(devc, &val, &muteval, dev, channel, value);
+	}
+	else
+		oss_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 = 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++;
+
+	ad1848_init_hw(devc);
+
+	if (irq > 0)
+	{
+		devc->dev_no = my_dev;
+		if (request_irq(devc->irq, adintr, 0, devc->name,
+				(void *)(long)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)
+	{
+		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 *)(long)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);
+
+		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);
+}
+
+static irqreturn_t adintr(int irq, void *dev_id)
+{
+	unsigned char status;
+	ad1848_info *devc;
+	int dev;
+	int alt_stat = 0xff;
+	unsigned char c930_stat = 0;
+	int cnt = 0;
+
+	dev = (long)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 */
+
+EXPORT_SYMBOL(ad1848_detect);
+EXPORT_SYMBOL(ad1848_init);
+EXPORT_SYMBOL(ad1848_unload);
+EXPORT_SYMBOL(ad1848_control);
+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 __init *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 00000000..b95ebe28
--- /dev/null
+++ b/sound/oss/ad1848.h
@@ -0,0 +1,24 @@
+
+#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);
+
+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 00000000..2cf719b5
--- /dev/null
+++ b/sound/oss/ad1848_mixer.h
@@ -0,0 +1,253 @@
+/*
+ * sound/oss/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/aedsp16.c b/sound/oss/aedsp16.c
new file mode 100644
index 00000000..35b5912c
--- /dev/null
+++ b/sound/oss/aedsp16.c
@@ -0,0 +1,1373 @@
+/*
+   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/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include "sound_config.h"
+
+/*
+
+   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 a 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	10	/* Size of the card's version in chars	*/
+#define CARDVERDIGITS	2	/* Number of digits in the version	*/
+
+#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[CARDVERDIGITS] __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 < CARDVERDIGITS);
+	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/audio.c b/sound/oss/audio.c
new file mode 100644
index 00000000..4b958b1c
--- /dev/null
+++ b/sound/oss/audio.c
@@ -0,0 +1,985 @@
+/*
+ * sound/oss/audio.c
+ *
+ * Device file manager for /dev/audio
+ */
+
+/*
+ * 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)
+ * Thomas Sailer   : moved several static variables into struct audio_operations
+ *                   (which is grossly misnamed btw.) because they have the same
+ *                   lifetime as the rest in there and dynamic allocation saves
+ *                   12k or so
+ * Thomas Sailer   : use more logical O_NONBLOCK semantics
+ * Daniel Rodriksson: reworked the use of the device specific copy_user
+ *                    still generic
+ * Horst von Brand:  Add missing #include <linux/string.h>
+ * Chris Rankin    : Update the module-usage counter for the coprocessor,
+ *                   and decrement the counters again if we cannot open
+ *                   the audio device.
+ */
+
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/kmod.h>
+
+#include "sound_config.h"
+#include "ulaw.h"
+#include "coproc.h"
+
+#define NEUTRAL8	0x80
+#define NEUTRAL16	0x00
+
+
+static int             dma_ioctl(int dev, unsigned int cmd, void __user *arg);
+
+static int set_format(int dev, int fmt)
+{
+	if (fmt != AFMT_QUERY)
+	{
+		audio_devs[dev]->local_conversion = 0;
+
+		if (!(audio_devs[dev]->format_mask & fmt))	/* Not supported */
+		{
+			if (fmt == AFMT_MU_LAW)
+			{
+				fmt = AFMT_U8;
+				audio_devs[dev]->local_conversion = CNV_MU_LAW;
+			}
+			else
+				fmt = AFMT_U8;	/* This is always supported */
+		}
+		audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
+		audio_devs[dev]->local_format = fmt;
+	}
+	else
+		return audio_devs[dev]->local_format;
+
+	if (audio_devs[dev]->local_conversion)
+		return audio_devs[dev]->local_conversion;
+	else 
+		return audio_devs[dev]->local_format;
+}
+
+int audio_open(int dev, struct file *file)
+{
+	int ret;
+	int bits;
+	int dev_type = dev & 0x0f;
+	int mode = translate_mode(file);
+	const struct audio_driver *driver;
+	const struct coproc_operations *coprocessor;
+
+	dev = dev >> 4;
+
+	if (dev_type == SND_DEV_DSP16)
+		bits = 16;
+	else
+		bits = 8;
+
+	if (dev < 0 || dev >= num_audiodevs)
+		return -ENXIO;
+
+	driver = audio_devs[dev]->d;
+
+	if (!try_module_get(driver->owner))
+		return -ENODEV;
+
+	if ((ret = DMAbuf_open(dev, mode)) < 0)
+		goto error_1;
+
+	if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
+		if (!try_module_get(coprocessor->owner))
+			goto error_2;
+
+		if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) {
+			printk(KERN_WARNING "Sound: Can't access coprocessor device\n");
+			goto error_3;
+		}
+	}
+	
+	audio_devs[dev]->local_conversion = 0;
+
+	if (dev_type == SND_DEV_AUDIO)
+		set_format(dev, AFMT_MU_LAW);
+	else 
+		set_format(dev, bits);
+
+	audio_devs[dev]->audio_mode = AM_NONE;
+
+	return 0;
+
+	/*
+	 * Clean-up stack: this is what needs (un)doing if
+	 * we can't open the audio device ...
+	 */
+	error_3:
+	module_put(coprocessor->owner);
+
+	error_2:
+	DMAbuf_release(dev, mode);
+
+	error_1:
+	module_put(driver->owner);
+
+	return ret;
+}
+
+static void sync_output(int dev)
+{
+	int             p, i;
+	int             l;
+	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+	if (dmap->fragment_size <= 0)
+		return;
+	dmap->flags |= DMA_POST;
+
+	/* Align the write pointer with fragment boundaries */
+	
+	if ((l = dmap->user_counter % dmap->fragment_size) > 0)
+	{
+		int len;
+		unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
+
+		len = dmap->fragment_size - l;
+		memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
+		DMAbuf_move_wrpointer(dev, len);
+	}
+	
+	/*
+	 * Clean all unused buffer fragments.
+	 */
+
+	p = dmap->qtail;
+	dmap->flags |= DMA_POST;
+
+	for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
+	{
+		p = (p + 1) % dmap->nbufs;
+		if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
+			(dmap->raw_buf + dmap->buffsize))
+				printk(KERN_ERR "audio: Buffer error 2\n");
+
+		memset(dmap->raw_buf + p * dmap->fragment_size,
+			dmap->neutral_byte,
+			dmap->fragment_size);
+	}
+
+	dmap->flags |= DMA_DIRTY;
+}
+
+void audio_release(int dev, struct file *file)
+{
+	const struct coproc_operations *coprocessor;
+	int mode = translate_mode(file);
+
+	dev = dev >> 4;
+
+	/*
+	 * We do this in DMAbuf_release(). Why are we doing it
+	 * here? Why don't we test the file mode before setting
+	 * both flags? DMAbuf_release() does.
+	 * ...pester...pester...pester...
+	 */
+	audio_devs[dev]->dmap_out->closing = 1;
+	audio_devs[dev]->dmap_in->closing = 1;
+
+	/*
+	 * We need to make sure we allocated the dmap_out buffer
+	 * before we go mucking around with it in sync_output().
+	 */
+	if (mode & OPEN_WRITE)
+		sync_output(dev);
+
+	if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
+		coprocessor->close(coprocessor->devc, COPR_PCM);
+		module_put(coprocessor->owner);
+	}
+	DMAbuf_release(dev, mode);
+
+	module_put(audio_devs[dev]->d->owner);
+}
+
+static void translate_bytes(const unsigned char *table, unsigned char *buff, int n)
+{
+	unsigned long   i;
+
+	if (n <= 0)
+		return;
+
+	for (i = 0; i < n; ++i)
+		buff[i] = table[buff[i]];
+}
+
+int audio_write(int dev, struct file *file, const char __user *buf, int count)
+{
+	int c, p, l, buf_size, used, returned;
+	int err;
+	char *dma_buf;
+
+	dev = dev >> 4;
+
+	p = 0;
+	c = count;
+	
+	if(count < 0)
+		return -EINVAL;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+		return -EPERM;
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+		audio_devs[dev]->audio_mode |= AM_WRITE;
+	else
+		audio_devs[dev]->audio_mode = AM_WRITE;
+
+	if (!count)		/* Flush output */
+	{
+		  sync_output(dev);
+		  return 0;
+	}
+	
+	while (c)
+	{
+		if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0)
+		{
+			    /* Handle nonblocking mode */
+			if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN)
+				return p? p : -EAGAIN;	/* No more space. Return # of accepted bytes */
+			return err;
+		}
+		l = c;
+
+		if (l > buf_size)
+			l = buf_size;
+
+		returned = l;
+		used = l;
+		if (!audio_devs[dev]->d->copy_user)
+		{
+			if ((dma_buf + l) >
+				(audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
+			{
+				printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
+				return -EDOM;
+			}
+			if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
+			{
+				printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
+				return -EDOM;
+			}
+			if(copy_from_user(dma_buf, &(buf)[p], l))
+				return -EFAULT;
+		} 
+		else audio_devs[dev]->d->copy_user (dev,
+						dma_buf, 0,
+						buf, p,
+						c, buf_size,
+						&used, &returned,
+						l);
+		l = returned;
+
+		if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
+		{
+			translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l);
+		}
+		c -= used;
+		p += used;
+		DMAbuf_move_wrpointer(dev, l);
+
+	}
+
+	return count;
+}
+
+int audio_read(int dev, struct file *file, char __user *buf, int count)
+{
+	int             c, p, l;
+	char           *dmabuf;
+	int             buf_no;
+
+	dev = dev >> 4;
+	p = 0;
+	c = count;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_READ))
+		return -EPERM;
+
+	if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+		sync_output(dev);
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+		audio_devs[dev]->audio_mode |= AM_READ;
+	else
+		audio_devs[dev]->audio_mode = AM_READ;
+
+	while(c)
+	{
+		if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0)
+		{
+			/*
+			 *	Nonblocking mode handling. Return current # of bytes
+			 */
+
+			if (p > 0) 		/* Avoid throwing away data */
+				return p;	/* Return it instead */
+
+			if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN)
+				return -EAGAIN;
+
+			return buf_no;
+		}
+		if (l > c)
+			l = c;
+
+		/*
+		 * Insert any local processing here.
+		 */
+
+		if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
+		{
+			translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l);
+		}
+		
+		{
+			char           *fixit = dmabuf;
+
+			if(copy_to_user(&(buf)[p], fixit, l))
+				return -EFAULT;
+		};
+
+		DMAbuf_rmchars(dev, buf_no, l);
+
+		p += l;
+		c -= l;
+	}
+
+	return count - c;
+}
+
+int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
+{
+	int val, count;
+	unsigned long flags;
+	struct dma_buffparms *dmap;
+	int __user *p = arg;
+
+	dev = dev >> 4;
+
+	if (_IOC_TYPE(cmd) == 'C')	{
+		if (audio_devs[dev]->coproc)	/* Coprocessor ioctl */
+			return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
+		/* else
+		        printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
+		return -ENXIO;
+	}
+	else switch (cmd) 
+	{
+		case SNDCTL_DSP_SYNC:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return 0;
+			if (audio_devs[dev]->dmap_out->fragment_size == 0)
+				return 0;
+			sync_output(dev);
+			DMAbuf_sync(dev);
+			DMAbuf_reset(dev);
+			return 0;
+
+		case SNDCTL_DSP_POST:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return 0;
+			if (audio_devs[dev]->dmap_out->fragment_size == 0)
+				return 0;
+			audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
+			sync_output(dev);
+			dma_ioctl(dev, SNDCTL_DSP_POST, NULL);
+			return 0;
+
+		case SNDCTL_DSP_RESET:
+			audio_devs[dev]->audio_mode = AM_NONE;
+			DMAbuf_reset(dev);
+			return 0;
+
+		case SNDCTL_DSP_GETFMTS:
+			val = audio_devs[dev]->format_mask | AFMT_MU_LAW;
+			break;
+	
+		case SNDCTL_DSP_SETFMT:
+			if (get_user(val, p))
+				return -EFAULT;
+			val = set_format(dev, val);
+			break;
+
+		case SNDCTL_DSP_GETISPACE:
+			if (!(audio_devs[dev]->open_mode & OPEN_READ))
+				return 0;
+  			if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+  				return -EBUSY;
+			return dma_ioctl(dev, cmd, arg);
+
+		case SNDCTL_DSP_GETOSPACE:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EPERM;
+  			if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+  				return -EBUSY;
+			return dma_ioctl(dev, cmd, arg);
+		
+		case SNDCTL_DSP_NONBLOCK:
+			spin_lock(&file->f_lock);
+			file->f_flags |= O_NONBLOCK;
+			spin_unlock(&file->f_lock);
+			return 0;
+
+		case SNDCTL_DSP_GETCAPS:
+				val = 1 | DSP_CAP_MMAP;	/* Revision level of this ioctl() */
+				if (audio_devs[dev]->flags & DMA_DUPLEX &&
+					audio_devs[dev]->open_mode == OPEN_READWRITE)
+					val |= DSP_CAP_DUPLEX;
+				if (audio_devs[dev]->coproc)
+					val |= DSP_CAP_COPROC;
+				if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
+					val |= DSP_CAP_BATCH;
+				if (audio_devs[dev]->d->trigger)	/* Supports SETTRIGGER */
+					val |= DSP_CAP_TRIGGER;
+				break;
+			
+		case SOUND_PCM_WRITE_RATE:
+			if (get_user(val, p))
+				return -EFAULT;
+			val = audio_devs[dev]->d->set_speed(dev, val);
+			break;
+
+		case SOUND_PCM_READ_RATE:
+			val = audio_devs[dev]->d->set_speed(dev, 0);
+			break;
+			
+		case SNDCTL_DSP_STEREO:
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val > 1 || val < 0)
+				return -EINVAL;
+			val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
+			break;
+
+		case SOUND_PCM_WRITE_CHANNELS:
+			if (get_user(val, p))
+				return -EFAULT;
+			val = audio_devs[dev]->d->set_channels(dev, val);
+			break;
+
+		case SOUND_PCM_READ_CHANNELS:
+			val = audio_devs[dev]->d->set_channels(dev, 0);
+			break;
+		
+		case SOUND_PCM_READ_BITS:
+			val = audio_devs[dev]->d->set_bits(dev, 0);
+			break;
+
+		case SNDCTL_DSP_SETDUPLEX:
+			if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+				return -EPERM;
+			return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
+
+		case SNDCTL_DSP_PROFILE:
+			if (get_user(val, p))
+				return -EFAULT;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				audio_devs[dev]->dmap_out->applic_profile = val;
+			if (audio_devs[dev]->open_mode & OPEN_READ)
+				audio_devs[dev]->dmap_in->applic_profile = val;
+			return 0;
+		
+		case SNDCTL_DSP_GETODELAY:
+			dmap = audio_devs[dev]->dmap_out;
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+			if (!(dmap->flags & DMA_ALLOC_DONE))
+			{
+				val=0;
+				break;
+			}
+		
+			spin_lock_irqsave(&dmap->lock,flags);
+			/* Compute number of bytes that have been played */
+			count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
+			if (count < dmap->fragment_size && dmap->qhead != 0)
+				count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
+			count += dmap->byte_counter;
+		
+			/* Subtract current count from the number of bytes written by app */
+			count = dmap->user_counter - count;
+			if (count < 0)
+				count = 0;
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			val = count;
+			break;
+		
+		default:
+			return dma_ioctl(dev, cmd, arg);
+	}
+	return put_user(val, p);
+}
+
+void audio_init_devices(void)
+{
+	/*
+	 * NOTE! This routine could be called several times during boot.
+	 */
+}
+
+void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording)
+{
+	/*
+	 * This routine breaks the physical device buffers to logical ones.
+	 */
+
+	struct audio_operations *dsp_dev = audio_devs[dev];
+
+	unsigned i, n;
+	unsigned sr, nc, sz, bsz;
+
+	sr = dsp_dev->d->set_speed(dev, 0);
+	nc = dsp_dev->d->set_channels(dev, 0);
+	sz = dsp_dev->d->set_bits(dev, 0);
+
+	if (sz == 8)
+		dmap->neutral_byte = NEUTRAL8;
+	else
+		dmap->neutral_byte = NEUTRAL16;
+
+	if (sr < 1 || nc < 1 || sz < 1)
+	{
+/*		printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/
+		sr = DSP_DEFAULT_SPEED;
+		nc = 1;
+		sz = 8;
+	}
+	
+	sz = sr * nc * sz;
+
+	sz /= 8;		/* #bits -> #bytes */
+	dmap->data_rate = sz;
+
+	if (!dmap->needs_reorg)
+		return;
+	dmap->needs_reorg = 0;
+
+	if (dmap->fragment_size == 0)
+	{	
+		/* Compute the fragment size using the default algorithm */
+
+		/*
+		 * Compute a buffer size for time not exceeding 1 second.
+		 * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+		 * of sound (using the current speed, sample size and #channels).
+		 */
+
+		bsz = dmap->buffsize;
+		while (bsz > sz)
+			bsz /= 2;
+
+		if (bsz == dmap->buffsize)
+			bsz /= 2;	/* Needs at least 2 buffers */
+
+		/*
+		 *    Split the computed fragment to smaller parts. After 3.5a9
+		 *      the default subdivision is 4 which should give better
+		 *      results when recording.
+		 */
+
+		if (dmap->subdivision == 0)	/* Not already set */
+		{
+			dmap->subdivision = 4;	/* Init to the default value */
+
+			if ((bsz / dmap->subdivision) > 4096)
+				dmap->subdivision *= 2;
+			if ((bsz / dmap->subdivision) < 4096)
+				dmap->subdivision = 1;
+		}
+		bsz /= dmap->subdivision;
+
+		if (bsz < 16)
+			bsz = 16;	/* Just a sanity check */
+
+		dmap->fragment_size = bsz;
+	}
+	else
+	{
+		/*
+		 * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
+		 * the buffer size computation has already been done.
+		 */
+		if (dmap->fragment_size > (dmap->buffsize / 2))
+			dmap->fragment_size = (dmap->buffsize / 2);
+		bsz = dmap->fragment_size;
+	}
+
+	if (audio_devs[dev]->min_fragment)
+		if (bsz < (1 << audio_devs[dev]->min_fragment))
+			bsz = 1 << audio_devs[dev]->min_fragment;
+	if (audio_devs[dev]->max_fragment)
+		if (bsz > (1 << audio_devs[dev]->max_fragment))
+			bsz = 1 << audio_devs[dev]->max_fragment;
+	bsz &= ~0x07;		/* Force size which is multiple of 8 bytes */
+#ifdef OS_DMA_ALIGN_CHECK
+	OS_DMA_ALIGN_CHECK(bsz);
+#endif
+
+	n = dmap->buffsize / bsz;
+	if (n > MAX_SUB_BUFFERS)
+		n = MAX_SUB_BUFFERS;
+	if (n > dmap->max_fragments)
+		n = dmap->max_fragments;
+
+	if (n < 2)
+	{
+		n = 2;
+		bsz /= 2;
+	}
+	dmap->nbufs = n;
+	dmap->bytes_in_use = n * bsz;
+	dmap->fragment_size = bsz;
+	dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
+			dmap->bytes_in_use;	/* Approximately one hour */
+
+	if (dmap->raw_buf)
+	{
+		memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use);
+	}
+	
+	for (i = 0; i < dmap->nbufs; i++)
+	{
+		dmap->counts[i] = 0;
+	}
+
+	dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
+}
+
+static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
+{
+	if (fact == 0) 
+	{
+		fact = dmap->subdivision;
+		if (fact == 0)
+			fact = 1;
+		return fact;
+	}
+	if (dmap->subdivision != 0 || dmap->fragment_size)	/* Too late to change */
+		return -EINVAL;
+
+	if (fact > MAX_REALTIME_FACTOR)
+		return -EINVAL;
+
+	if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+		return -EINVAL;
+
+	dmap->subdivision = fact;
+	return fact;
+}
+
+static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
+{
+	int bytes, count;
+
+	if (fact == 0)
+		return -EIO;
+
+	if (dmap->subdivision != 0 ||
+	    dmap->fragment_size)	/* Too late to change */
+		return -EINVAL;
+
+	bytes = fact & 0xffff;
+	count = (fact >> 16) & 0x7fff;
+
+	if (count == 0)
+		count = MAX_SUB_BUFFERS;
+	else if (count < MAX_SUB_BUFFERS)
+		count++;
+
+	if (bytes < 4 || bytes > 17)	/* <16 || > 512k */
+		return -EINVAL;
+
+	if (count < 2)
+		return -EINVAL;
+
+	if (audio_devs[dev]->min_fragment > 0)
+		if (bytes < audio_devs[dev]->min_fragment)
+			bytes = audio_devs[dev]->min_fragment;
+
+	if (audio_devs[dev]->max_fragment > 0)
+		if (bytes > audio_devs[dev]->max_fragment)
+			bytes = audio_devs[dev]->max_fragment;
+
+#ifdef OS_DMA_MINBITS
+	if (bytes < OS_DMA_MINBITS)
+		bytes = OS_DMA_MINBITS;
+#endif
+
+	dmap->fragment_size = (1 << bytes);
+	dmap->max_fragments = count;
+
+	if (dmap->fragment_size > dmap->buffsize)
+		dmap->fragment_size = dmap->buffsize;
+
+	if (dmap->fragment_size == dmap->buffsize &&
+	    audio_devs[dev]->flags & DMA_AUTOMODE)
+		dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
+
+	dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
+	return bytes | ((count - 1) << 16);
+}
+
+static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
+	struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
+	struct dma_buffparms *dmap;
+	audio_buf_info info;
+	count_info cinfo;
+	int fact, ret, changed, bits, count, err;
+	unsigned long flags;
+
+	switch (cmd) 
+	{
+		case SNDCTL_DSP_SUBDIVIDE:
+			ret = 0;
+			if (get_user(fact, (int __user *)arg))
+				return -EFAULT;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				ret = dma_subdivide(dev, dmap_out, fact);
+			if (ret < 0)
+				return ret;
+			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+				(audio_devs[dev]->flags & DMA_DUPLEX &&
+					audio_devs[dev]->open_mode & OPEN_READ))
+				ret = dma_subdivide(dev, dmap_in, fact);
+			if (ret < 0)
+				return ret;
+			break;
+
+		case SNDCTL_DSP_GETISPACE:
+		case SNDCTL_DSP_GETOSPACE:
+			dmap = dmap_out;
+			if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
+				return -EINVAL;
+			if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+			if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+				dmap = dmap_in;
+			if (dmap->mapping_flags & DMA_MAP_MAPPED)
+				return -EINVAL;
+			if (!(dmap->flags & DMA_ALLOC_DONE))
+				reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+			info.fragstotal = dmap->nbufs;
+			if (cmd == SNDCTL_DSP_GETISPACE)
+				info.fragments = dmap->qlen;
+			else 
+			{
+				if (!DMAbuf_space_in_queue(dev))
+					info.fragments = 0;
+				else
+				{
+					info.fragments = DMAbuf_space_in_queue(dev);
+					if (audio_devs[dev]->d->local_qlen) 
+					{
+						int tmp = audio_devs[dev]->d->local_qlen(dev);
+						if (tmp && info.fragments)
+							tmp--;	/*
+								 * This buffer has been counted twice
+								 */
+						info.fragments -= tmp;
+					}
+				}
+			}
+			if (info.fragments < 0)
+				info.fragments = 0;
+			else if (info.fragments > dmap->nbufs)
+				info.fragments = dmap->nbufs;
+
+			info.fragsize = dmap->fragment_size;
+			info.bytes = info.fragments * dmap->fragment_size;
+
+			if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+				info.bytes -= dmap->counts[dmap->qhead];
+			else 
+			{
+				info.fragments = info.bytes / dmap->fragment_size;
+				info.bytes -= dmap->user_counter % dmap->fragment_size;
+			}
+			if (copy_to_user(arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_DSP_SETTRIGGER:
+			if (get_user(bits, (int __user *)arg))
+				return -EFAULT;
+			bits &= audio_devs[dev]->open_mode;
+			if (audio_devs[dev]->d->trigger == NULL)
+				return -EINVAL;
+			if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
+				(bits & PCM_ENABLE_OUTPUT))
+				return -EINVAL;
+
+			if (bits & PCM_ENABLE_INPUT)
+			{
+				spin_lock_irqsave(&dmap_in->lock,flags);
+				changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_INPUT;
+				if (changed && audio_devs[dev]->go) 
+				{
+					reorganize_buffers(dev, dmap_in, 1);
+					if ((err = audio_devs[dev]->d->prepare_for_input(dev,
+						     dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
+						spin_unlock_irqrestore(&dmap_in->lock,flags);
+						return err;
+					}
+					dmap_in->dma_mode = DMODE_INPUT;
+					audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
+					DMAbuf_activate_recording(dev, dmap_in);
+				} else
+					audio_devs[dev]->enable_bits &= ~PCM_ENABLE_INPUT;
+				spin_unlock_irqrestore(&dmap_in->lock,flags);
+			}
+			if (bits & PCM_ENABLE_OUTPUT)
+			{
+				spin_lock_irqsave(&dmap_out->lock,flags);
+				changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_OUTPUT;
+				if (changed &&
+				    (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+				    audio_devs[dev]->go) 
+				{
+					if (!(dmap_out->flags & DMA_ALLOC_DONE))
+						reorganize_buffers(dev, dmap_out, 0);
+					dmap_out->dma_mode = DMODE_OUTPUT;
+					audio_devs[dev]->enable_bits |= PCM_ENABLE_OUTPUT;
+					dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+					DMAbuf_launch_output(dev, dmap_out);
+				} else
+					audio_devs[dev]->enable_bits &= ~PCM_ENABLE_OUTPUT;
+				spin_unlock_irqrestore(&dmap_out->lock,flags);
+			}
+#if 0
+			if (changed && audio_devs[dev]->d->trigger)
+				audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
+#endif				
+			/* Falls through... */
+
+		case SNDCTL_DSP_GETTRIGGER:
+			ret = audio_devs[dev]->enable_bits;
+			break;
+
+		case SNDCTL_DSP_SETSYNCRO:
+			if (!audio_devs[dev]->d->trigger)
+				return -EINVAL;
+			audio_devs[dev]->d->trigger(dev, 0);
+			audio_devs[dev]->go = 0;
+			return 0;
+
+		case SNDCTL_DSP_GETIPTR:
+			if (!(audio_devs[dev]->open_mode & OPEN_READ))
+				return -EINVAL;
+			spin_lock_irqsave(&dmap_in->lock,flags);
+			cinfo.bytes = dmap_in->byte_counter;
+			cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
+			if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
+				cinfo.bytes += dmap_in->bytes_in_use;	/* Pointer wrap not handled yet */
+			cinfo.blocks = dmap_in->qlen;
+			cinfo.bytes += cinfo.ptr;
+			if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
+				dmap_in->qlen = 0;	/* Reset interrupt counter */
+			spin_unlock_irqrestore(&dmap_in->lock,flags);
+			if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_DSP_GETOPTR:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+
+			spin_lock_irqsave(&dmap_out->lock,flags);
+			cinfo.bytes = dmap_out->byte_counter;
+			cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
+			if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
+				cinfo.bytes += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+			cinfo.blocks = dmap_out->qlen;
+			cinfo.bytes += cinfo.ptr;
+			if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
+				dmap_out->qlen = 0;	/* Reset interrupt counter */
+			spin_unlock_irqrestore(&dmap_out->lock,flags);
+			if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_DSP_GETODELAY:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+			if (!(dmap_out->flags & DMA_ALLOC_DONE))
+			{
+				ret=0;
+				break;
+			}
+			spin_lock_irqsave(&dmap_out->lock,flags);
+			/* Compute number of bytes that have been played */
+			count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
+			if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
+				count += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+			count += dmap_out->byte_counter;
+			/* Subtract current count from the number of bytes written by app */
+			count = dmap_out->user_counter - count;
+			if (count < 0)
+				count = 0;
+			spin_unlock_irqrestore(&dmap_out->lock,flags);
+			ret = count;
+			break;
+
+		case SNDCTL_DSP_POST:
+			if (audio_devs[dev]->dmap_out->qlen > 0)
+				if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+					DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
+			return 0;
+
+		case SNDCTL_DSP_GETBLKSIZE:
+			dmap = dmap_out;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
+			if (audio_devs[dev]->open_mode == OPEN_READ ||
+			    (audio_devs[dev]->flags & DMA_DUPLEX &&
+			     audio_devs[dev]->open_mode & OPEN_READ))
+				reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
+			if (audio_devs[dev]->open_mode == OPEN_READ)
+				dmap = dmap_in;
+			ret = dmap->fragment_size;
+			break;
+
+		case SNDCTL_DSP_SETFRAGMENT:
+			ret = 0;
+			if (get_user(fact, (int __user *)arg))
+				return -EFAULT;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				ret = dma_set_fragment(dev, dmap_out, fact);
+			if (ret < 0)
+				return ret;
+			if (audio_devs[dev]->open_mode == OPEN_READ ||
+			    (audio_devs[dev]->flags & DMA_DUPLEX &&
+			     audio_devs[dev]->open_mode & OPEN_READ))
+				ret = dma_set_fragment(dev, dmap_in, fact);
+			if (ret < 0)
+				return ret;
+			if (!arg) /* don't know what this is good for, but preserve old semantics */
+				return 0;
+			break;
+
+		default:
+			if (!audio_devs[dev]->d->ioctl)
+				return -EINVAL;
+			return audio_devs[dev]->d->ioctl(dev, cmd, arg);
+	}
+	return put_user(ret, (int __user *)arg);
+}
diff --git a/sound/oss/bin2hex.c b/sound/oss/bin2hex.c
new file mode 100644
index 00000000..b59109eb
--- /dev/null
+++ b/sound/oss/bin2hex.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int main( int argc, const char * argv [] )
+{
+    const char * varname;
+    int i = 0;
+    int c;
+    int id = 0;
+
+    if(argv[1] && strcmp(argv[1],"-i")==0)
+    {
+    	argv++;
+    	argc--;
+    	id=1;
+    }
+    	
+    if(argc==1)
+    {
+    	fprintf(stderr, "bin2hex: [-i] firmware\n");
+    	exit(1);
+    }
+    
+    varname = argv[1];
+    printf( "/* automatically generated by bin2hex */\n" );
+    printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":"");
+
+    while ( ( c = getchar( ) ) != EOF )
+    {
+	if ( i != 0 && i % 10 == 0 )
+	    printf( "\n" );
+	printf( "0x%02lx,", c & 0xFFl );
+	i++;
+    }
+
+    printf( "};\nstatic int %sLen =  %d;\n", varname, i );
+    return 0;
+}
diff --git a/sound/oss/coproc.h b/sound/oss/coproc.h
new file mode 100644
index 00000000..7bec21bb
--- /dev/null
+++ b/sound/oss/coproc.h
@@ -0,0 +1,12 @@
+/*
+ * Definitions for various on board processors on the sound cards. For
+ * example DSP processors.
+ */
+
+/*
+ * Coprocessor access types
+ */
+#define COPR_CUSTOM		0x0001	/* Custom applications */
+#define COPR_MIDI		0x0002	/* MIDI (MPU-401) emulation */
+#define COPR_PCM		0x0004	/* Digitized voice applications */
+#define COPR_SYNTH		0x0008	/* Music synthesis */
diff --git a/sound/oss/dev_table.c b/sound/oss/dev_table.c
new file mode 100644
index 00000000..d8cf3e58
--- /dev/null
+++ b/sound/oss/dev_table.c
@@ -0,0 +1,256 @@
+/*
+ * sound/oss/dev_table.c
+ *
+ * Device call tables.
+ *
+ *
+ * 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/init.h>
+
+#include "sound_config.h"
+
+struct audio_operations *audio_devs[MAX_AUDIO_DEV];
+EXPORT_SYMBOL(audio_devs);
+
+int num_audiodevs;
+EXPORT_SYMBOL(num_audiodevs);
+
+struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
+EXPORT_SYMBOL(mixer_devs);
+
+int num_mixers;
+EXPORT_SYMBOL(num_mixers);
+
+struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
+EXPORT_SYMBOL(synth_devs);
+
+int num_synths;
+
+struct midi_operations *midi_devs[MAX_MIDI_DEV];
+EXPORT_SYMBOL(midi_devs);
+
+int num_midis;
+EXPORT_SYMBOL(num_midis);
+
+struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
+	&default_sound_timer, NULL
+};
+EXPORT_SYMBOL(sound_timer_devs);
+
+int num_sound_timers = 1;
+
+
+static int sound_alloc_audiodev(void);
+
+int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
+			int driver_size, int flags, unsigned int format_mask,
+			void *devc, int dma1, int dma2)
+{
+	struct audio_driver *d;
+	struct audio_operations *op;
+	int num;
+
+	if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) {
+		printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
+		return -(EINVAL);
+	}
+	num = sound_alloc_audiodev();
+
+	if (num == -1) {
+		printk(KERN_ERR "sound: Too many audio drivers\n");
+		return -(EBUSY);
+	}
+	d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
+	sound_nblocks++;
+	if (sound_nblocks >= MAX_MEM_BLOCKS)
+		sound_nblocks = MAX_MEM_BLOCKS - 1;
+
+	op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct audio_operations)));
+	sound_nblocks++;
+	if (sound_nblocks >= MAX_MEM_BLOCKS)
+		sound_nblocks = MAX_MEM_BLOCKS - 1;
+
+	if (d == NULL || op == NULL) {
+		printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
+		sound_unload_audiodev(num);
+		return -(ENOMEM);
+	}
+	init_waitqueue_head(&op->in_sleeper);
+	init_waitqueue_head(&op->out_sleeper);	
+	init_waitqueue_head(&op->poll_sleeper);
+	if (driver_size < sizeof(struct audio_driver))
+		memset((char *) d, 0, sizeof(struct audio_driver));
+
+	memcpy((char *) d, (char *) driver, driver_size);
+
+	op->d = d;
+	strlcpy(op->name, name, sizeof(op->name));
+	op->flags = flags;
+	op->format_mask = format_mask;
+	op->devc = devc;
+
+	/*
+	 *    Hardcoded defaults
+	 */
+	audio_devs[num] = op;
+
+	DMAbuf_init(num, dma1, dma2);
+
+	audio_init_devices();
+	return num;
+}
+EXPORT_SYMBOL(sound_install_audiodrv);
+
+int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
+	int driver_size, void *devc)
+{
+	struct mixer_operations *op;
+
+	int n = sound_alloc_mixerdev();
+
+	if (n == -1) {
+		printk(KERN_ERR "Sound: Too many mixer drivers\n");
+		return -EBUSY;
+	}
+	if (vers != MIXER_DRIVER_VERSION ||
+		driver_size > sizeof(struct mixer_operations)) {
+		printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name);
+		return -EINVAL;
+	}
+	
+	/* FIXME: This leaks a mixer_operations struct every time its called
+	   until you unload sound! */
+	   
+	op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct mixer_operations)));
+	sound_nblocks++;
+	if (sound_nblocks >= MAX_MEM_BLOCKS)
+		sound_nblocks = MAX_MEM_BLOCKS - 1;
+
+	if (op == NULL) {
+		printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name);
+		return -ENOMEM;
+	}
+	memcpy((char *) op, (char *) driver, driver_size);
+
+	strlcpy(op->name, name, sizeof(op->name));
+	op->devc = devc;
+
+	mixer_devs[n] = op;
+	return n;
+}
+EXPORT_SYMBOL(sound_install_mixer);
+
+void sound_unload_audiodev(int dev)
+{
+	if (dev != -1) {
+		DMAbuf_deinit(dev);
+		audio_devs[dev] = NULL;
+		unregister_sound_dsp((dev<<4)+3);
+	}
+}
+EXPORT_SYMBOL(sound_unload_audiodev);
+
+static int sound_alloc_audiodev(void)
+{ 
+	int i = register_sound_dsp(&oss_sound_fops, -1);
+	if(i==-1)
+		return i;
+	i>>=4;
+	if(i>=num_audiodevs)
+		num_audiodevs = i + 1;
+	return i;
+}
+
+int sound_alloc_mididev(void)
+{
+	int i = register_sound_midi(&oss_sound_fops, -1);
+	if(i==-1)
+		return i;
+	i>>=4;
+	if(i>=num_midis)
+		num_midis = i + 1;
+	return i;
+}
+EXPORT_SYMBOL(sound_alloc_mididev);
+
+int sound_alloc_synthdev(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_SYNTH_DEV; i++) {
+		if (synth_devs[i] == NULL) {
+			if (i >= num_synths)
+				num_synths++;
+			return i;
+		}
+	}
+	return -1;
+}
+EXPORT_SYMBOL(sound_alloc_synthdev);
+
+int sound_alloc_mixerdev(void)
+{
+	int i = register_sound_mixer(&oss_sound_fops, -1);
+	if(i==-1)
+		return -1;
+	i>>=4;
+	if(i>=num_mixers)
+		num_mixers = i + 1;
+	return i;
+}
+EXPORT_SYMBOL(sound_alloc_mixerdev);
+
+int sound_alloc_timerdev(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_TIMER_DEV; i++) {
+		if (sound_timer_devs[i] == NULL) {
+			if (i >= num_sound_timers)
+				num_sound_timers++;
+			return i;
+		}
+	}
+	return -1;
+}
+EXPORT_SYMBOL(sound_alloc_timerdev);
+
+void sound_unload_mixerdev(int dev)
+{
+	if (dev != -1) {
+		mixer_devs[dev] = NULL;
+		unregister_sound_mixer(dev<<4);
+		num_mixers--;
+	}
+}
+EXPORT_SYMBOL(sound_unload_mixerdev);
+
+void sound_unload_mididev(int dev)
+{
+	if (dev != -1) {
+		midi_devs[dev] = NULL;
+		unregister_sound_midi((dev<<4)+2);
+	}
+}
+EXPORT_SYMBOL(sound_unload_mididev);
+
+void sound_unload_synthdev(int dev)
+{
+	if (dev != -1)
+		synth_devs[dev] = NULL;
+}
+EXPORT_SYMBOL(sound_unload_synthdev);
+
+void sound_unload_timerdev(int dev)
+{
+	if (dev != -1)
+		sound_timer_devs[dev] = NULL;
+}
+EXPORT_SYMBOL(sound_unload_timerdev);
+
diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h
new file mode 100644
index 00000000..0199a317
--- /dev/null
+++ b/sound/oss/dev_table.h
@@ -0,0 +1,390 @@
+/*
+ *	dev_table.h
+ *
+ *	Global definitions for device call tables
+ *
+ *
+ * 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.
+ */
+
+
+#ifndef _DEV_TABLE_H_
+#define _DEV_TABLE_H_
+
+#include <linux/spinlock.h>
+/*
+ * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
+ * Numbers 1000 to N are reserved for driver's internal use.
+ */
+
+#define SNDCARD_DESKPROXL		27	/* Compaq Deskpro XL */
+#define SNDCARD_VIDC			28	/* ARMs VIDC */
+#define SNDCARD_SBPNP			29
+#define SNDCARD_SOFTOSS			36
+#define SNDCARD_VMIDI			37
+#define SNDCARD_OPL3SA1			38	/* Note: clash in msnd.h */
+#define SNDCARD_OPL3SA1_SB		39
+#define SNDCARD_OPL3SA1_MPU		40
+#define SNDCARD_WAVEFRONT               41
+#define SNDCARD_OPL3SA2                 42
+#define SNDCARD_OPL3SA2_MPU             43
+#define SNDCARD_WAVEARTIST              44	/* Waveartist */
+#define SNDCARD_OPL3SA2_MSS             45	/* Originally missed */
+#define SNDCARD_AD1816                  88
+
+/*
+ *	NOTE! 	NOTE!	NOTE!	NOTE!
+ *
+ *	If you modify this file, please check the dev_table.c also.
+ *
+ *	NOTE! 	NOTE!	NOTE!	NOTE!
+ */
+
+struct driver_info 
+{
+	char *driver_id;
+	int card_subtype;	/* Driver specific. Usually 0 */
+	int card_type;		/*	From soundcard.h	*/
+	char *name;
+	void (*attach) (struct address_info *hw_config);
+	int (*probe) (struct address_info *hw_config);
+	void (*unload) (struct address_info *hw_config);
+};
+
+struct card_info 
+{
+	int card_type;	/* Link (search key) to the driver list */
+	struct address_info config;
+	int enabled;
+	void *for_driver_use;
+};
+
+
+/*
+ * Device specific parameters (used only by dmabuf.c)
+ */
+#define MAX_SUB_BUFFERS		(32*MAX_REALTIME_FACTOR)
+
+#define DMODE_NONE		0
+#define DMODE_OUTPUT		PCM_ENABLE_OUTPUT
+#define DMODE_INPUT		PCM_ENABLE_INPUT
+
+struct dma_buffparms 
+{
+	int      dma_mode;	/* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
+	int	 closing;
+
+	/*
+ 	 * Pointers to raw buffers
+ 	 */
+
+  	char     *raw_buf;
+    	unsigned long   raw_buf_phys;
+	int buffsize;
+
+     	/*
+         * Device state tables
+         */
+
+	unsigned long flags;
+#define DMA_BUSY	0x00000001
+#define DMA_RESTART	0x00000002
+#define DMA_ACTIVE	0x00000004
+#define DMA_STARTED	0x00000008
+#define DMA_EMPTY	0x00000010	
+#define DMA_ALLOC_DONE	0x00000020
+#define DMA_SYNCING	0x00000040
+#define DMA_DIRTY	0x00000080
+#define DMA_POST	0x00000100
+#define DMA_NODMA	0x00000200
+#define DMA_NOTIMEOUT	0x00000400
+
+	int      open_mode;
+
+	/*
+	 * Queue parameters.
+	 */
+	int      qlen;
+	int      qhead;
+	int      qtail;
+	spinlock_t lock;
+		
+	int	 cfrag;	/* Current incomplete fragment (write) */
+
+	int      nbufs;
+	int      counts[MAX_SUB_BUFFERS];
+	int      subdivision;
+
+	int      fragment_size;
+        int	 needs_reorg;
+	int	 max_fragments;
+
+	int	 bytes_in_use;
+
+	int	 underrun_count;
+	unsigned long	 byte_counter;
+	unsigned long	 user_counter;
+	unsigned long	 max_byte_counter;
+	int	 data_rate; /* Bytes/second */
+
+	int	 mapping_flags;
+#define			DMA_MAP_MAPPED		0x00000001
+	char	neutral_byte;
+	int	dma;		/* DMA channel */
+
+	int     applic_profile;	/* Application profile (APF_*) */
+	/* Interrupt callback stuff */
+	void (*audio_callback) (int dev, int parm);
+	int callback_parm;
+
+	int	 buf_flags[MAX_SUB_BUFFERS];
+#define		 BUFF_EOF		0x00000001 /* Increment eof count */
+#define		 BUFF_DIRTY		0x00000002 /* Buffer written */
+};
+
+/*
+ * Structure for use with various microcontrollers and DSP processors 
+ * in the recent sound cards.
+ */
+typedef struct coproc_operations 
+{
+	char name[64];
+	struct module *owner;
+	int (*open) (void *devc, int sub_device);
+	void (*close) (void *devc, int sub_device);
+	int (*ioctl) (void *devc, unsigned int cmd, void __user * arg, int local);
+	void (*reset) (void *devc);
+
+	void *devc;		/* Driver specific info */
+} coproc_operations;
+
+struct audio_driver 
+{
+	struct module *owner;
+	int (*open) (int dev, int mode);
+	void (*close) (int dev);
+	void (*output_block) (int dev, unsigned long buf, 
+			      int count, int intrflag);
+	void (*start_input) (int dev, unsigned long buf, 
+			     int count, int intrflag);
+	int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
+	int (*prepare_for_input) (int dev, int bufsize, int nbufs);
+	int (*prepare_for_output) (int dev, int bufsize, int nbufs);
+	void (*halt_io) (int dev);
+	int (*local_qlen)(int dev);
+	void (*copy_user) (int dev,
+			char *localbuf, int localoffs,
+                        const char __user *userbuf, int useroffs,
+                        int max_in, int max_out,
+                        int *used, int *returned,
+                        int len);
+	void (*halt_input) (int dev);
+	void (*halt_output) (int dev);
+	void (*trigger) (int dev, int bits);
+	int (*set_speed)(int dev, int speed);
+	unsigned int (*set_bits)(int dev, unsigned int bits);
+	short (*set_channels)(int dev, short channels);
+	void (*postprocess_write)(int dev); 	/* Device spesific postprocessing for written data */
+	void (*preprocess_read)(int dev); 	/* Device spesific preprocessing for read data */
+	void (*mmap)(int dev);
+};
+
+struct audio_operations 
+{
+        char name[128];
+	int flags;
+#define NOTHING_SPECIAL 	0x00
+#define NEEDS_RESTART		0x01
+#define DMA_AUTOMODE		0x02
+#define DMA_DUPLEX		0x04
+#define DMA_PSEUDO_AUTOMODE	0x08
+#define DMA_HARDSTOP		0x10
+#define DMA_EXACT		0x40
+#define DMA_NORESET		0x80
+	int  format_mask;	/* Bitmask for supported audio formats */
+	void *devc;		/* Driver specific info */
+	struct audio_driver *d;
+	void *portc;		/* Driver specific info */
+	struct dma_buffparms *dmap_in, *dmap_out;
+	struct coproc_operations *coproc;
+	int mixer_dev;
+	int enable_bits;
+ 	int open_mode;
+	int go;
+	int min_fragment;	/* 0 == unlimited */
+	int max_fragment;	/* 0 == unlimited */
+	int parent_dev;		/* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
+
+	/* fields formerly in dmabuf.c */
+	wait_queue_head_t in_sleeper;
+	wait_queue_head_t out_sleeper;
+	wait_queue_head_t poll_sleeper;
+
+	/* fields formerly in audio.c */
+	int audio_mode;
+
+#define		AM_NONE		0
+#define		AM_WRITE	OPEN_WRITE
+#define 	AM_READ		OPEN_READ
+
+	int local_format;
+	int audio_format;
+	int local_conversion;
+#define CNV_MU_LAW	0x00000001
+
+	/* large structures at the end to keep offsets small */
+	struct dma_buffparms dmaps[2];
+};
+
+int *load_mixer_volumes(char *name, int *levels, int present);
+
+struct mixer_operations 
+{
+	struct module *owner;
+	char id[16];
+	char name[64];
+	int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
+	
+	void *devc;
+	int modify_counter;
+};
+
+struct synth_operations 
+{
+	struct module *owner;
+	char *id;	/* Unique identifier (ASCII) max 29 char */
+	struct synth_info *info;
+	int midi_dev;
+	int synth_type;
+	int synth_subtype;
+
+	int (*open) (int dev, int mode);
+	void (*close) (int dev);
+	int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
+	int (*kill_note) (int dev, int voice, int note, int velocity);
+	int (*start_note) (int dev, int voice, int note, int velocity);
+	int (*set_instr) (int dev, int voice, int instr);
+	void (*reset) (int dev);
+	void (*hw_control) (int dev, unsigned char *event);
+	int (*load_patch) (int dev, int format, const char __user *addr,
+	     int count, int pmgr_flag);
+	void (*aftertouch) (int dev, int voice, int pressure);
+	void (*controller) (int dev, int voice, int ctrl_num, int value);
+	void (*panning) (int dev, int voice, int value);
+	void (*volume_method) (int dev, int mode);
+	void (*bender) (int dev, int chn, int value);
+	int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
+	void (*setup_voice) (int dev, int voice, int chn);
+	int (*send_sysex)(int dev, unsigned char *bytes, int len);
+
+ 	struct voice_alloc_info alloc;
+ 	struct channel_info chn_info[16];
+	int emulation;
+#define	EMU_GM			1	/* General MIDI */
+#define	EMU_XG			2	/* Yamaha XG */
+#define MAX_SYSEX_BUF	64
+	unsigned char sysex_buf[MAX_SYSEX_BUF];
+	int sysex_ptr;
+};
+
+struct midi_input_info 
+{
+	/* MIDI input scanner variables */
+#define MI_MAX	10
+	volatile int             m_busy;
+    	unsigned char   m_buf[MI_MAX];
+	unsigned char	m_prev_status;	/* For running status */
+    	int             m_ptr;
+#define MST_INIT			0
+#define MST_DATA			1
+#define MST_SYSEX			2
+    	int             m_state;
+    	int             m_left;
+};
+
+struct midi_operations 
+{
+	struct module *owner;
+	struct midi_info info;
+	struct synth_operations *converter;
+	struct midi_input_info in_info;
+	int (*open) (int dev, int mode,
+		void (*inputintr)(int dev, unsigned char data),
+		void (*outputintr)(int dev)
+		);
+	void (*close) (int dev);
+	int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
+	int (*outputc) (int dev, unsigned char data);
+	int (*start_read) (int dev);
+	int (*end_read) (int dev);
+	void (*kick)(int dev);
+	int (*command) (int dev, unsigned char *data);
+	int (*buffer_status) (int dev);
+	int (*prefix_cmd) (int dev, unsigned char status);
+	struct coproc_operations *coproc;
+	void *devc;
+};
+
+struct sound_lowlev_timer 
+{
+	int dev;
+	int priority;
+	unsigned int (*tmr_start)(int dev, unsigned int usecs);
+	void (*tmr_disable)(int dev);
+	void (*tmr_restart)(int dev);
+};
+
+struct sound_timer_operations 
+{
+	struct module *owner;
+	struct sound_timer_info info;
+	int priority;
+	int devlink;
+	int (*open)(int dev, int mode);
+	void (*close)(int dev);
+	int (*event)(int dev, unsigned char *ev);
+	unsigned long (*get_time)(int dev);
+	int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
+	void (*arm_timer)(int dev, long time);
+};
+
+extern struct sound_timer_operations default_sound_timer;
+
+extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
+extern int num_audiodevs;
+extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
+extern int num_mixers;
+extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
+extern int num_synths;
+extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
+extern int num_midis;
+extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
+extern int num_sound_timers;
+
+extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
+void sound_timer_init (struct sound_lowlev_timer *t, char *name);
+void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan);
+
+#define AUDIO_DRIVER_VERSION	2
+#define MIXER_DRIVER_VERSION	2
+int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
+			int driver_size, int flags, unsigned int format_mask,
+			void *devc, int dma1, int dma2);
+int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
+			int driver_size, void *devc);
+
+void sound_unload_audiodev(int dev);
+void sound_unload_mixerdev(int dev);
+void sound_unload_mididev(int dev);
+void sound_unload_synthdev(int dev);
+void sound_unload_timerdev(int dev);
+int sound_alloc_mixerdev(void);
+int sound_alloc_timerdev(void);
+int sound_alloc_synthdev(void);
+int sound_alloc_mididev(void);
+#endif	/* _DEV_TABLE_H_ */
+
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
new file mode 100644
index 00000000..bcc3e8e0
--- /dev/null
+++ b/sound/oss/dmabuf.c
@@ -0,0 +1,1268 @@
+/*
+ * sound/oss/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ */
+/*
+ * 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   : moved several static variables into struct audio_operations
+ *                   (which is grossly misnamed btw.) because they have the same
+ *                   lifetime as the rest in there and dynamic allocation saves
+ *                   12k or so
+ * Thomas Sailer   : remove {in,out}_sleep_flag. It was used for the sleeper to
+ *                   determine if it was woken up by the expiring timeout or by
+ *                   an explicit wake_up. The return value from schedule_timeout
+ *		     can be used instead; if 0, the wakeup was due to the timeout.
+ *
+ * Rob Riggs		Added persistent DMA buffers (1998/10/17)
+ */
+
+#define BE_CONSERVATIVE
+#define SAMPLE_ROUNDUP 0
+
+#include <linux/mm.h>
+#include <linux/gfp.h>
+#include "sound_config.h"
+
+#define DMAP_FREE_ON_CLOSE      0
+#define DMAP_KEEP_ON_CLOSE      1
+extern int sound_dmap_flag;
+
+static void dma_reset_output(int dev);
+static void dma_reset_input(int dev);
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode);
+
+
+
+static int debugmem;    	/* switched off by default */
+static int dma_buffsize = DSP_BUFFSIZE;
+
+static long dmabuf_timeout(struct dma_buffparms *dmap)
+{
+	long tmout;
+
+	tmout = (dmap->fragment_size * HZ) / dmap->data_rate;
+	tmout += HZ / 5;	/* Some safety distance */
+	if (tmout < (HZ / 2))
+		tmout = HZ / 2;
+	if (tmout > 20 * HZ)
+		tmout = 20 * HZ;
+	return tmout;
+}
+
+static int sound_alloc_dmap(struct dma_buffparms *dmap)
+{
+	char *start_addr, *end_addr;
+	int dma_pagesize;
+	int sz, size;
+	struct page *page;
+
+	dmap->mapping_flags &= ~DMA_MAP_MAPPED;
+
+	if (dmap->raw_buf != NULL)
+		return 0;	/* Already done */
+	if (dma_buffsize < 4096)
+		dma_buffsize = 4096;
+	dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+	
+	/*
+	 *	Now check for the Cyrix problem.
+	 */
+	 
+	if(isa_dma_bridge_buggy==2)
+		dma_pagesize=32768;
+	 
+	dmap->raw_buf = NULL;
+	dmap->buffsize = dma_buffsize;
+	if (dmap->buffsize > dma_pagesize)
+		dmap->buffsize = dma_pagesize;
+	start_addr = NULL;
+	/*
+	 * Now loop until we get a free buffer. Try to get smaller buffer if
+	 * it fails. Don't accept smaller than 8k buffer for performance
+	 * reasons.
+	 */
+	while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) {
+		for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+		dmap->buffsize = PAGE_SIZE * (1 << sz);
+		start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA|__GFP_NOWARN, sz);
+		if (start_addr == NULL)
+			dmap->buffsize /= 2;
+	}
+
+	if (start_addr == NULL) {
+		printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
+		return -ENOMEM;
+	} else {
+		/* make some checks */
+		end_addr = start_addr + dmap->buffsize - 1;
+
+		if (debugmem)
+			printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
+		
+		/* now check if it fits into the same dma-pagesize */
+
+		if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
+		    || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
+			printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
+			return -EFAULT;
+		}
+	}
+	dmap->raw_buf = start_addr;
+	dmap->raw_buf_phys = virt_to_bus(start_addr);
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		SetPageReserved(page);
+	return 0;
+}
+
+static void sound_free_dmap(struct dma_buffparms *dmap)
+{
+	int sz, size;
+	struct page *page;
+	unsigned long start_addr, end_addr;
+
+	if (dmap->raw_buf == NULL)
+		return;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+		return;		/* Don't free mmapped buffer. Will use it next time */
+	for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+
+	start_addr = (unsigned long) dmap->raw_buf;
+	end_addr = start_addr + dmap->buffsize;
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		ClearPageReserved(page);
+
+	free_pages((unsigned long) dmap->raw_buf, sz);
+	dmap->raw_buf = NULL;
+}
+
+
+/* Intel version !!!!!!!!! */
+
+static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode)
+{
+	unsigned long flags;
+	int chan = dmap->dma;
+
+	/* printk( "Start DMA%d %d, %d\n",  chan,  (int)(physaddr-dmap->raw_buf_phys),  count); */
+
+	flags = claim_dma_lock();
+	disable_dma(chan);
+	clear_dma_ff(chan);
+	set_dma_mode(chan, dma_mode);
+	set_dma_addr(chan, physaddr);
+	set_dma_count(chan, count);
+	enable_dma(chan);
+	release_dma_lock(flags);
+
+	return 0;
+}
+
+static void dma_init_buffers(struct dma_buffparms *dmap)
+{
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	dmap->byte_counter = 0;
+	dmap->max_byte_counter = 8000 * 60 * 60;
+	dmap->bytes_in_use = dmap->buffsize;
+
+	dmap->dma_mode = DMODE_NONE;
+	dmap->mapping_flags = 0;
+	dmap->neutral_byte = 0x80;
+	dmap->data_rate = 8000;
+	dmap->cfrag = -1;
+	dmap->closing = 0;
+	dmap->nbufs = 1;
+	dmap->flags = DMA_BUSY;	/* Other flags off */
+}
+
+static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap)
+{
+	int err;
+	
+	if (dmap->flags & DMA_BUSY)
+		return -EBUSY;
+	if ((err = sound_alloc_dmap(dmap)) < 0)
+		return err;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_WARNING "Sound: DMA buffers not available\n");
+		return -ENOSPC;	/* Memory allocation failed during boot */
+	}
+	if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) {
+		printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma);
+		return -EBUSY;
+	}
+	dma_init_buffers(dmap);
+	spin_lock_init(&dmap->lock);
+	dmap->open_mode = mode;
+	dmap->subdivision = dmap->underrun_count = 0;
+	dmap->fragment_size = 0;
+	dmap->max_fragments = 65536;	/* Just a large value */
+	dmap->byte_counter = 0;
+	dmap->max_byte_counter = 8000 * 60 * 60;
+	dmap->applic_profile = APF_NORMAL;
+	dmap->needs_reorg = 1;
+	dmap->audio_callback = NULL;
+	dmap->callback_parm = 0;
+	return 0;
+}
+
+static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap)
+{
+	unsigned long flags;
+	
+	if (dmap->dma >= 0) {
+		sound_close_dma(dmap->dma);
+		flags=claim_dma_lock();
+		disable_dma(dmap->dma);
+		release_dma_lock(flags);
+	}
+	if (dmap->flags & DMA_BUSY)
+		dmap->dma_mode = DMODE_NONE;
+	dmap->flags &= ~DMA_BUSY;
+	
+	if (sound_dmap_flag == DMAP_FREE_ON_CLOSE)
+		sound_free_dmap(dmap);
+}
+
+
+static unsigned int default_set_bits(int dev, unsigned int bits)
+{
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (void __user *)&bits);
+	set_fs(fs);
+	return bits;
+}
+
+static int default_set_speed(int dev, int speed)
+{
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (void __user *)&speed);
+	set_fs(fs);
+	return speed;
+}
+
+static short default_set_channels(int dev, short channels)
+{
+	int c = channels;
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (void __user *)&c);
+	set_fs(fs);
+	return c;
+}
+
+static void check_driver(struct audio_driver *d)
+{
+	if (d->set_speed == NULL)
+		d->set_speed = default_set_speed;
+	if (d->set_bits == NULL)
+		d->set_bits = default_set_bits;
+	if (d->set_channels == NULL)
+		d->set_channels = default_set_channels;
+}
+
+int DMAbuf_open(int dev, int mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int retval;
+	struct dma_buffparms *dmap_in = NULL;
+	struct dma_buffparms *dmap_out = NULL;
+
+	if (!adev)
+		  return -ENXIO;
+	if (!(adev->flags & DMA_DUPLEX))
+		adev->dmap_in = adev->dmap_out;
+	check_driver(adev->d);
+
+	if ((retval = adev->d->open(dev, mode)) < 0)
+		return retval;
+	dmap_out = adev->dmap_out;
+	dmap_in = adev->dmap_in;
+	if (dmap_in == dmap_out)
+		adev->flags &= ~DMA_DUPLEX;
+
+	if (mode & OPEN_WRITE) {
+		if ((retval = open_dmap(adev, mode, dmap_out)) < 0) {
+			adev->d->close(dev);
+			return retval;
+		}
+	}
+	adev->enable_bits = mode;
+
+	if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) {
+		if ((retval = open_dmap(adev, mode, dmap_in)) < 0) {
+			adev->d->close(dev);
+			if (mode & OPEN_WRITE)
+				close_dmap(adev, dmap_out);
+			return retval;
+		}
+	}
+	adev->open_mode = mode;
+	adev->go = 1;
+
+	adev->d->set_bits(dev, 8);
+	adev->d->set_channels(dev, 1);
+	adev->d->set_speed(dev, DSP_DEFAULT_SPEED);
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) 
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+		       adev->dmap_out->bytes_in_use);
+	return 0;
+}
+/* MUST not hold the spinlock */
+void DMAbuf_reset(int dev)
+{
+	if (audio_devs[dev]->open_mode & OPEN_WRITE)
+		dma_reset_output(dev);
+
+	if (audio_devs[dev]->open_mode & OPEN_READ)
+		dma_reset_input(dev);
+}
+
+static void dma_reset_output(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags,f ;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	if (!(dmap->flags & DMA_STARTED))	/* DMA is not active */
+		return;
+
+	/*
+	 *	First wait until the current fragment has been played completely
+	 */
+	spin_lock_irqsave(&dmap->lock,flags);
+	adev->dmap_out->flags |= DMA_SYNCING;
+
+	adev->dmap_out->underrun_count = 0;
+	if (!signal_pending(current) && adev->dmap_out->qlen && 
+	    adev->dmap_out->underrun_count == 0){
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		interruptible_sleep_on_timeout(&adev->out_sleeper,
+					       dmabuf_timeout(dmap));
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+	adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+
+	/*
+	 *	Finally shut the device off
+	 */
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output)
+		adev->d->halt_io(dev);
+	else
+		adev->d->halt_output(dev);
+	adev->dmap_out->flags &= ~DMA_STARTED;
+	
+	f=claim_dma_lock();
+	clear_dma_ff(dmap->dma);
+	disable_dma(dmap->dma);
+	release_dma_lock(f);
+	
+	dmap->byte_counter = 0;
+	reorganize_buffers(dev, adev->dmap_out, 0);
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+
+static void dma_reset_input(int dev)
+{
+        struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input)
+		adev->d->halt_io(dev);
+	else
+		adev->d->halt_input(dev);
+	adev->dmap_in->flags &= ~DMA_STARTED;
+
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	dmap->byte_counter = 0;
+	reorganize_buffers(dev, adev->dmap_in, 1);
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+/* MUST be called with holding the dmap->lock */
+void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT))
+		return;		/* Don't start DMA yet */
+	dmap->dma_mode = DMODE_OUTPUT;
+
+	if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
+		if (!(dmap->flags & DMA_STARTED)) {
+			reorganize_buffers(dev, dmap, 0);
+			if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs))
+				return;
+			if (!(dmap->flags & DMA_NODMA))
+				local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE);
+			dmap->flags |= DMA_STARTED;
+		}
+		if (dmap->counts[dmap->qhead] == 0)
+			dmap->counts[dmap->qhead] = dmap->fragment_size;
+		dmap->dma_mode = DMODE_OUTPUT;
+		adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size,
+				      dmap->counts[dmap->qhead], 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+	dmap->flags |= DMA_ACTIVE;
+}
+
+int DMAbuf_sync(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int n = 0;
+	struct dma_buffparms *dmap;
+
+	if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT))
+		return 0;
+
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) {
+		dmap = adev->dmap_out;
+		spin_lock_irqsave(&dmap->lock,flags);
+		if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE))
+			DMAbuf_launch_output(dev, dmap);
+		adev->dmap_out->flags |= DMA_SYNCING;
+		adev->dmap_out->underrun_count = 0;
+		while (!signal_pending(current) && n++ < adev->dmap_out->nbufs &&
+		       adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) {
+			long t = dmabuf_timeout(dmap);
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			/* FIXME: not safe may miss events */
+			t = interruptible_sleep_on_timeout(&adev->out_sleeper, t);
+			spin_lock_irqsave(&dmap->lock,flags);
+			if (!t) {
+				adev->dmap_out->flags &= ~DMA_SYNCING;
+				spin_unlock_irqrestore(&dmap->lock,flags);
+				return adev->dmap_out->qlen;
+			}
+		}
+		adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+		
+		/*
+		 * Some devices such as GUS have huge amount of on board RAM for the
+		 * audio data. We have to wait until the device has finished playing.
+		 */
+
+		/* still holding the lock */
+		if (adev->d->local_qlen) {   /* Device has hidden buffers */
+			while (!signal_pending(current) &&
+			       adev->d->local_qlen(dev)){
+				spin_unlock_irqrestore(&dmap->lock,flags);
+				interruptible_sleep_on_timeout(&adev->out_sleeper,
+							       dmabuf_timeout(dmap));
+				spin_lock_irqsave(&dmap->lock,flags);
+			}
+		}
+		spin_unlock_irqrestore(&dmap->lock,flags);
+	}
+	adev->dmap_out->dma_mode = DMODE_NONE;
+	return adev->dmap_out->qlen;
+}
+
+int DMAbuf_release(int dev, int mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap;
+	unsigned long flags;
+
+	dmap = adev->dmap_out;
+	if (adev->open_mode & OPEN_WRITE)
+		adev->dmap_out->closing = 1;
+
+	if (adev->open_mode & OPEN_READ){
+		adev->dmap_in->closing = 1;
+		dmap = adev->dmap_in;
+	}
+	if (adev->open_mode & OPEN_WRITE)
+		if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED))
+			if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT))
+				DMAbuf_sync(dev);
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use);
+
+	DMAbuf_reset(dev);
+	spin_lock_irqsave(&dmap->lock,flags);
+	adev->d->close(dev);
+
+	if (adev->open_mode & OPEN_WRITE)
+		close_dmap(adev, adev->dmap_out);
+
+	if (adev->open_mode == OPEN_READ ||
+	    (adev->open_mode != OPEN_WRITE &&
+	     (adev->flags & DMA_DUPLEX)))
+		close_dmap(adev, adev->dmap_in);
+	adev->open_mode = 0;
+	spin_unlock_irqrestore(&dmap->lock,flags);
+	return 0;
+}
+/* called with dmap->lock dold */
+int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int  err;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return 0;
+	if (!(adev->enable_bits & PCM_ENABLE_INPUT))
+		return 0;
+	if (dmap->dma_mode == DMODE_OUTPUT) {	/* Direction change */
+		/* release lock - it's not recursive */
+		spin_unlock_irq(&dmap->lock);
+		DMAbuf_sync(dev);
+		DMAbuf_reset(dev);
+		spin_lock_irq(&dmap->lock);
+		dmap->dma_mode = DMODE_NONE;
+	}
+	if (!dmap->dma_mode) {
+		reorganize_buffers(dev, dmap, 1);
+		if ((err = adev->d->prepare_for_input(dev,
+				dmap->fragment_size, dmap->nbufs)) < 0)
+			return err;
+		dmap->dma_mode = DMODE_INPUT;
+	}
+	if (!(dmap->flags & DMA_ACTIVE)) {
+		if (dmap->needs_reorg)
+			reorganize_buffers(dev, dmap, 0);
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
+				     dmap->fragment_size, 0);
+		dmap->flags |= DMA_ACTIVE;
+		if (adev->d->trigger)
+			adev->d->trigger(dev, adev->enable_bits * adev->go);
+	}
+	return 0;
+}
+/* acquires lock */
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int err = 0, n = 0;
+	struct dma_buffparms *dmap = adev->dmap_in;
+	int go;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return -EIO;
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (dmap->needs_reorg)
+		reorganize_buffers(dev, dmap, 0);
+	if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) {
+/*		  printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/
+		  spin_unlock_irqrestore(&dmap->lock,flags);
+		  return -EINVAL;
+	} else while (dmap->qlen <= 0 && n++ < 10) {
+		long timeout = MAX_SCHEDULE_TIMEOUT;
+		if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) {
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			return -EAGAIN;
+		}
+		if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) {
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			return err;
+		}
+		/* Wait for the next block */
+
+		if (dontblock) {
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			return -EAGAIN;
+		}
+		if ((go = adev->go))
+			timeout = dmabuf_timeout(dmap);
+
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		timeout = interruptible_sleep_on_timeout(&adev->in_sleeper,
+							 timeout);
+		if (!timeout) {
+			/* FIXME: include device name */
+			err = -EIO;
+			printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
+			dma_reset_input(dev);
+		} else
+			err = -EINTR;
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+	spin_unlock_irqrestore(&dmap->lock,flags);
+
+	if (dmap->qlen <= 0)
+		return err ? err : -EINTR;
+	*buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
+	*len = dmap->fragment_size - dmap->counts[dmap->qhead];
+
+	return dmap->qhead;
+}
+
+int DMAbuf_rmchars(int dev, int buff_no, int c)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+	int p = dmap->counts[dmap->qhead] + c;
+
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	{
+/*		  printk("Sound: Can't read from mmapped device (2)\n");*/
+		return -EINVAL;
+	}
+	else if (dmap->qlen <= 0)
+		return -EIO;
+	else if (p >= dmap->fragment_size) {  /* This buffer is completely empty */
+		dmap->counts[dmap->qhead] = 0;
+		dmap->qlen--;
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+	}
+	else dmap->counts[dmap->qhead] = p;
+
+	return 0;
+}
+/* MUST be called with dmap->lock hold */
+int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction)
+{
+	/*
+	 *	Try to approximate the active byte position of the DMA pointer within the
+	 *	buffer area as well as possible.
+	 */
+
+	int pos;
+	unsigned long f;
+
+	if (!(dmap->flags & DMA_ACTIVE))
+		pos = 0;
+	else {
+		int chan = dmap->dma;
+		
+		f=claim_dma_lock();
+		clear_dma_ff(chan);
+		
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		
+		pos = get_dma_residue(chan);
+		
+		pos = dmap->bytes_in_use - pos;
+
+		if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) {
+			if (direction == DMODE_OUTPUT) {
+				if (dmap->qhead == 0)
+					if (pos > dmap->fragment_size)
+						pos = 0;
+			} else {
+				if (dmap->qtail == 0)
+					if (pos > dmap->fragment_size)
+						pos = 0;
+			}
+		}
+		if (pos < 0)
+			pos = 0;
+		if (pos >= dmap->bytes_in_use)
+			pos = 0;
+		
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+			
+		release_dma_lock(f);
+	}
+	/* printk( "%04x ",  pos); */
+
+	return pos;
+}
+
+/*
+ *	DMAbuf_start_devices() is called by the /dev/music driver to start
+ *	one or more audio devices at desired moment.
+ */
+
+void DMAbuf_start_devices(unsigned int devmask)
+{
+	struct audio_operations *adev;
+	int dev;
+
+	for (dev = 0; dev < num_audiodevs; dev++) {
+		if (!(devmask & (1 << dev)))
+			continue;
+		if (!(adev = audio_devs[dev]))
+			continue;
+		if (adev->open_mode == 0)
+			continue;
+		if (adev->go)
+			continue;
+		/* OK to start the device */
+		adev->go = 1;
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+}
+/* via poll called without a lock ?*/
+int DMAbuf_space_in_queue(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int len, max, tmp;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	int lim = dmap->nbufs;
+
+	if (lim < 2)
+		lim = 2;
+
+	if (dmap->qlen >= lim)	/* No space at all */
+		return 0;
+
+	/*
+	 *	Verify that there are no more pending buffers than the limit
+	 *	defined by the process.
+	 */
+
+	max = dmap->max_fragments;
+	if (max > lim)
+		max = lim;
+	len = dmap->qlen;
+
+	if (adev->d->local_qlen) {
+		tmp = adev->d->local_qlen(dev);
+		if (tmp && len)
+			tmp--;	/* This buffer has been counted twice */
+		len += tmp;
+	}
+	if (dmap->byte_counter % dmap->fragment_size)	/* There is a partial fragment */
+		len = len + 1;
+
+	if (len >= max)
+		return 0;
+	return max - len;
+}
+/* MUST not hold the spinlock  - this function may sleep */
+static int output_sleep(int dev, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int err = 0;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	long timeout;
+	long timeout_value;
+
+	if (dontblock)
+		return -EAGAIN;
+	if (!(adev->enable_bits & PCM_ENABLE_OUTPUT))
+		return -EAGAIN;
+
+	/*
+	 * Wait for free space
+	 */
+	if (signal_pending(current))
+		return -EINTR;
+	timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT));
+	if (timeout) 
+		timeout_value = dmabuf_timeout(dmap);
+	else
+		timeout_value = MAX_SCHEDULE_TIMEOUT;
+	timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper,
+						       timeout_value);
+	if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) {
+		printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
+		dma_reset_output(dev);
+	} else {
+		if (signal_pending(current))
+			err = -EINTR;
+	}
+	return err;
+}
+/* called with the lock held */
+static int find_output_space(int dev, char **buf, int *size)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	unsigned long active_offs;
+	long len, offs;
+	int maxfrags;
+	int occupied_bytes = (dmap->user_counter % dmap->fragment_size);
+
+	*buf = dmap->raw_buf;
+	if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes)
+		return 0;
+
+#ifdef BE_CONSERVATIVE
+	active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
+#else
+	active_offs = max(DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT), 0);
+	/* Check for pointer wrapping situation */
+	if (active_offs >= dmap->bytes_in_use)
+		active_offs = 0;
+	active_offs += dmap->byte_counter;
+#endif
+
+	offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP;
+	if (offs < 0 || offs >= dmap->bytes_in_use) {
+		printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs);
+		printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
+		return 0;
+	}
+	*buf = dmap->raw_buf + offs;
+
+	len = active_offs + dmap->bytes_in_use - dmap->user_counter;	/* Number of unused bytes in buffer */
+
+	if ((offs + len) > dmap->bytes_in_use)
+		len = dmap->bytes_in_use - offs;
+	if (len < 0) {
+		return 0;
+	}
+	if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
+		len = (maxfrags * dmap->fragment_size) - occupied_bytes;
+	*size = len & ~SAMPLE_ROUNDUP;
+	return (*size > 0);
+}
+/* acquires lock  */
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int err = -EIO;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+/*		printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (dmap->needs_reorg)
+		reorganize_buffers(dev, dmap, 0);
+
+	if (dmap->dma_mode == DMODE_INPUT) {	/* Direction change */
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		DMAbuf_reset(dev);
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+	dmap->dma_mode = DMODE_OUTPUT;
+
+	while (find_output_space(dev, buf, size) <= 0) {
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		if ((err = output_sleep(dev, dontblock)) < 0) {
+			return err;
+		}
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+
+	spin_unlock_irqrestore(&dmap->lock,flags);
+	return 0;
+}
+/* has to acquire dmap->lock */
+int DMAbuf_move_wrpointer(int dev, int l)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	unsigned long ptr;
+	unsigned long end_ptr, p;
+	int post;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+	post= (dmap->flags & DMA_POST);
+	ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+
+	dmap->flags &= ~DMA_POST;
+	dmap->cfrag = -1;
+	dmap->user_counter += l;
+	dmap->flags |= DMA_DIRTY;
+
+	if (dmap->byte_counter >= dmap->max_byte_counter) {
+		/* Wrap the byte counters */
+		long decr = dmap->byte_counter;
+		dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+		decr -= dmap->byte_counter;
+		dmap->user_counter -= decr;
+	}
+	end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+
+	p = (dmap->user_counter - 1) % dmap->bytes_in_use;
+	dmap->neutral_byte = dmap->raw_buf[p];
+
+	/* Update the fragment based bookkeeping too */
+	while (ptr < end_ptr) {
+		dmap->counts[dmap->qtail] = dmap->fragment_size;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		dmap->qlen++;
+		ptr += dmap->fragment_size;
+	}
+
+	dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+
+	/*
+	 *	Let the low level driver perform some postprocessing to
+	 *	the written data.
+	 */
+	if (adev->d->postprocess_write)
+		adev->d->postprocess_write(dev);
+
+	if (!(dmap->flags & DMA_ACTIVE))
+		if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
+			DMAbuf_launch_output(dev, dmap);
+
+	spin_unlock_irqrestore(&dmap->lock,flags);
+	return 0;
+}
+
+int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "sound: DMA buffer(1) == NULL\n");
+		printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in");
+		return 0;
+	}
+	if (dmap->dma < 0)
+		return 0;
+	sound_start_dma(dmap, physaddr, count, dma_mode);
+	return count;
+}
+EXPORT_SYMBOL(DMAbuf_start_dma);
+
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode)
+{
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "sound: DMA buffer(2) == NULL\n");
+		printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in");
+		return 0;
+	}
+	if (dmap->flags & DMA_NODMA)
+		return 1;
+	if (dmap->dma < 0)
+		return 0;
+	sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT);
+	dmap->flags |= DMA_STARTED;
+	return count;
+}
+
+static void finish_output_interrupt(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (dmap->audio_callback != NULL)
+		dmap->audio_callback(dev, dmap->callback_parm);
+	wake_up(&adev->out_sleeper);
+	wake_up(&adev->poll_sleeper);
+}
+/* called with dmap->lock held in irq context*/
+static void do_outputintr(int dev, int dummy)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	int this_fragment;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev);
+		return;
+	}
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {	/* Virtual memory mapped access */
+		/* mmapped access */
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+		if (dmap->qhead == 0) {	    /* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+		dmap->qlen++;	/* Yes increment it (don't decrement) */
+		if (!(adev->flags & DMA_AUTOMODE))
+			dmap->flags &= ~DMA_ACTIVE;
+		dmap->counts[dmap->qhead] = dmap->fragment_size;
+		DMAbuf_launch_output(dev, dmap);
+		finish_output_interrupt(dev, dmap);
+		return;
+	}
+
+	dmap->qlen--;
+	this_fragment = dmap->qhead;
+	dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+
+	if (dmap->qhead == 0) {	/* Wrapped */
+		dmap->byte_counter += dmap->bytes_in_use;
+		if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+			long decr = dmap->byte_counter;
+			dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+			decr -= dmap->byte_counter;
+			dmap->user_counter -= decr;
+		}
+	}
+	if (!(adev->flags & DMA_AUTOMODE))
+		dmap->flags &= ~DMA_ACTIVE;
+		
+	/*
+	 *	This is  dmap->qlen <= 0 except when closing when
+	 *	dmap->qlen < 0
+	 */
+	 
+	while (dmap->qlen <= -dmap->closing) {
+		dmap->underrun_count++;
+		dmap->qlen++;
+		if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) {
+			dmap->flags &= ~DMA_DIRTY;
+			memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+			       adev->dmap_out->buffsize);
+		}
+		dmap->user_counter += dmap->fragment_size;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+	}
+	if (dmap->qlen > 0)
+		DMAbuf_launch_output(dev, dmap);
+	finish_output_interrupt(dev, dmap);
+}
+/* called in irq context */
+void DMAbuf_outputintr(int dev, int notify_only)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (!(dmap->flags & DMA_NODMA)) {
+		int chan = dmap->dma, pos, n;
+		unsigned long f;
+		
+		f=claim_dma_lock();
+		
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		clear_dma_ff(chan);
+		pos = dmap->bytes_in_use - get_dma_residue(chan);
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+		release_dma_lock(f);
+		
+		pos = pos / dmap->fragment_size;	/* Actual qhead */
+		if (pos < 0 || pos >= dmap->nbufs)
+			pos = 0;
+		n = 0;
+		while (dmap->qhead != pos && n++ < dmap->nbufs)
+			do_outputintr(dev, notify_only);
+	}
+	else
+		do_outputintr(dev, notify_only);
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+EXPORT_SYMBOL(DMAbuf_outputintr);
+
+/* called with dmap->lock held in irq context */
+static void do_inputintr(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n");
+		return;
+	}
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		if (dmap->qtail == 0) {		/* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+		dmap->qlen++;
+
+		if (!(adev->flags & DMA_AUTOMODE)) {
+			if (dmap->needs_reorg)
+				reorganize_buffers(dev, dmap, 0);
+			local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ);
+			adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
+					     dmap->fragment_size, 1);
+			if (adev->d->trigger)
+				adev->d->trigger(dev, adev->enable_bits * adev->go);
+		}
+		dmap->flags |= DMA_ACTIVE;
+	} else if (dmap->qlen >= (dmap->nbufs - 1)) {
+		printk(KERN_WARNING "Sound: Recording overrun\n");
+		dmap->underrun_count++;
+
+		/* Just throw away the oldest fragment but keep the engine running */
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+	} else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) {
+		dmap->qlen++;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		if (dmap->qtail == 0) {		/* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+	}
+	if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+	dmap->flags |= DMA_ACTIVE;
+	if (dmap->qlen > 0)
+	{
+		wake_up(&adev->in_sleeper);
+		wake_up(&adev->poll_sleeper);
+	}
+}
+/* called in irq context */
+void DMAbuf_inputintr(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+
+	if (!(dmap->flags & DMA_NODMA)) {
+		int chan = dmap->dma, pos, n;
+		unsigned long f;
+		
+		f=claim_dma_lock();
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		clear_dma_ff(chan);
+		pos = dmap->bytes_in_use - get_dma_residue(chan);
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+		release_dma_lock(f);
+
+		pos = pos / dmap->fragment_size;	/* Actual qhead */
+		if (pos < 0 || pos >= dmap->nbufs)
+			pos = 0;
+
+		n = 0;
+		while (dmap->qtail != pos && ++n < dmap->nbufs)
+			do_inputintr(dev);
+	} else
+		do_inputintr(dev);
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+EXPORT_SYMBOL(DMAbuf_inputintr);
+
+void DMAbuf_init(int dev, int dma1, int dma2)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	/*
+	 * NOTE! This routine could be called several times.
+	 */
+
+	if (adev && adev->dmap_out == NULL) {
+		if (adev->d == NULL)
+			panic("OSS: audio_devs[%d]->d == NULL\n", dev);
+
+		if (adev->parent_dev) {	 /* Use DMA map of the parent dev */
+			int parent = adev->parent_dev - 1;
+			adev->dmap_out = audio_devs[parent]->dmap_out;
+			adev->dmap_in = audio_devs[parent]->dmap_in;
+		} else {
+			adev->dmap_out = adev->dmap_in = &adev->dmaps[0];
+			adev->dmap_out->dma = dma1;
+			if (adev->flags & DMA_DUPLEX) {
+				adev->dmap_in = &adev->dmaps[1];
+				adev->dmap_in->dma = dma2;
+			}
+		}
+		/* Persistent DMA buffers allocated here */
+		if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
+			if (adev->dmap_in->raw_buf == NULL)
+				sound_alloc_dmap(adev->dmap_in);
+			if (adev->dmap_out->raw_buf == NULL)
+				sound_alloc_dmap(adev->dmap_out);
+		}
+	}
+}
+
+/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */
+static unsigned int poll_input(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		if (dmap->qlen)
+			return POLLIN | POLLRDNORM;
+		return 0;
+	}
+	if (dmap->dma_mode != DMODE_INPUT) {
+		if (dmap->dma_mode == DMODE_NONE &&
+		    adev->enable_bits & PCM_ENABLE_INPUT &&
+		    !dmap->qlen && adev->go) {
+			unsigned long flags;
+			
+			spin_lock_irqsave(&dmap->lock,flags);
+			DMAbuf_activate_recording(dev, dmap);
+			spin_unlock_irqrestore(&dmap->lock,flags);
+		}
+		return 0;
+	}
+	if (!dmap->qlen)
+		return 0;
+	return POLLIN | POLLRDNORM;
+}
+
+static unsigned int poll_output(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	
+	if (!(adev->open_mode & OPEN_WRITE))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		if (dmap->qlen)
+			return POLLOUT | POLLWRNORM;
+		return 0;
+	}
+	if (dmap->dma_mode == DMODE_INPUT)
+		return 0;
+	if (dmap->dma_mode == DMODE_NONE)
+		return POLLOUT | POLLWRNORM;
+	if (!DMAbuf_space_in_queue(dev))
+		return 0;
+	return POLLOUT | POLLWRNORM;
+}
+
+unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	poll_wait(file, &adev->poll_sleeper, wait);
+	return poll_input(file, dev, wait) | poll_output(file, dev, wait);
+}
+
+void DMAbuf_deinit(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	/* This routine is called when driver is being unloaded */
+	if (!adev)
+		return;
+
+	/* Persistent DMA buffers deallocated here */
+	if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
+		sound_free_dmap(adev->dmap_out);
+		if (adev->flags & DMA_DUPLEX)
+			sound_free_dmap(adev->dmap_in);
+	}
+}
diff --git a/sound/oss/dmasound/Kconfig b/sound/oss/dmasound/Kconfig
new file mode 100644
index 00000000..f456574a
--- /dev/null
+++ b/sound/oss/dmasound/Kconfig
@@ -0,0 +1,45 @@
+config DMASOUND_ATARI
+	tristate "Atari DMA sound support"
+	depends on ATARI && SOUND
+	select DMASOUND
+	help
+	  If you want to use the internal audio of your Atari in Linux, answer
+	  Y to this question. This will provide a Sun-like /dev/audio,
+	  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you
+	  want). If you want to compile it as a module, say M here and read
+	  <file:Documentation/kbuild/modules.txt>.
+
+config DMASOUND_PAULA
+	tristate "Amiga DMA sound support"
+	depends on AMIGA && SOUND
+	select DMASOUND
+	help
+	  If you want to use the internal audio of your Amiga in Linux, answer
+	  Y to this question. This will provide a Sun-like /dev/audio,
+	  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you
+	  want). If you want to compile it as a module, say M here and read
+	  <file:Documentation/kbuild/modules.txt>.
+
+config DMASOUND_Q40
+	tristate "Q40 sound support"
+	depends on Q40 && SOUND
+	select DMASOUND
+	help
+	  If you want to use the internal audio of your Q40 in Linux, answer
+	  Y to this question. This will provide a Sun-like /dev/audio,
+	  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you
+	  want). If you want to compile it as a module, say M here and read
+	  <file:Documentation/kbuild/modules.txt>.
+
+config DMASOUND
+	tristate
+	select SOUND_OSS_CORE
diff --git a/sound/oss/dmasound/Makefile b/sound/oss/dmasound/Makefile
new file mode 100644
index 00000000..3c153165
--- /dev/null
+++ b/sound/oss/dmasound/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the DMA sound driver
+#
+
+obj-$(CONFIG_DMASOUND_ATARI)	+= dmasound_core.o dmasound_atari.o
+obj-$(CONFIG_DMASOUND_PAULA)	+= dmasound_core.o dmasound_paula.o
+obj-$(CONFIG_DMASOUND_Q40)	+= dmasound_core.o dmasound_q40.o
diff --git a/sound/oss/dmasound/dmasound.h b/sound/oss/dmasound/dmasound.h
new file mode 100644
index 00000000..1308d8d3
--- /dev/null
+++ b/sound/oss/dmasound/dmasound.h
@@ -0,0 +1,262 @@
+#ifndef _dmasound_h_
+/*
+ *  linux/sound/oss/dmasound/dmasound.h
+ *
+ *
+ *  Minor numbers for the sound driver.
+ *
+ *  Unfortunately Creative called the codec chip of SB as a DSP. For this
+ *  reason the /dev/dsp is reserved for digitized audio use. There is a
+ *  device for true DSP processors but it will be called something else.
+ *  In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+#define _dmasound_h_
+
+#include <linux/types.h>
+
+#define SND_NDEVS	256	/* Number of supported devices */
+#define SND_DEV_CTL	0	/* Control port /dev/mixer */
+#define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
+				   synthesizer and MIDI output) */
+#define SND_DEV_MIDIN	2	/* Raw midi access */
+#define SND_DEV_DSP	3	/* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO	4	/* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16	5	/* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS	6	/* /dev/sndstat */
+/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2	8	/* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9	/* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS	SND_DEV_SNDPROC
+
+/* switch on various prinks */
+#define DEBUG_DMASOUND 1
+
+#define MAX_AUDIO_DEV	5
+#define MAX_MIXER_DEV	4
+#define MAX_SYNTH_DEV	3
+#define MAX_MIDI_DEV	6
+#define MAX_TIMER_DEV	3
+
+#define MAX_CATCH_RADIUS	10
+
+#define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
+#define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
+
+#define IOCTL_IN(arg, ret) \
+	do { int error = get_user(ret, (int __user *)(arg)); \
+		if (error) return error; \
+	} while (0)
+#define IOCTL_OUT(arg, ret)	ioctl_return((int __user *)(arg), ret)
+
+static inline int ioctl_return(int __user *addr, int value)
+{
+	return value < 0 ? value : put_user(value, addr);
+}
+
+
+    /*
+     *  Configuration
+     */
+
+#undef HAS_8BIT_TABLES
+
+#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
+    defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
+    defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE)
+#define HAS_8BIT_TABLES
+#define MIN_BUFFERS	4
+#define MIN_BUFSIZE	(1<<12)	/* in bytes (- where does this come from ?) */
+#define MIN_FRAG_SIZE	8	/* not 100% sure about this */
+#define MAX_BUFSIZE	(1<<17)	/* Limit for Amiga is 128 kb */
+#define MAX_FRAG_SIZE	15	/* allow *4 for mono-8 => stereo-16 (for multi) */
+
+#else /* is pmac and multi is off */
+
+#define MIN_BUFFERS	2
+#define MIN_BUFSIZE	(1<<8)	/* in bytes */
+#define MIN_FRAG_SIZE	8
+#define MAX_BUFSIZE	(1<<18)	/* this is somewhat arbitrary for pmac */
+#define MAX_FRAG_SIZE	16	/* need to allow *4 for mono-8 => stereo-16 */
+#endif
+
+#define DEFAULT_N_BUFFERS 4
+#define DEFAULT_BUFF_SIZE (1<<15)
+
+    /*
+     *  Initialization
+     */
+
+extern int dmasound_init(void);
+#ifdef MODULE
+extern void dmasound_deinit(void);
+#else
+#define dmasound_deinit()	do { } while (0)
+#endif
+
+/* description of the set-up applies to either hard or soft settings */
+
+typedef struct {
+    int format;		/* AFMT_* */
+    int stereo;		/* 0 = mono, 1 = stereo */
+    int size;		/* 8/16 bit*/
+    int speed;		/* speed */
+} SETTINGS;
+
+    /*
+     *  Machine definitions
+     */
+
+typedef struct {
+    const char *name;
+    const char *name2;
+    struct module *owner;
+    void *(*dma_alloc)(unsigned int, gfp_t);
+    void (*dma_free)(void *, unsigned int);
+    int (*irqinit)(void);
+#ifdef MODULE
+    void (*irqcleanup)(void);
+#endif
+    void (*init)(void);
+    void (*silence)(void);
+    int (*setFormat)(int);
+    int (*setVolume)(int);
+    int (*setBass)(int);
+    int (*setTreble)(int);
+    int (*setGain)(int);
+    void (*play)(void);
+    void (*record)(void);		/* optional */
+    void (*mixer_init)(void);		/* optional */
+    int (*mixer_ioctl)(u_int, u_long);	/* optional */
+    int (*write_sq_setup)(void);	/* optional */
+    int (*read_sq_setup)(void);		/* optional */
+    int (*sq_open)(fmode_t);		/* optional */
+    int (*state_info)(char *, size_t);	/* optional */
+    void (*abort_read)(void);		/* optional */
+    int min_dsp_speed;
+    int max_dsp_speed;
+    int version ;
+    int hardware_afmts ;		/* OSS says we only return h'ware info */
+					/* when queried via SNDCTL_DSP_GETFMTS */
+    int capabilities ;		/* low-level reply to SNDCTL_DSP_GETCAPS */
+    SETTINGS default_hard ;	/* open() or init() should set something valid */
+    SETTINGS default_soft ;	/* you can make it look like old OSS, if you want to */
+} MACHINE;
+
+    /*
+     *  Low level stuff
+     */
+
+typedef struct {
+    ssize_t (*ct_ulaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_alaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+} TRANS;
+
+struct sound_settings {
+    MACHINE mach;	/* machine dependent things */
+    SETTINGS hard;	/* hardware settings */
+    SETTINGS soft;	/* software settings */
+    SETTINGS dsp;	/* /dev/dsp default settings */
+    TRANS *trans_write;	/* supported translations */
+    int volume_left;	/* volume (range is machine dependent) */
+    int volume_right;
+    int bass;		/* tone (range is machine dependent) */
+    int treble;
+    int gain;
+    int minDev;		/* minor device number currently open */
+    spinlock_t lock;
+};
+
+extern struct sound_settings dmasound;
+
+#ifdef HAS_8BIT_TABLES
+extern char dmasound_ulaw2dma8[];
+extern char dmasound_alaw2dma8[];
+#endif
+
+    /*
+     *  Mid level stuff
+     */
+
+static inline int dmasound_set_volume(int volume)
+{
+	return dmasound.mach.setVolume(volume);
+}
+
+static inline int dmasound_set_bass(int bass)
+{
+	return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50;
+}
+
+static inline int dmasound_set_treble(int treble)
+{
+	return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50;
+}
+
+static inline int dmasound_set_gain(int gain)
+{
+	return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100;
+}
+
+
+    /*
+     * Sound queue stuff, the heart of the driver
+     */
+
+struct sound_queue {
+    /* buffers allocated for this queue */
+    int numBufs;		/* real limits on what the user can have */
+    int bufSize;		/* in bytes */
+    char **buffers;
+
+    /* current parameters */
+    int locked ;		/* params cannot be modified when != 0 */
+    int user_frags ;		/* user requests this many */
+    int user_frag_size ;	/* of this size */
+    int max_count;		/* actual # fragments <= numBufs */
+    int block_size;		/* internal block size in bytes */
+    int max_active;		/* in-use fragments <= max_count */
+
+    /* it shouldn't be necessary to declare any of these volatile */
+    int front, rear, count;
+    int rear_size;
+    /*
+     *	The use of the playing field depends on the hardware
+     *
+     *	Atari, PMac: The number of frames that are loaded/playing
+     *
+     *	Amiga: Bit 0 is set: a frame is loaded
+     *	       Bit 1 is set: a frame is playing
+     */
+    int active;
+    wait_queue_head_t action_queue, open_queue, sync_queue;
+    int non_blocking;
+    int busy, syncing, xruns, died;
+};
+
+#define SLEEP(queue)		interruptible_sleep_on_timeout(&queue, HZ)
+#define WAKE_UP(queue)		(wake_up_interruptible(&queue))
+
+extern struct sound_queue dmasound_write_sq;
+#define write_sq	dmasound_write_sq
+
+extern int dmasound_catchRadius;
+#define catchRadius	dmasound_catchRadius
+
+/* define the value to be put in the byte-swap reg in mac-io
+   when we want it to swap for us.
+*/
+#define BS_VAL 1
+
+#define SW_INPUT_VOLUME_SCALE	4
+#define SW_INPUT_VOLUME_DEFAULT	(128 / SW_INPUT_VOLUME_SCALE)
+
+extern int expand_read_bal;	/* Balance factor for reading */
+extern uint software_input_volume; /* software implemented recording volume! */
+
+#endif /* _dmasound_h_ */
diff --git a/sound/oss/dmasound/dmasound_atari.c b/sound/oss/dmasound/dmasound_atari.c
new file mode 100644
index 00000000..13c21446
--- /dev/null
+++ b/sound/oss/dmasound/dmasound_atari.c
@@ -0,0 +1,1620 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_atari.c
+ *
+ *  Atari TT and Falcon DMA Sound Driver
+ *
+ *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *  01/02/2001 [0.3] - put in default hard/soft settings.
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+#include <asm/atariints.h>
+#include <asm/atari_stram.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_ATARI_REVISION 0
+#define DMASOUND_ATARI_EDITION 3
+
+extern void atari_microwire_cmd(int cmd);
+
+static int is_falcon;
+static int write_sq_ignore_int;	/* ++TeSche: used for Falcon */
+
+static int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+
+/*** Translations ************************************************************/
+
+
+/* ++TeSche: radically changed for new expanding purposes...
+ *
+ * These two routines now deal with copying/expanding/translating the samples
+ * from user space into our buffer at the right frequency. They take care about
+ * how much data there's actually to read, how much buffer space there is and
+ * to convert samples into the right frequency/encoding. They will only work on
+ * complete samples so it may happen they leave some bytes in the input stream
+ * if the user didn't write a multiple of the current sample size. They both
+ * return the number of bytes they've used from both streams so you may detect
+ * such a situation. Luckily all programs should be able to cope with that.
+ *
+ * I think I've optimized anything as far as one can do in plain C, all
+ * variables should fit in registers and the loops are really short. There's
+ * one loop for every possible situation. Writing a more generalized and thus
+ * parameterized loop would only produce slower code. Feel free to optimize
+ * this in assembler if you like. :)
+ *
+ * I think these routines belong here because they're not yet really hardware
+ * independent, especially the fact that the Falcon can play 16bit samples
+ * only in stereo is hardcoded in both of them!
+ *
+ * ++geert: split in even more functions (one per format)
+ */
+
+static ssize_t ata_ct_law(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ct_s8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_u8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_s16be(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16be(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_s16le(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16le(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ctx_law(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t ata_ctx_s8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_u8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_s16be(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16be(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_s16le(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16le(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void *AtaAlloc(unsigned int size, gfp_t flags);
+static void AtaFree(void *, unsigned int size);
+static int AtaIrqInit(void);
+#ifdef MODULE
+static void AtaIrqCleanUp(void);
+#endif /* MODULE */
+static int AtaSetBass(int bass);
+static int AtaSetTreble(int treble);
+static void TTSilence(void);
+static void TTInit(void);
+static int TTSetFormat(int format);
+static int TTSetVolume(int volume);
+static int TTSetGain(int gain);
+static void FalconSilence(void);
+static void FalconInit(void);
+static int FalconSetFormat(int format);
+static int FalconSetVolume(int volume);
+static void AtaPlayNextFrame(int index);
+static void AtaPlay(void);
+static irqreturn_t AtaInterrupt(int irq, void *dummy);
+
+/*** Mid level stuff *********************************************************/
+
+static void TTMixerInit(void);
+static void FalconMixerInit(void);
+static int AtaMixerIoctl(u_int cmd, u_long arg);
+static int TTMixerIoctl(u_int cmd, u_long arg);
+static int FalconMixerIoctl(u_int cmd, u_long arg);
+static int AtaWriteSqSetup(void);
+static int AtaSqOpen(fmode_t mode);
+static int TTStateInfo(char *buffer, size_t space);
+static int FalconStateInfo(char *buffer, size_t space);
+
+
+/*** Translations ************************************************************/
+
+
+static ssize_t ata_ct_law(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8
+							  : dmasound_alaw2dma8;
+	ssize_t count, used;
+	u_char *p = &frame[*frameUsed];
+
+	count = min_t(unsigned long, userCount, frameLeft);
+	if (dmasound.soft.stereo)
+		count &= ~1;
+	used = count;
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		*p++ = table[data];
+		count--;
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_s8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft)
+{
+	ssize_t count, used;
+	void *p = &frame[*frameUsed];
+
+	count = min_t(unsigned long, userCount, frameLeft);
+	if (dmasound.soft.stereo)
+		count &= ~1;
+	used = count;
+	if (copy_from_user(p, userPtr, count))
+		return -EFAULT;
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_u8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft);
+		used = count;
+		while (count > 0) {
+			u_char data;
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			*p++ = data ^ 0x80;
+			count--;
+		}
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			*p++ = data ^ 0x8080;
+			count--;
+		}
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_s16be(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		void *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft) & ~3;
+		used = count;
+		if (copy_from_user(p, userPtr, count))
+			return -EFAULT;
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_u16be(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			data ^= 0x8000;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_int data;
+			if (get_user(data, (u_int __user *)userPtr))
+				return -EFAULT;
+			userPtr += 4;
+			*p++ = data ^ 0x80008000;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_s16le(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	count = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			data = le2be16(data);
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, (u_int __user *)userPtr))
+				return -EFAULT;
+			userPtr += 4;
+			data = le2be16dbl(data);
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_u16le(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	count = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			data = le2be16(data) ^ 0x8000;
+			*p++ = data;
+			*p++ = data;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, (u_int __user *)userPtr))
+				return -EFAULT;
+			userPtr += 4;
+			data = le2be16dbl(data) ^ 0x80008000;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ctx_law(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8
+							  : dmasound_alaw2dma8;
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			u_char c;
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = table[c];
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			u_char c;
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = table[c] << 8;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data |= table[c];
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(data, userPtr++))
+					return -EFAULT;
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, (u_short __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(data, userPtr++))
+					return -EFAULT;
+				data ^= 0x80;
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, (u_short __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				data ^= 0x8080;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s16be(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, (u_short __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, (u_int __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u16be(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, (u_short __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				data ^= 0x8000;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, (u_int __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				data ^= 0x80008000;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s16le(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, (u_short __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				data = le2be16(data);
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, (u_int __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				data = le2be16dbl(data);
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u16le(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, (u_short __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				data = le2be16(data) ^ 0x8000;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, (u_int __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				data = le2be16dbl(data) ^ 0x80008000;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static TRANS transTTNormal = {
+	.ct_ulaw	= ata_ct_law,
+	.ct_alaw	= ata_ct_law,
+	.ct_s8		= ata_ct_s8,
+	.ct_u8		= ata_ct_u8,
+};
+
+static TRANS transTTExpanding = {
+	.ct_ulaw	= ata_ctx_law,
+	.ct_alaw	= ata_ctx_law,
+	.ct_s8		= ata_ctx_s8,
+	.ct_u8		= ata_ctx_u8,
+};
+
+static TRANS transFalconNormal = {
+	.ct_ulaw	= ata_ct_law,
+	.ct_alaw	= ata_ct_law,
+	.ct_s8		= ata_ct_s8,
+	.ct_u8		= ata_ct_u8,
+	.ct_s16be	= ata_ct_s16be,
+	.ct_u16be	= ata_ct_u16be,
+	.ct_s16le	= ata_ct_s16le,
+	.ct_u16le	= ata_ct_u16le
+};
+
+static TRANS transFalconExpanding = {
+	.ct_ulaw	= ata_ctx_law,
+	.ct_alaw	= ata_ctx_law,
+	.ct_s8		= ata_ctx_s8,
+	.ct_u8		= ata_ctx_u8,
+	.ct_s16be	= ata_ctx_s16be,
+	.ct_u16be	= ata_ctx_u16be,
+	.ct_s16le	= ata_ctx_s16le,
+	.ct_u16le	= ata_ctx_u16le,
+};
+
+
+/*** Low level stuff *********************************************************/
+
+
+
+/*
+ * Atari (TT/Falcon)
+ */
+
+static void *AtaAlloc(unsigned int size, gfp_t flags)
+{
+	return atari_stram_alloc(size, "dmasound");
+}
+
+static void AtaFree(void *obj, unsigned int size)
+{
+	atari_stram_free( obj );
+}
+
+static int __init AtaIrqInit(void)
+{
+	/* Set up timer A. Timer A
+	   will receive a signal upon end of playing from the sound
+	   hardware. Furthermore Timer A is able to count events
+	   and will cause an interrupt after a programmed number
+	   of events. So all we need to keep the music playing is
+	   to provide the sound hardware with new data upon
+	   an interrupt from timer A. */
+	st_mfp.tim_ct_a = 0;	/* ++roman: Stop timer before programming! */
+	st_mfp.tim_dt_a = 1;	/* Cause interrupt after first event. */
+	st_mfp.tim_ct_a = 8;	/* Turn on event counting. */
+	/* Register interrupt handler. */
+	if (request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound",
+			AtaInterrupt))
+		return 0;
+	st_mfp.int_en_a |= 0x20;	/* Turn interrupt on. */
+	st_mfp.int_mk_a |= 0x20;
+	return 1;
+}
+
+#ifdef MODULE
+static void AtaIrqCleanUp(void)
+{
+	st_mfp.tim_ct_a = 0;		/* stop timer */
+	st_mfp.int_en_a &= ~0x20;	/* turn interrupt off */
+	free_irq(IRQ_MFP_TIMA, AtaInterrupt);
+}
+#endif /* MODULE */
+
+
+#define TONE_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25)
+#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50)
+
+
+static int AtaSetBass(int bass)
+{
+	dmasound.bass = TONE_VOXWARE_TO_DB(bass);
+	atari_microwire_cmd(MW_LM1992_BASS(dmasound.bass));
+	return TONE_DB_TO_VOXWARE(dmasound.bass);
+}
+
+
+static int AtaSetTreble(int treble)
+{
+	dmasound.treble = TONE_VOXWARE_TO_DB(treble);
+	atari_microwire_cmd(MW_LM1992_TREBLE(dmasound.treble));
+	return TONE_DB_TO_VOXWARE(dmasound.treble);
+}
+
+
+
+/*
+ * TT
+ */
+
+
+static void TTSilence(void)
+{
+	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+	atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */
+}
+
+
+static void TTInit(void)
+{
+	int mode, i, idx;
+	const int freq[4] = {50066, 25033, 12517, 6258};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < ARRAY_SIZE(freq); i++)
+		/* this isn't as much useful for a TT than for a Falcon, but
+		 * then it doesn't hurt very much to implement it for a TT too.
+		 */
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+			idx = i;
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transTTNormal;
+	} else
+		dmasound.trans_write = &transTTExpanding;
+
+	TTSilence();
+	dmasound.hard = dmasound.soft;
+
+	if (dmasound.hard.speed > 50066) {
+		/* we would need to squeeze the sound, but we won't do that */
+		dmasound.hard.speed = 50066;
+		mode = DMASND_MODE_50KHZ;
+		dmasound.trans_write = &transTTNormal;
+	} else if (dmasound.hard.speed > 25033) {
+		dmasound.hard.speed = 50066;
+		mode = DMASND_MODE_50KHZ;
+	} else if (dmasound.hard.speed > 12517) {
+		dmasound.hard.speed = 25033;
+		mode = DMASND_MODE_25KHZ;
+	} else if (dmasound.hard.speed > 6258) {
+		dmasound.hard.speed = 12517;
+		mode = DMASND_MODE_12KHZ;
+	} else {
+		dmasound.hard.speed = 6258;
+		mode = DMASND_MODE_6KHZ;
+	}
+
+	tt_dmasnd.mode = (dmasound.hard.stereo ?
+			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		DMASND_MODE_8BIT | mode;
+
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int TTSetFormat(int format)
+{
+	/* TT sound DMA supports only 8bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_S8:
+	case AFMT_U8:
+		break;
+	default:
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = 8;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = 8;
+	}
+	TTInit();
+
+	return format;
+}
+
+
+#define VOLUME_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40)
+#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2)
+
+
+static int TTSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff);
+	atari_microwire_cmd(MW_LM1992_BALLEFT(dmasound.volume_left));
+	dmasound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8);
+	atari_microwire_cmd(MW_LM1992_BALRIGHT(dmasound.volume_right));
+	return VOLUME_DB_TO_VOXWARE(dmasound.volume_left) |
+	       (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8);
+}
+
+
+#define GAIN_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80)
+#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4)
+
+static int TTSetGain(int gain)
+{
+	dmasound.gain = GAIN_VOXWARE_TO_DB(gain);
+	atari_microwire_cmd(MW_LM1992_VOLUME(dmasound.gain));
+	return GAIN_DB_TO_VOXWARE(dmasound.gain);
+}
+
+
+
+/*
+ * Falcon
+ */
+
+
+static void FalconSilence(void)
+{
+	/* stop playback, set sample rate 50kHz for PSG sound */
+	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+	tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT;
+	tt_dmasnd.int_div = 0; /* STE compatible divider */
+	tt_dmasnd.int_ctrl = 0x0;
+	tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */
+	tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */
+	tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */
+	tt_dmasnd.adc_src = 3; /* ADC Input = PSG */
+}
+
+
+static void FalconInit(void)
+{
+	int divider, i, idx;
+	const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < ARRAY_SIZE(freq); i++)
+		/* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would
+		 * be playable without expanding, but that now a kernel runtime
+		 * option
+		 */
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+			idx = i;
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transFalconNormal;
+	} else
+		dmasound.trans_write = &transFalconExpanding;
+
+	FalconSilence();
+	dmasound.hard = dmasound.soft;
+
+	if (dmasound.hard.size == 16) {
+		/* the Falcon can play 16bit samples only in stereo */
+		dmasound.hard.stereo = 1;
+	}
+
+	if (dmasound.hard.speed > 49170) {
+		/* we would need to squeeze the sound, but we won't do that */
+		dmasound.hard.speed = 49170;
+		divider = 1;
+		dmasound.trans_write = &transFalconNormal;
+	} else if (dmasound.hard.speed > 32780) {
+		dmasound.hard.speed = 49170;
+		divider = 1;
+	} else if (dmasound.hard.speed > 24585) {
+		dmasound.hard.speed = 32780;
+		divider = 2;
+	} else if (dmasound.hard.speed > 19668) {
+		dmasound.hard.speed = 24585;
+		divider = 3;
+	} else if (dmasound.hard.speed > 16390) {
+		dmasound.hard.speed = 19668;
+		divider = 4;
+	} else if (dmasound.hard.speed > 12292) {
+		dmasound.hard.speed = 16390;
+		divider = 5;
+	} else if (dmasound.hard.speed > 9834) {
+		dmasound.hard.speed = 12292;
+		divider = 7;
+	} else if (dmasound.hard.speed > 8195) {
+		dmasound.hard.speed = 9834;
+		divider = 9;
+	} else {
+		dmasound.hard.speed = 8195;
+		divider = 11;
+	}
+	tt_dmasnd.int_div = divider;
+
+	/* Setup Falcon sound DMA for playback */
+	tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */
+	tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */
+	tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */
+	tt_dmasnd.cbar_dst = 0x0000;
+	tt_dmasnd.rec_track_select = 0;
+	tt_dmasnd.dac_src = 2; /* connect matrix to DAC */
+	tt_dmasnd.adc_src = 0; /* ADC Input = Mic */
+
+	tt_dmasnd.mode = (dmasound.hard.stereo ?
+			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		((dmasound.hard.size == 8) ?
+		 DMASND_MODE_8BIT : DMASND_MODE_16BIT) |
+		DMASND_MODE_6KHZ;
+
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int FalconSetFormat(int format)
+{
+	int size;
+	/* Falcon sound DMA supports 8bit and 16bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+		size = 8;
+		break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+		size = 16;
+		break;
+	default: /* :-) */
+		size = 8;
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = size;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = dmasound.soft.size;
+	}
+
+	FalconInit();
+
+	return format;
+}
+
+
+/* This is for the Falcon output *attenuation* in 1.5dB steps,
+ * i.e. output level from 0 to -22.5dB in -1.5dB steps.
+ */
+#define VOLUME_VOXWARE_TO_ATT(v) \
+	((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20)
+#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3)
+
+
+static int FalconSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff);
+	dmasound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8);
+	tt_dmasnd.output_atten = dmasound.volume_left << 8 | dmasound.volume_right << 4;
+	return VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
+	       VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8;
+}
+
+
+static void AtaPlayNextFrame(int index)
+{
+	char *start, *end;
+
+	/* used by AtaPlay() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	end = start+((write_sq.count == index) ? write_sq.rear_size
+					       : write_sq.block_size);
+	/* end might not be a legal virtual address. */
+	DMASNDSetEnd(virt_to_phys(end - 1) + 1);
+	DMASNDSetBase(virt_to_phys(start));
+	/* Since only an even number of samples per frame can
+	   be played, we might lose one byte here. (TO DO) */
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active++;
+	tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
+}
+
+
+static void AtaPlay(void)
+{
+	/* ++TeSche: Note that write_sq.active is no longer just a flag but
+	 * holds the number of frames the DMA is currently programmed for
+	 * instead, may be 0, 1 (currently being played) or 2 (pre-programmed).
+	 *
+	 * Changes done to write_sq.count and write_sq.active are a bit more
+	 * subtle again so now I must admit I also prefer disabling the irq
+	 * here rather than considering all possible situations. But the point
+	 * is that disabling the irq doesn't have any bad influence on this
+	 * version of the driver as we benefit from having pre-programmed the
+	 * DMA wherever possible: There's no need to reload the DMA at the
+	 * exact time of an interrupt but only at some time while the
+	 * pre-programmed frame is playing!
+	 */
+	atari_disable_irq(IRQ_MFP_TIMA);
+
+	if (write_sq.active == 2 ||	/* DMA is 'full' */
+	    write_sq.count <= 0) {	/* nothing to do */
+		atari_enable_irq(IRQ_MFP_TIMA);
+		return;
+	}
+
+	if (write_sq.active == 0) {
+		/* looks like there's nothing 'in' the DMA yet, so try
+		 * to put two frames into it (at least one is available).
+		 */
+		if (write_sq.count == 1 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, the only existing frame is not
+			 * yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(1);
+		if (write_sq.count == 1) {
+			/* no more frames */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		if (write_sq.count == 2 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, there were two frames, but the second
+			 * one is not yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(2);
+	} else {
+		/* there's already a frame being played so we may only stuff
+		 * one new into the DMA, but even if this may be the last
+		 * frame existing the previous one is still on write_sq.count.
+		 */
+		if (write_sq.count == 2 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, the only existing frame is not
+			 * yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(2);
+	}
+	atari_enable_irq(IRQ_MFP_TIMA);
+}
+
+
+static irqreturn_t AtaInterrupt(int irq, void *dummy)
+{
+#if 0
+	/* ++TeSche: if you should want to test this... */
+	static int cnt;
+	if (write_sq.active == 2)
+		if (++cnt == 10) {
+			/* simulate losing an interrupt */
+			cnt = 0;
+			return IRQ_HANDLED;
+		}
+#endif
+	spin_lock(&dmasound.lock);
+	if (write_sq_ignore_int && is_falcon) {
+		/* ++TeSche: Falcon only: ignore first irq because it comes
+		 * immediately after starting a frame. after that, irqs come
+		 * (almost) like on the TT.
+		 */
+		write_sq_ignore_int = 0;
+		goto out;
+	}
+
+	if (!write_sq.active) {
+		/* playing was interrupted and sq_reset() has already cleared
+		 * the sq variables, so better don't do anything here.
+		 */
+		WAKE_UP(write_sq.sync_queue);
+		goto out;
+	}
+
+	/* Probably ;) one frame is finished. Well, in fact it may be that a
+	 * pre-programmed one is also finished because there has been a long
+	 * delay in interrupt delivery and we've completely lost one, but
+	 * there's no way to detect such a situation. In such a case the last
+	 * frame will be played more than once and the situation will recover
+	 * as soon as the irq gets through.
+	 */
+	write_sq.count--;
+	write_sq.active--;
+
+	if (!write_sq.active) {
+		tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+		write_sq_ignore_int = 1;
+	}
+
+	WAKE_UP(write_sq.action_queue);
+	/* At least one block of the queue is free now
+	   so wake up a writing process blocked because
+	   of a full queue. */
+
+	if ((write_sq.active != 1) || (write_sq.count != 1))
+		/* We must be a bit carefully here: write_sq.count indicates the
+		 * number of buffers used and not the number of frames to be
+		 * played. If write_sq.count==1 and write_sq.active==1 that
+		 * means the only remaining frame was already programmed
+		 * earlier (and is currently running) so we mustn't call
+		 * AtaPlay() here, otherwise we'll play one frame too much.
+		 */
+		AtaPlay();
+
+	if (!write_sq.active) WAKE_UP(write_sq.sync_queue);
+	/* We are not playing after AtaPlay(), so there
+	   is nothing to play any more. Wake up a process
+	   waiting for audio output to drain. */
+out:
+	spin_unlock(&dmasound.lock);
+	return IRQ_HANDLED;
+}
+
+
+/*** Mid level stuff *********************************************************/
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+#define RECLEVEL_VOXWARE_TO_GAIN(v)	\
+	((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20)
+#define RECLEVEL_GAIN_TO_VOXWARE(v)	(((v) * 20 + 2) / 3)
+
+
+static void __init TTMixerInit(void)
+{
+	atari_microwire_cmd(MW_LM1992_VOLUME(0));
+	dmasound.volume_left = 0;
+	atari_microwire_cmd(MW_LM1992_BALLEFT(0));
+	dmasound.volume_right = 0;
+	atari_microwire_cmd(MW_LM1992_BALRIGHT(0));
+	atari_microwire_cmd(MW_LM1992_TREBLE(0));
+	atari_microwire_cmd(MW_LM1992_BASS(0));
+}
+
+static void __init FalconMixerInit(void)
+{
+	dmasound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8;
+	dmasound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4;
+}
+
+static int AtaMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	unsigned long flags;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_SPEAKER:
+		    if (is_falcon || MACH_IS_TT) {
+			    int porta;
+			    spin_lock_irqsave(&dmasound.lock, flags);
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = sound_ym.rd_data_reg_sel;
+			    spin_unlock_irqrestore(&dmasound.lock, flags);
+			    return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100);
+		    }
+		    break;
+	    case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_volume(data));
+	    case SOUND_MIXER_WRITE_SPEAKER:
+		    if (is_falcon || MACH_IS_TT) {
+			    int porta;
+			    IOCTL_IN(arg, data);
+			    spin_lock_irqsave(&dmasound.lock, flags);
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+				    (data < 50 ? 0x40 : 0);
+			    sound_ym.wd_data = porta;
+			    spin_unlock_irqrestore(&dmasound.lock, flags);
+			    return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100);
+		    }
+	}
+	return -EINVAL;
+}
+
+
+static int TTMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_RECMASK:
+		return IOCTL_OUT(arg, 0);
+	    case SOUND_MIXER_READ_DEVMASK:
+		return IOCTL_OUT(arg,
+				 SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
+				 (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0));
+	    case SOUND_MIXER_READ_STEREODEVS:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
+	    case SOUND_MIXER_READ_VOLUME:
+		return IOCTL_OUT(arg,
+				 VOLUME_DB_TO_VOXWARE(dmasound.volume_left) |
+				 (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8));
+	    case SOUND_MIXER_READ_BASS:
+		return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.bass));
+	    case SOUND_MIXER_READ_TREBLE:
+		return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.treble));
+	    case SOUND_MIXER_READ_OGAIN:
+		return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(dmasound.gain));
+	    case SOUND_MIXER_WRITE_BASS:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_bass(data));
+	    case SOUND_MIXER_WRITE_TREBLE:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_treble(data));
+	    case SOUND_MIXER_WRITE_OGAIN:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_gain(data));
+	}
+	return AtaMixerIoctl(cmd, arg);
+}
+
+static int FalconMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_RECMASK:
+		return IOCTL_OUT(arg, SOUND_MASK_MIC);
+	    case SOUND_MIXER_READ_DEVMASK:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
+	    case SOUND_MIXER_READ_STEREODEVS:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
+	    case SOUND_MIXER_READ_VOLUME:
+		return IOCTL_OUT(arg,
+			VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
+			VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
+	    case SOUND_MIXER_READ_CAPS:
+		return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
+	    case SOUND_MIXER_WRITE_MIC:
+		IOCTL_IN(arg, data);
+		tt_dmasnd.input_gain =
+			RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
+			RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
+		/* fall thru, return set value */
+	    case SOUND_MIXER_READ_MIC:
+		return IOCTL_OUT(arg,
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);
+	}
+	return AtaMixerIoctl(cmd, arg);
+}
+
+static int AtaWriteSqSetup(void)
+{
+	write_sq_ignore_int = 0;
+	return 0 ;
+}
+
+static int AtaSqOpen(fmode_t mode)
+{
+	write_sq_ignore_int = 1;
+	return 0 ;
+}
+
+static int TTStateInfo(char *buffer, size_t space)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tvol left  %ddB [-40...  0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tvol right %ddB [-40...  0]\n",
+		       dmasound.volume_right);
+	len += sprintf(buffer+len, "\tbass      %ddB [-12...+12]\n",
+		       dmasound.bass);
+	len += sprintf(buffer+len, "\ttreble    %ddB [-12...+12]\n",
+		       dmasound.treble);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
+	return len;
+}
+
+static int FalconStateInfo(char *buffer, size_t space)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tvol left  %ddB [-22.5 ... 0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tvol right %ddB [-22.5 ... 0]\n",
+		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
+	return len;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+static SETTINGS def_hard_falcon = {
+	.format		= AFMT_S8,
+	.stereo		= 0,
+	.size		= 8,
+	.speed		= 8195
+} ;
+
+static SETTINGS def_hard_tt = {
+	.format	= AFMT_S8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 12517
+} ;
+
+static SETTINGS def_soft = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static __initdata MACHINE machTT = {
+	.name		= "Atari",
+	.name2		= "TT",
+	.owner		= THIS_MODULE,
+	.dma_alloc	= AtaAlloc,
+	.dma_free	= AtaFree,
+	.irqinit	= AtaIrqInit,
+#ifdef MODULE
+	.irqcleanup	= AtaIrqCleanUp,
+#endif /* MODULE */
+	.init		= TTInit,
+	.silence	= TTSilence,
+	.setFormat	= TTSetFormat,
+	.setVolume	= TTSetVolume,
+	.setBass	= AtaSetBass,
+	.setTreble	= AtaSetTreble,
+	.setGain	= TTSetGain,
+	.play		= AtaPlay,
+	.mixer_init	= TTMixerInit,
+	.mixer_ioctl	= TTMixerIoctl,
+	.write_sq_setup	= AtaWriteSqSetup,
+	.sq_open	= AtaSqOpen,
+	.state_info	= TTStateInfo,
+	.min_dsp_speed	= 6258,
+	.version	= ((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	.hardware_afmts	= AFMT_S8,  /* h'ware-supported formats *only* here */
+	.capabilities	=  DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
+};
+
+static __initdata MACHINE machFalcon = {
+	.name		= "Atari",
+	.name2		= "FALCON",
+	.dma_alloc	= AtaAlloc,
+	.dma_free	= AtaFree,
+	.irqinit	= AtaIrqInit,
+#ifdef MODULE
+	.irqcleanup	= AtaIrqCleanUp,
+#endif /* MODULE */
+	.init		= FalconInit,
+	.silence	= FalconSilence,
+	.setFormat	= FalconSetFormat,
+	.setVolume	= FalconSetVolume,
+	.setBass	= AtaSetBass,
+	.setTreble	= AtaSetTreble,
+	.play		= AtaPlay,
+	.mixer_init	= FalconMixerInit,
+	.mixer_ioctl	= FalconMixerIoctl,
+	.write_sq_setup	= AtaWriteSqSetup,
+	.sq_open	= AtaSqOpen,
+	.state_info	= FalconStateInfo,
+	.min_dsp_speed	= 8195,
+	.version	= ((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	.hardware_afmts	= (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+	.capabilities	=  DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init dmasound_atari_init(void)
+{
+	if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) {
+	    if (ATARIHW_PRESENT(CODEC)) {
+		dmasound.mach = machFalcon;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_falcon ;
+		is_falcon = 1;
+	    } else if (ATARIHW_PRESENT(MICROWIRE)) {
+		dmasound.mach = machTT;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_tt ;
+		is_falcon = 0;
+	    } else
+		return -ENODEV;
+	    if ((st_mfp.int_en_a & st_mfp.int_mk_a & 0x20) == 0)
+		return dmasound_init();
+	    else {
+		printk("DMA sound driver: Timer A interrupt already in use\n");
+		return -EBUSY;
+	    }
+	}
+	return -ENODEV;
+}
+
+static void __exit dmasound_atari_cleanup(void)
+{
+	dmasound_deinit();
+}
+
+module_init(dmasound_atari_init);
+module_exit(dmasound_atari_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
new file mode 100644
index 00000000..c918313c
--- /dev/null
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -0,0 +1,1589 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_core.c
+ *
+ *
+ *  OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for
+ *  Linux/m68k
+ *  Extended to support Power Macintosh for Linux/ppc by Paul Mackerras
+ *
+ *  (c) 1995 by Michael Schlueter & Michael Marte
+ *
+ *  Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS
+ *  interface and the u-law to signed byte conversion.
+ *
+ *  Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue,
+ *  /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like
+ *  to thank:
+ *    - Michael Schlueter for initial ideas and documentation on the MFP and
+ *	the DMA sound hardware.
+ *    - Therapy? for their CD 'Troublegum' which really made me rock.
+ *
+ *  /dev/sndstat is based on code by Hannu Savolainen, the author of the
+ *  VoxWare family of drivers.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ *
+ *  History:
+ *
+ *	1995/8/25	First release
+ *
+ *	1995/9/02	Roman Hodek:
+ *			  - Fixed atari_stram_alloc() call, the timer
+ *			    programming and several race conditions
+ *	1995/9/14	Roman Hodek:
+ *			  - After some discussion with Michael Schlueter,
+ *			    revised the interrupt disabling
+ *			  - Slightly speeded up U8->S8 translation by using
+ *			    long operations where possible
+ *			  - Added 4:3 interpolation for /dev/audio
+ *
+ *	1995/9/20	Torsten Scherer:
+ *			  - Fixed a bug in sq_write and changed /dev/audio
+ *			    converting to play at 12517Hz instead of 6258Hz.
+ *
+ *	1995/9/23	Torsten Scherer:
+ *			  - Changed sq_interrupt() and sq_play() to pre-program
+ *			    the DMA for another frame while there's still one
+ *			    running. This allows the IRQ response to be
+ *			    arbitrarily delayed and playing will still continue.
+ *
+ *	1995/10/14	Guenther Kelleter, Torsten Scherer:
+ *			  - Better support for Falcon audio (the Falcon doesn't
+ *			    raise an IRQ at the end of a frame, but at the
+ *			    beginning instead!). uses 'if (codec_dma)' in lots
+ *			    of places to simply switch between Falcon and TT
+ *			    code.
+ *
+ *	1995/11/06	Torsten Scherer:
+ *			  - Started introducing a hardware abstraction scheme
+ *			    (may perhaps also serve for Amigas?)
+ *			  - Can now play samples at almost all frequencies by
+ *			    means of a more generalized expand routine
+ *			  - Takes a good deal of care to cut data only at
+ *			    sample sizes
+ *			  - Buffer size is now a kernel runtime option
+ *			  - Implemented fsync() & several minor improvements
+ *			Guenther Kelleter:
+ *			  - Useful hints and bug fixes
+ *			  - Cross-checked it for Falcons
+ *
+ *	1996/3/9	Geert Uytterhoeven:
+ *			  - Support added for Amiga, A-law, 16-bit little
+ *			    endian.
+ *			  - Unification to drivers/sound/dmasound.c.
+ *
+ *	1996/4/6	Martin Mitchell:
+ *			  - Updated to 1.3 kernel.
+ *
+ *	1996/6/13       Topi Kanerva:
+ *			  - Fixed things that were broken (mainly the amiga
+ *			    14-bit routines)
+ *			  - /dev/sndstat shows now the real hardware frequency
+ *			  - The lowpass filter is disabled by default now
+ *
+ *	1996/9/25	Geert Uytterhoeven:
+ *			  - Modularization
+ *
+ *	1998/6/10	Andreas Schwab:
+ *			  - Converted to use sound_core
+ *
+ *	1999/12/28	Richard Zidlicky:
+ *			  - Added support for Q40
+ *
+ *	2000/2/27	Geert Uytterhoeven:
+ *			  - Clean up and split the code into 4 parts:
+ *			      o dmasound_core: machine-independent code
+ *			      o dmasound_atari: Atari TT and Falcon support
+ *			      o dmasound_awacs: Apple PowerMac support
+ *			      o dmasound_paula: Amiga support
+ *
+ *	2000/3/25	Geert Uytterhoeven:
+ *			  - Integration of dmasound_q40
+ *			  - Small clean ups
+ *
+ *	2001/01/26 [1.0] Iain Sandoe
+ *			  - make /dev/sndstat show revision & edition info.
+ *			  - since dmasound.mach.sq_setup() can fail on pmac
+ *			    its type has been changed to int and the returns
+ *			    are checked.
+ *		   [1.1]  - stop missing translations from being called.
+ *	2001/02/08 [1.2]  - remove unused translation tables & move machine-
+ *			    specific tables to low-level.
+ *			  - return correct info. for SNDCTL_DSP_GETFMTS.
+ *		   [1.3]  - implement SNDCTL_DSP_GETCAPS fully.
+ *		   [1.4]  - make /dev/sndstat text length usage deterministic.
+ *			  - make /dev/sndstat call to low-level
+ *			    dmasound.mach.state_info() pass max space to ll driver.
+ *			  - tidy startup banners and output info.
+ *		   [1.5]  - tidy up a little (removed some unused #defines in
+ *			    dmasound.h)
+ *			  - fix up HAS_RECORD conditionalisation.
+ *			  - add record code in places it is missing...
+ *			  - change buf-sizes to bytes to allow < 1kb for pmac
+ *			    if user param entry is < 256 the value is taken to
+ *			    be in kb > 256 is taken to be in bytes.
+ *			  - make default buff/frag params conditional on
+ *			    machine to allow smaller values for pmac.
+ *			  - made the ioctls, read & write comply with the OSS
+ *			    rules on setting params.
+ *			  - added parsing of _setup() params for record.
+ *	2001/04/04 [1.6]  - fix bug where sample rates higher than maximum were
+ *			    being reported as OK.
+ *			  - fix open() to return -EBUSY as per OSS doc. when
+ *			    audio is in use - this is independent of O_NOBLOCK.
+ *			  - fix bug where SNDCTL_DSP_POST was blocking.
+ */
+
+ /* Record capability notes 30/01/2001:
+  * At present these observations apply only to pmac LL driver (the only one
+  * that can do record, at present).  However, if other LL drivers for machines
+  * with record are added they may apply.
+  *
+  * The fragment parameters for the record and play channels are separate.
+  * However, if the driver is opened O_RDWR there is no way (in the current OSS
+  * API) to specify their values independently for the record and playback
+  * channels.  Since the only common factor between the input & output is the
+  * sample rate (on pmac) it should be possible to open /dev/dspX O_WRONLY and
+  * /dev/dspY O_RDONLY.  The input & output channels could then have different
+  * characteristics (other than the first that sets sample rate claiming the
+  * right to set it for ever).  As it stands, the format, channels, number of
+  * bits & sample rate are assumed to be common.  In the future perhaps these
+  * should be the responsibility of the LL driver - and then if a card really
+  * does not share items between record & playback they can be specified
+  * separately.
+*/
+
+/* Thread-safeness of shared_resources notes: 31/01/2001
+ * If the user opens O_RDWR and then splits record & play between two threads
+ * both of which inherit the fd - and then starts changing things from both
+ * - we will have difficulty telling.
+ *
+ * It's bad application coding - but ...
+ * TODO: think about how to sort this out... without bogging everything down in
+ * semaphores.
+ *
+ * Similarly, the OSS spec says "all changes to parameters must be between
+ * open() and the first read() or write(). - and a bit later on (by
+ * implication) "between SNDCTL_DSP_RESET and the first read() or write() after
+ * it".  If the app is multi-threaded and this rule is broken between threads
+ * we will have trouble spotting it - and the fault will be rather obscure :-(
+ *
+ * We will try and put out at least a kmsg if we see it happen... but I think
+ * it will be quite hard to trap it with an -EXXX return... because we can't
+ * see the fault until after the damage is done.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sound.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_CORE_REVISION 1
+#define DMASOUND_CORE_EDITION 6
+
+    /*
+     *  Declarations
+     */
+
+static DEFINE_MUTEX(dmasound_core_mutex);
+int dmasound_catchRadius = 0;
+module_param(dmasound_catchRadius, int, 0);
+
+static unsigned int numWriteBufs = DEFAULT_N_BUFFERS;
+module_param(numWriteBufs, int, 0);
+static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ;	/* in bytes */
+module_param(writeBufSize, int, 0);
+
+MODULE_LICENSE("GPL");
+
+#ifdef MODULE
+static int sq_unit = -1;
+static int mixer_unit = -1;
+static int state_unit = -1;
+static int irq_installed;
+#endif /* MODULE */
+
+/* control over who can modify resources shared between play/record */
+static fmode_t shared_resource_owner;
+static int shared_resources_initialised;
+
+    /*
+     *  Mid level stuff
+     */
+
+struct sound_settings dmasound = {
+	.lock = __SPIN_LOCK_UNLOCKED(dmasound.lock)
+};
+
+static inline void sound_silence(void)
+{
+	dmasound.mach.silence(); /* _MUST_ stop DMA */
+}
+
+static inline int sound_set_format(int format)
+{
+	return dmasound.mach.setFormat(format);
+}
+
+
+static int sound_set_speed(int speed)
+{
+	if (speed < 0)
+		return dmasound.soft.speed;
+
+	/* trap out-of-range speed settings.
+	   at present we allow (arbitrarily) low rates - using soft
+	   up-conversion - but we can't allow > max because there is
+	   no soft down-conversion.
+	*/
+	if (dmasound.mach.max_dsp_speed &&
+	   (speed > dmasound.mach.max_dsp_speed))
+		speed = dmasound.mach.max_dsp_speed ;
+
+	dmasound.soft.speed = speed;
+
+	if (dmasound.minDev == SND_DEV_DSP)
+		dmasound.dsp.speed = dmasound.soft.speed;
+
+	return dmasound.soft.speed;
+}
+
+static int sound_set_stereo(int stereo)
+{
+	if (stereo < 0)
+		return dmasound.soft.stereo;
+
+	stereo = !!stereo;    /* should be 0 or 1 now */
+
+	dmasound.soft.stereo = stereo;
+	if (dmasound.minDev == SND_DEV_DSP)
+		dmasound.dsp.stereo = stereo;
+
+	return stereo;
+}
+
+static ssize_t sound_copy_translate(TRANS *trans, const u_char __user *userPtr,
+				    size_t userCount, u_char frame[],
+				    ssize_t *frameUsed, ssize_t frameLeft)
+{
+	ssize_t (*ct_func)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+
+	switch (dmasound.soft.format) {
+	    case AFMT_MU_LAW:
+		ct_func = trans->ct_ulaw;
+		break;
+	    case AFMT_A_LAW:
+		ct_func = trans->ct_alaw;
+		break;
+	    case AFMT_S8:
+		ct_func = trans->ct_s8;
+		break;
+	    case AFMT_U8:
+		ct_func = trans->ct_u8;
+		break;
+	    case AFMT_S16_BE:
+		ct_func = trans->ct_s16be;
+		break;
+	    case AFMT_U16_BE:
+		ct_func = trans->ct_u16be;
+		break;
+	    case AFMT_S16_LE:
+		ct_func = trans->ct_s16le;
+		break;
+	    case AFMT_U16_LE:
+		ct_func = trans->ct_u16le;
+		break;
+	    default:
+		return 0;
+	}
+	/* if the user has requested a non-existent translation don't try
+	   to call it but just return 0 bytes moved
+	*/
+	if (ct_func)
+		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	return 0;
+}
+
+    /*
+     *  /dev/mixer abstraction
+     */
+
+static struct {
+    int busy;
+    int modify_counter;
+} mixer;
+
+static int mixer_open(struct inode *inode, struct file *file)
+{
+	mutex_lock(&dmasound_core_mutex);
+	if (!try_module_get(dmasound.mach.owner)) {
+		mutex_unlock(&dmasound_core_mutex);
+		return -ENODEV;
+	}
+	mixer.busy = 1;
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+}
+
+static int mixer_release(struct inode *inode, struct file *file)
+{
+	mutex_lock(&dmasound_core_mutex);
+	mixer.busy = 0;
+	module_put(dmasound.mach.owner);
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+}
+
+static int mixer_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+	    mixer.modify_counter++;
+	switch (cmd) {
+	    case OSS_GETVERSION:
+		return IOCTL_OUT(arg, SOUND_VERSION);
+	    case SOUND_MIXER_INFO:
+		{
+		    mixer_info info;
+		    memset(&info, 0, sizeof(info));
+		    strlcpy(info.id, dmasound.mach.name2, sizeof(info.id));
+		    strlcpy(info.name, dmasound.mach.name2, sizeof(info.name));
+		    info.modify_counter = mixer.modify_counter;
+		    if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+			    return -EFAULT;
+		    return 0;
+		}
+	}
+	if (dmasound.mach.mixer_ioctl)
+	    return dmasound.mach.mixer_ioctl(cmd, arg);
+	return -EINVAL;
+}
+
+static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	mutex_lock(&dmasound_core_mutex);
+	ret = mixer_ioctl(file, cmd, arg);
+	mutex_unlock(&dmasound_core_mutex);
+
+	return ret;
+}
+
+static const struct file_operations mixer_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.unlocked_ioctl	= mixer_unlocked_ioctl,
+	.open		= mixer_open,
+	.release	= mixer_release,
+};
+
+static void mixer_init(void)
+{
+#ifndef MODULE
+	int mixer_unit;
+#endif
+	mixer_unit = register_sound_mixer(&mixer_fops, -1);
+	if (mixer_unit < 0)
+		return;
+
+	mixer.busy = 0;
+	dmasound.treble = 0;
+	dmasound.bass = 0;
+	if (dmasound.mach.mixer_init)
+	    dmasound.mach.mixer_init();
+}
+
+
+    /*
+     *  Sound queue stuff, the heart of the driver
+     */
+
+struct sound_queue dmasound_write_sq;
+static void sq_reset_output(void) ;
+
+static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
+{
+	int i;
+
+	if (sq->buffers)
+		return 0;
+	sq->numBufs = num;
+	sq->bufSize = size;
+	sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL);
+	if (!sq->buffers)
+		return -ENOMEM;
+	for (i = 0; i < num; i++) {
+		sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL);
+		if (!sq->buffers[i]) {
+			while (i--)
+				dmasound.mach.dma_free(sq->buffers[i], size);
+			kfree(sq->buffers);
+			sq->buffers = NULL;
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+static void sq_release_buffers(struct sound_queue *sq)
+{
+	int i;
+
+	if (sq->buffers) {
+		for (i = 0; i < sq->numBufs; i++)
+			dmasound.mach.dma_free(sq->buffers[i], sq->bufSize);
+		kfree(sq->buffers);
+		sq->buffers = NULL;
+	}
+}
+
+
+static int sq_setup(struct sound_queue *sq)
+{
+	int (*setup_func)(void) = NULL;
+	int hard_frame ;
+
+	if (sq->locked) { /* are we already set? - and not changeable */
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to sq_setup a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+	sq->locked = 1 ; /* don't think we have a race prob. here _check_ */
+
+	/* make sure that the parameters are set up
+	   This should have been done already...
+	*/
+
+	dmasound.mach.init();
+
+	/* OK.  If the user has set fragment parameters explicitly, then we
+	   should leave them alone... as long as they are valid.
+	   Invalid user fragment params can occur if we allow the whole buffer
+	   to be used when the user requests the fragments sizes (with no soft
+	   x-lation) and then the user subsequently sets a soft x-lation that
+	   requires increased internal buffering.
+
+	   Othwerwise (if the user did not set them) OSS says that we should
+	   select frag params on the basis of 0.5 s output & 0.1 s input
+	   latency. (TODO.  For now we will copy in the defaults.)
+	*/
+
+	if (sq->user_frags <= 0) {
+		sq->max_count = sq->numBufs ;
+		sq->max_active = sq->numBufs ;
+		sq->block_size = sq->bufSize;
+		/* set up the user info */
+		sq->user_frags = sq->numBufs ;
+		sq->user_frag_size = sq->bufSize ;
+		sq->user_frag_size *=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		sq->user_frag_size /=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+	} else {
+		/* work out requested block size */
+		sq->block_size = sq->user_frag_size ;
+		sq->block_size *=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+		sq->block_size /=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		/* the user wants to write frag-size chunks */
+		sq->block_size *= dmasound.hard.speed ;
+		sq->block_size /= dmasound.soft.speed ;
+		/* this only works for size values which are powers of 2 */
+		hard_frame =
+			(dmasound.hard.size * (dmasound.hard.stereo+1))/8 ;
+		sq->block_size +=  (hard_frame - 1) ;
+		sq->block_size &= ~(hard_frame - 1) ; /* make sure we are aligned */
+		/* let's just check for obvious mistakes */
+		if ( sq->block_size <= 0 || sq->block_size > sq->bufSize) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag size (user set %d)\n", sq->user_frag_size) ;
+#endif
+			sq->block_size = sq->bufSize ;
+		}
+		if ( sq->user_frags <= sq->numBufs ) {
+			sq->max_count = sq->user_frags ;
+			/* if user has set max_active - then use it */
+			sq->max_active = (sq->max_active <= sq->max_count) ?
+				sq->max_active : sq->max_count ;
+		} else {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;
+#endif
+			sq->max_count =
+			sq->max_active = sq->numBufs ;
+		}
+	}
+	sq->front = sq->count = sq->rear_size = 0;
+	sq->syncing = 0;
+	sq->active = 0;
+
+	if (sq == &write_sq) {
+	    sq->rear = -1;
+	    setup_func = dmasound.mach.write_sq_setup;
+	}
+	if (setup_func)
+	    return setup_func();
+	return 0 ;
+}
+
+static inline void sq_play(void)
+{
+	dmasound.mach.play();
+}
+
+static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
+			loff_t *ppos)
+{
+	ssize_t uWritten = 0;
+	u_char *dest;
+	ssize_t uUsed = 0, bUsed, bLeft;
+	unsigned long flags ;
+
+	/* ++TeSche: Is something like this necessary?
+	 * Hey, that's an honest question! Or does any other part of the
+	 * filesystem already checks this situation? I really don't know.
+	 */
+	if (uLeft == 0)
+		return 0;
+
+	/* implement any changes we have made to the soft/hard params.
+	   this is not satisfactory really, all we have done up to now is to
+	   say what we would like - there hasn't been any real checking of capability
+	*/
+
+	if (shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. This may seem a dumb place
+	   to do it - but it is what OSS requires.  It means that write() can
+	   return memory allocation errors.  To avoid this possibility use the
+	   GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the
+	   params you want to change) - these ioctls also force the setup.
+	*/
+
+	if (write_sq.locked == 0) {
+		if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ;
+		uWritten = 0 ;
+	}
+
+/* FIXME: I think that this may be the wrong behaviour when we get strapped
+	for time and the cpu is close to being (or actually) behind in sending data.
+	- because we've lost the time that the N samples, already in the buffer,
+	would have given us to get here with the next lot from the user.
+*/
+	/* The interrupt doesn't start to play the last, incomplete frame.
+	 * Thus we can append to it without disabling the interrupts! (Note
+	 * also that write_sq.rear isn't affected by the interrupt.)
+	 */
+
+	/* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued:
+	   this will mimic the behaviour of syncing and allow the sq_play() to
+	   queue a partial fragment.  Since sq_play() may/will be called from
+	   the IRQ handler - at least on Pmac we have to deal with it.
+	   The strategy - possibly not optimum - is to kill _POST status if we
+	   get here.  This seems, at least, reasonable - in the sense that POST
+	   is supposed to indicate that we might not write before the queue
+	   is drained - and if we get here in time then it does not apply.
+	*/
+
+	spin_lock_irqsave(&dmasound.lock, flags);
+	write_sq.syncing &= ~2 ; /* take out POST status */
+	spin_unlock_irqrestore(&dmasound.lock, flags);
+
+	if (write_sq.count > 0 &&
+	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {
+		dest = write_sq.buffers[write_sq.rear];
+		bUsed = write_sq.rear_size;
+		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
+					     dest, &bUsed, bLeft);
+		if (uUsed <= 0)
+			return uUsed;
+		src += uUsed;
+		uWritten += uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
+		write_sq.rear_size = bUsed;
+	}
+
+	while (uLeft) {
+		while (write_sq.count >= write_sq.max_active) {
+			sq_play();
+			if (write_sq.non_blocking)
+				return uWritten > 0 ? uWritten : -EAGAIN;
+			SLEEP(write_sq.action_queue);
+			if (signal_pending(current))
+				return uWritten > 0 ? uWritten : -EINTR;
+		}
+
+		/* Here, we can avoid disabling the interrupt by first
+		 * copying and translating the data, and then updating
+		 * the write_sq variables. Until this is done, the interrupt
+		 * won't see the new frame and we can work on it
+		 * undisturbed.
+		 */
+
+		dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count];
+		bUsed = 0;
+		bLeft = write_sq.block_size;
+		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
+					     dest, &bUsed, bLeft);
+		if (uUsed <= 0)
+			break;
+		src += uUsed;
+		uWritten += uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
+		if (bUsed) {
+			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;
+			write_sq.rear_size = bUsed;
+			write_sq.count++;
+		}
+	} /* uUsed may have been 0 */
+
+	sq_play();
+
+	return uUsed < 0? uUsed: uWritten;
+}
+
+static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	int retVal;
+	
+	if (write_sq.locked == 0) {
+		if ((retVal = sq_setup(&write_sq)) < 0)
+			return retVal;
+		return 0;
+	}
+	if (file->f_mode & FMODE_WRITE )
+		poll_wait(file, &write_sq.action_queue, wait);
+	if (file->f_mode & FMODE_WRITE)
+		if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
+			mask |= POLLOUT | POLLWRNORM;
+	return mask;
+
+}
+
+static inline void sq_init_waitqueue(struct sound_queue *sq)
+{
+	init_waitqueue_head(&sq->action_queue);
+	init_waitqueue_head(&sq->open_queue);
+	init_waitqueue_head(&sq->sync_queue);
+	sq->busy = 0;
+}
+
+#if 0 /* blocking open() */
+static inline void sq_wake_up(struct sound_queue *sq, struct file *file,
+			      fmode_t mode)
+{
+	if (file->f_mode & mode) {
+		sq->busy = 0; /* CHECK: IS THIS OK??? */
+		WAKE_UP(sq->open_queue);
+	}
+}
+#endif
+
+static int sq_open2(struct sound_queue *sq, struct file *file, fmode_t mode,
+		    int numbufs, int bufsize)
+{
+	int rc = 0;
+
+	if (file->f_mode & mode) {
+		if (sq->busy) {
+#if 0 /* blocking open() */
+			rc = -EBUSY;
+			if (file->f_flags & O_NONBLOCK)
+				return rc;
+			rc = -EINTR;
+			while (sq->busy) {
+				SLEEP(sq->open_queue);
+				if (signal_pending(current))
+					return rc;
+			}
+			rc = 0;
+#else
+			/* OSS manual says we will return EBUSY regardless
+			   of O_NOBLOCK.
+			*/
+			return -EBUSY ;
+#endif
+		}
+		sq->busy = 1; /* Let's play spot-the-race-condition */
+
+		/* allocate the default number & size of buffers.
+		   (i.e. specified in _setup() or as module params)
+		   can't be changed at the moment - but _could_ be perhaps
+		   in the setfragments ioctl.
+		*/
+		if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {
+#if 0 /* blocking open() */
+			sq_wake_up(sq, file, mode);
+#else
+			sq->busy = 0 ;
+#endif
+			return rc;
+		}
+
+		sq->non_blocking = file->f_flags & O_NONBLOCK;
+	}
+	return rc;
+}
+
+#define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)
+#if 0 /* blocking open() */
+#define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)
+#endif
+#define write_sq_release_buffers()	sq_release_buffers(&write_sq)
+#define write_sq_open(file)	\
+	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )
+
+static int sq_open(struct inode *inode, struct file *file)
+{
+	int rc;
+
+	mutex_lock(&dmasound_core_mutex);
+	if (!try_module_get(dmasound.mach.owner)) {
+		mutex_unlock(&dmasound_core_mutex);
+		return -ENODEV;
+	}
+
+	rc = write_sq_open(file); /* checks the f_mode */
+	if (rc)
+		goto out;
+	if (file->f_mode & FMODE_READ) {
+		/* TODO: if O_RDWR, release any resources grabbed by write part */
+		rc = -ENXIO ; /* I think this is what is required by open(2) */
+		goto out;
+	}
+
+	if (dmasound.mach.sq_open)
+	    dmasound.mach.sq_open(file->f_mode);
+
+	/* CHECK whether this is sensible - in the case that dsp0 could be opened
+	  O_RDONLY and dsp1 could be opened O_WRONLY
+	*/
+
+	dmasound.minDev = iminor(inode) & 0x0f;
+
+	/* OK. - we should make some attempt at consistency. At least the H'ware
+	   options should be set with a valid mode.  We will make it that the LL
+	   driver must supply defaults for hard & soft params.
+	*/
+
+	if (shared_resource_owner == 0) {
+		/* you can make this AFMT_U8/mono/8K if you want to mimic old
+		   OSS behaviour - while we still have soft translations ;-) */
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+#ifndef DMASOUND_STRICT_OSS_COMPLIANCE
+	/* none of the current LL drivers can actually do this "native" at the moment
+	   OSS does not really require us to supply /dev/audio if we can't do it.
+	*/
+	if (dmasound.minDev == SND_DEV_AUDIO) {
+		sound_set_speed(8000);
+		sound_set_stereo(0);
+		sound_set_format(AFMT_MU_LAW);
+	}
+#endif
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+ out:
+	module_put(dmasound.mach.owner);
+	mutex_unlock(&dmasound_core_mutex);
+	return rc;
+}
+
+static void sq_reset_output(void)
+{
+	sound_silence(); /* this _must_ stop DMA, we might be about to lose the buffers */
+	write_sq.active = 0;
+	write_sq.count = 0;
+	write_sq.rear_size = 0;
+	/* write_sq.front = (write_sq.rear+1) % write_sq.max_count;*/
+	write_sq.front = 0 ;
+	write_sq.rear = -1 ; /* same as for set-up */
+
+	/* OK - we can unlock the parameters and fragment settings */
+	write_sq.locked = 0 ;
+	write_sq.user_frags = 0 ;
+	write_sq.user_frag_size = 0 ;
+}
+
+static void sq_reset(void)
+{
+	sq_reset_output() ;
+	/* we could consider resetting the shared_resources_owner here... but I
+	   think it is probably still rather non-obvious to application writer
+	*/
+
+	/* we release everything else though */
+	shared_resources_initialised = 0 ;
+}
+
+static int sq_fsync(struct file *filp, struct dentry *dentry)
+{
+	int rc = 0;
+	int timeout = 5;
+
+	write_sq.syncing |= 1;
+	sq_play();	/* there may be an incomplete frame waiting */
+
+	while (write_sq.active) {
+		SLEEP(write_sq.sync_queue);
+		if (signal_pending(current)) {
+			/* While waiting for audio output to drain, an
+			 * interrupt occurred.  Stop audio output immediately
+			 * and clear the queue. */
+			sq_reset_output();
+			rc = -EINTR;
+			break;
+		}
+		if (!--timeout) {
+			printk(KERN_WARNING "dmasound: Timeout draining output\n");
+			sq_reset_output();
+			rc = -EIO;
+			break;
+		}
+	}
+
+	/* flag no sync regardless of whether we had a DSP_POST or not */
+	write_sq.syncing = 0 ;
+	return rc;
+}
+
+static int sq_release(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+
+	mutex_lock(&dmasound_core_mutex);
+
+	if (file->f_mode & FMODE_WRITE) {
+		if (write_sq.busy)
+			rc = sq_fsync(file, file->f_path.dentry);
+
+		sq_reset_output() ; /* make sure dma is stopped and all is quiet */
+		write_sq_release_buffers();
+		write_sq.busy = 0;
+	}
+
+	if (file->f_mode & shared_resource_owner) { /* it's us that has them */
+		shared_resource_owner = 0 ;
+		shared_resources_initialised = 0 ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+	module_put(dmasound.mach.owner);
+
+#if 0 /* blocking open() */
+	/* Wake up a process waiting for the queue being released.
+	 * Note: There may be several processes waiting for a call
+	 * to open() returning. */
+
+	/* Iain: hmm I don't understand this next comment ... */
+	/* There is probably a DOS atack here. They change the mode flag. */
+	/* XXX add check here,*/
+	read_sq_wake_up(file); /* checks f_mode */
+	write_sq_wake_up(file); /* checks f_mode */
+#endif /* blocking open() */
+
+	mutex_unlock(&dmasound_core_mutex);
+
+	return rc;
+}
+
+/* here we see if we have a right to modify format, channels, size and so on
+   if no-one else has claimed it already then we do...
+
+   TODO: We might change this to mask O_RDWR such that only one or the other channel
+   is the owner - if we have problems.
+*/
+
+static int shared_resources_are_mine(fmode_t md)
+{
+	if (shared_resource_owner)
+		return (shared_resource_owner & md) != 0;
+	else {
+		shared_resource_owner = md ;
+		return 1 ;
+	}
+}
+
+/* if either queue is locked we must deny the right to change shared params
+*/
+
+static int queues_are_quiescent(void)
+{
+	if (write_sq.locked)
+		return 0 ;
+	return 1 ;
+}
+
+/* check and set a queue's fragments per user's wishes...
+   we will check against the pre-defined literals and the actual sizes.
+   This is a bit fraught - because soft translations can mess with our
+   buffer requirements *after* this call - OSS says "call setfrags first"
+*/
+
+/* It is possible to replace all the -EINVAL returns with an override that
+   just puts the allowable value in.  This may be what many OSS apps require
+*/
+
+static int set_queue_frags(struct sound_queue *sq, int bufs, int size)
+{
+	if (sq->locked) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to set_queue_frags on a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+
+	if ((size < MIN_FRAG_SIZE) || (size > MAX_FRAG_SIZE))
+		return -EINVAL ;
+	size = (1<<size) ; /* now in bytes */
+	if (size > sq->bufSize)
+		return -EINVAL ; /* this might still not work */
+
+	if (bufs <= 0)
+		return -EINVAL ;
+	if (bufs > sq->numBufs) /* the user is allowed say "don't care" with 0x7fff */
+		bufs = sq->numBufs ;
+
+	/* there is, currently, no way to specify max_active separately
+	   from max_count.  This could be a LL driver issue - I guess
+	   if there is a requirement for these values to be different then
+	  we will have to pass that info. up to this level.
+	*/
+	sq->user_frags =
+	sq->max_active = bufs ;
+	sq->user_frag_size = size ;
+
+	return 0 ;
+}
+
+static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int val, result;
+	u_long fmt;
+	int data;
+	int size, nbufs;
+	audio_buf_info info;
+
+	switch (cmd) {
+	case SNDCTL_DSP_RESET:
+		sq_reset();
+		return 0;
+		break ;
+	case SNDCTL_DSP_GETFMTS:
+		fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
+		return IOCTL_OUT(arg, fmt);
+		break ;
+	case SNDCTL_DSP_GETBLKSIZE:
+		/* this should tell the caller about bytes that the app can
+		   read/write - the app doesn't care about our internal buffers.
+		   We force sq_setup() here as per OSS 1.1 (which should
+		   compute the values necessary).
+		   Since there is no mechanism to specify read/write separately, for
+		   fds opened O_RDWR, the write_sq values will, arbitrarily, overwrite
+		   the read_sq ones.
+		*/
+		size = 0 ;
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			size = write_sq.user_frag_size ;
+		}
+		return IOCTL_OUT(arg, size);
+		break ;
+	case SNDCTL_DSP_POST:
+		/* all we are going to do is to tell the LL that any
+		   partial frags can be queued for output.
+		   The LL will have to clear this flag when last output
+		   is queued.
+		*/
+		write_sq.syncing |= 0x2 ;
+		sq_play() ;
+		return 0 ;
+	case SNDCTL_DSP_SYNC:
+		/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
+		   except that it waits for output to finish before resetting
+		   everything - read, however, is killed immediately.
+		*/
+		result = 0 ;
+		if (file->f_mode & FMODE_WRITE) {
+			result = sq_fsync(file, file->f_path.dentry);
+			sq_reset_output() ;
+		}
+		/* if we are the shared resource owner then release them */
+		if (file->f_mode & shared_resource_owner)
+			shared_resources_initialised = 0 ;
+		return result ;
+		break ;
+	case SOUND_PCM_READ_RATE:
+		return IOCTL_OUT(arg, dmasound.soft.speed);
+	case SNDCTL_DSP_SPEED:
+		/* changing this on the fly will have weird effects on the sound.
+		   Where there are rate conversions implemented in soft form - it
+		   will cause the _ctx_xxx() functions to be substituted.
+		   However, there doesn't appear to be any reason to dis-allow it from
+		   a driver pov.
+		*/
+		if (shared_resources_are_mine(file->f_mode)) {
+			IOCTL_IN(arg, data);
+			data = sound_set_speed(data) ;
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, data);
+		} else
+			return -EINVAL ;
+		break ;
+	/* OSS says these next 4 actions are undefined when the device is
+	   busy/active - we will just return -EINVAL.
+	   To be allowed to change one - (a) you have to own the right
+	    (b) the queue(s) must be quiescent
+	*/
+	case SNDCTL_DSP_STEREO:
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data));
+		} else
+			return -EINVAL ;
+		break ;
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			/* the user might ask for 20 channels, we will return 1 or 2 */
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		} else
+			return -EINVAL ;
+		break ;
+	case SNDCTL_DSP_SETFMT:
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+		    	int format;
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			format = sound_set_format(data);
+			result = IOCTL_OUT(arg, format);
+			if (result < 0)
+				return result;
+			if (format != data && data != AFMT_QUERY)
+				return -EINVAL;
+			return 0;
+		} else
+			return -EINVAL ;
+	case SNDCTL_DSP_SUBDIVIDE:
+		return -EINVAL ;
+	case SNDCTL_DSP_SETFRAGMENT:
+		/* we can do this independently for the two queues - with the
+		   proviso that for fds opened O_RDWR we cannot separate the
+		   actions and both queues will be set per the last call.
+		   NOTE: this does *NOT* actually set the queue up - merely
+		   registers our intentions.
+		*/
+		IOCTL_IN(arg, data);
+		result = 0 ;
+		nbufs = (data >> 16) & 0x7fff ; /* 0x7fff is 'use maximum' */
+		size = data & 0xffff;
+		if (file->f_mode & FMODE_WRITE) {
+			result = set_queue_frags(&write_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+		/* NOTE: this return value is irrelevant - OSS specifically says that
+		   the value is 'random' and that the user _must_ check the actual
+		   frags values using SNDCTL_DSP_GETBLKSIZE or similar */
+		return IOCTL_OUT(arg, data);
+		break ;
+	case SNDCTL_DSP_GETOSPACE:
+		/*
+		*/
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			info.fragments = write_sq.max_active - write_sq.count;
+			info.fragstotal = write_sq.max_active;
+			info.fragsize = write_sq.user_frag_size;
+			info.bytes = info.fragments * info.fragsize;
+			if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
+	case SNDCTL_DSP_GETCAPS:
+		val = dmasound.mach.capabilities & 0xffffff00;
+		return IOCTL_OUT(arg,val);
+
+	default:
+		return mixer_ioctl(file, cmd, arg);
+	}
+	return -EINVAL;
+}
+
+static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	mutex_lock(&dmasound_core_mutex);
+	ret = sq_ioctl(file, cmd, arg);
+	mutex_unlock(&dmasound_core_mutex);
+
+	return ret;
+}
+
+static const struct file_operations sq_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= sq_write,
+	.poll		= sq_poll,
+	.unlocked_ioctl	= sq_unlocked_ioctl,
+	.open		= sq_open,
+	.release	= sq_release,
+};
+
+static int sq_init(void)
+{
+	const struct file_operations *fops = &sq_fops;
+#ifndef MODULE
+	int sq_unit;
+#endif
+
+	sq_unit = register_sound_dsp(fops, -1);
+	if (sq_unit < 0) {
+		printk(KERN_ERR "dmasound_core: couldn't register fops\n") ;
+		return sq_unit ;
+	}
+
+	write_sq_init_waitqueue();
+
+	/* These parameters will be restored for every clean open()
+	 * in the case of multiple open()s (e.g. dsp0 & dsp1) they
+	 * will be set so long as the shared resources have no owner.
+	 */
+
+	if (shared_resource_owner == 0) {
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		shared_resources_initialised = 0 ;
+	}
+	return 0 ;
+}
+
+
+    /*
+     *  /dev/sndstat
+     */
+
+/* we allow more space for record-enabled because there are extra output lines.
+   the number here must include the amount we are prepared to give to the low-level
+   driver.
+*/
+
+#define STAT_BUFF_LEN 768
+
+/* this is how much space we will allow the low-level driver to use
+   in the stat buffer.  Currently, 2 * (80 character line + <NL>).
+   We do not police this (it is up to the ll driver to be honest).
+*/
+
+#define LOW_LEVEL_STAT_ALLOC 162
+
+static struct {
+    int busy;
+    char buf[STAT_BUFF_LEN];	/* state.buf should not overflow! */
+    int len, ptr;
+} state;
+
+/* publish this function for use by low-level code, if required */
+
+static char *get_afmt_string(int afmt)
+{
+        switch(afmt) {
+            case AFMT_MU_LAW:
+                return "mu-law";
+                break;
+            case AFMT_A_LAW:
+                return "A-law";
+                break;
+            case AFMT_U8:
+                return "unsigned 8 bit";
+                break;
+            case AFMT_S8:
+                return "signed 8 bit";
+                break;
+            case AFMT_S16_BE:
+                return "signed 16 bit BE";
+                break;
+            case AFMT_U16_BE:
+                return "unsigned 16 bit BE";
+                break;
+            case AFMT_S16_LE:
+                return "signed 16 bit LE";
+                break;
+            case AFMT_U16_LE:
+                return "unsigned 16 bit LE";
+                break;
+	    case 0:
+		return "format not set" ;
+		break ;
+            default:
+                break ;
+        }
+        return "ERROR: Unsupported AFMT_XXXX code" ;
+}
+
+static int state_open(struct inode *inode, struct file *file)
+{
+	char *buffer = state.buf;
+	int len = 0;
+	int ret;
+
+	mutex_lock(&dmasound_core_mutex);
+	ret = -EBUSY;
+	if (state.busy)
+		goto out;
+
+	ret = -ENODEV;
+	if (!try_module_get(dmasound.mach.owner))
+		goto out;
+
+	state.ptr = 0;
+	state.busy = 1;
+
+	len += sprintf(buffer+len, "%sDMA sound driver rev %03d :\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	len += sprintf(buffer+len,
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+
+	/* call the low-level module to fill in any stat info. that it has
+	   if present.  Maximum buffer usage is specified.
+	*/
+
+	if (dmasound.mach.state_info)
+		len += dmasound.mach.state_info(buffer+len,
+			(size_t) LOW_LEVEL_STAT_ALLOC) ;
+
+	/* make usage of the state buffer as deterministic as poss.
+	   exceptional conditions could cause overrun - and this is flagged as
+	   a kernel error.
+	*/
+
+	/* formats and settings */
+
+	len += sprintf(buffer+len,"\t\t === Formats & settings ===\n") ;
+	len += sprintf(buffer+len,"Parameter %20s%20s\n","soft","hard") ;
+	len += sprintf(buffer+len,"Format   :%20s%20s\n",
+		get_afmt_string(dmasound.soft.format),
+		get_afmt_string(dmasound.hard.format));
+
+	len += sprintf(buffer+len,"Samp Rate:%14d s/sec%14d s/sec\n",
+		       dmasound.soft.speed, dmasound.hard.speed);
+
+	len += sprintf(buffer+len,"Channels :%20s%20s\n",
+		       dmasound.soft.stereo ? "stereo" : "mono",
+		       dmasound.hard.stereo ? "stereo" : "mono" );
+
+	/* sound queue status */
+
+	len += sprintf(buffer+len,"\t\t === Sound Queue status ===\n");
+	len += sprintf(buffer+len,"Allocated:%8s%6s\n","Buffers","Size") ;
+	len += sprintf(buffer+len,"%9s:%8d%6d\n",
+		"write", write_sq.numBufs, write_sq.bufSize) ;
+	len += sprintf(buffer+len,
+		"Current  : MaxFrg FragSiz MaxAct Frnt Rear "
+		"Cnt RrSize A B S L  xruns\n") ;
+	len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+		"write", write_sq.max_count, write_sq.block_size,
+		write_sq.max_active, write_sq.front, write_sq.rear,
+		write_sq.count, write_sq.rear_size, write_sq.active,
+		write_sq.busy, write_sq.syncing, write_sq.locked, write_sq.xruns) ;
+#ifdef DEBUG_DMASOUND
+printk("dmasound: stat buffer used %d bytes\n", len) ;
+#endif
+
+	if (len >= STAT_BUFF_LEN)
+		printk(KERN_ERR "dmasound_core: stat buffer overflowed!\n");
+
+	state.len = len;
+	ret = 0;
+out:
+	mutex_unlock(&dmasound_core_mutex);
+	return ret;
+}
+
+static int state_release(struct inode *inode, struct file *file)
+{
+	mutex_lock(&dmasound_core_mutex);
+	state.busy = 0;
+	module_put(dmasound.mach.owner);
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+}
+
+static ssize_t state_read(struct file *file, char __user *buf, size_t count,
+			  loff_t *ppos)
+{
+	int n = state.len - state.ptr;
+	if (n > count)
+		n = count;
+	if (n <= 0)
+		return 0;
+	if (copy_to_user(buf, &state.buf[state.ptr], n))
+		return -EFAULT;
+	state.ptr += n;
+	return n;
+}
+
+static const struct file_operations state_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= state_read,
+	.open		= state_open,
+	.release	= state_release,
+};
+
+static int state_init(void)
+{
+#ifndef MODULE
+	int state_unit;
+#endif
+	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
+	if (state_unit < 0)
+		return state_unit ;
+	state.busy = 0;
+	return 0 ;
+}
+
+
+    /*
+     *  Config & Setup
+     *
+     *  This function is called by _one_ chipset-specific driver
+     */
+
+int dmasound_init(void)
+{
+	int res ;
+#ifdef MODULE
+	if (irq_installed)
+		return -EBUSY;
+#endif
+
+	/* Set up sound queue, /dev/audio and /dev/dsp. */
+
+	/* Set default settings. */
+	if ((res = sq_init()) < 0)
+		return res ;
+
+	/* Set up /dev/sndstat. */
+	if ((res = state_init()) < 0)
+		return res ;
+
+	/* Set up /dev/mixer. */
+	mixer_init();
+
+	if (!dmasound.mach.irqinit()) {
+		printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
+		return -ENODEV;
+	}
+#ifdef MODULE
+	irq_installed = 1;
+#endif
+
+	printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	printk(KERN_INFO
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+	printk(KERN_INFO "Write will use %4d fragments of %7d bytes as default\n",
+		numWriteBufs, writeBufSize) ;
+	return 0;
+}
+
+#ifdef MODULE
+
+void dmasound_deinit(void)
+{
+	if (irq_installed) {
+		sound_silence();
+		dmasound.mach.irqcleanup();
+		irq_installed = 0;
+	}
+
+	write_sq_release_buffers();
+
+	if (mixer_unit >= 0)
+		unregister_sound_mixer(mixer_unit);
+	if (state_unit >= 0)
+		unregister_sound_special(state_unit);
+	if (sq_unit >= 0)
+		unregister_sound_dsp(sq_unit);
+}
+
+#else /* !MODULE */
+
+static int dmasound_setup(char *str)
+{
+	int ints[6], size;
+
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	/* check the bootstrap parameter for "dmasound=" */
+
+	/* FIXME: other than in the most naive of cases there is no sense in these
+	 *	  buffers being other than powers of two.  This is not checked yet.
+	 */
+
+	switch (ints[0]) {
+	case 3:
+		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
+			printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
+		else
+			catchRadius = ints[3];
+		/* fall through */
+	case 2:
+		if (ints[1] < MIN_BUFFERS)
+			printk("dmasound_setup: invalid number of buffers, using default = %d\n", numWriteBufs);
+		else
+			numWriteBufs = ints[1];
+		/* fall through */
+	case 1:
+		if ((size = ints[2]) < 256) /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: invalid write buffer size, using default = %d\n", writeBufSize);
+                else
+                        writeBufSize = size;
+	case 0:
+		break;
+	default:
+		printk("dmasound_setup: invalid number of arguments\n");
+		return 0;
+	}
+	return 1;
+}
+
+__setup("dmasound=", dmasound_setup);
+
+#endif /* !MODULE */
+
+    /*
+     *  Conversion tables
+     */
+
+#ifdef HAS_8BIT_TABLES
+/* 8 bit mu-law */
+
+char dmasound_ulaw2dma8[] = {
+	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
+	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
+	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
+	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
+	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
+	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
+	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
+	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
+	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
+	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
+	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
+	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
+	125,	121,	117,	113,	109,	105,	101,	97,
+	93,	89,	85,	81,	77,	73,	69,	65,
+	62,	60,	58,	56,	54,	52,	50,	48,
+	46,	44,	42,	40,	38,	36,	34,	32,
+	30,	29,	28,	27,	26,	25,	24,	23,
+	22,	21,	20,	19,	18,	17,	16,	15,
+	15,	14,	14,	13,	13,	12,	12,	11,
+	11,	10,	10,	9,	9,	8,	8,	7,
+	7,	7,	6,	6,	6,	6,	5,	5,
+	5,	5,	4,	4,	4,	4,	3,	3,
+	3,	3,	3,	3,	2,	2,	2,	2,
+	2,	2,	2,	2,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0
+};
+
+/* 8 bit A-law */
+
+char dmasound_alaw2dma8[] = {
+	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
+	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
+	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
+	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
+	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
+	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
+	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
+	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
+	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
+	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
+	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
+	21,	20,	23,	22,	17,	16,	19,	18,
+	29,	28,	31,	30,	25,	24,	27,	26,
+	10,	10,	11,	11,	8,	8,	9,	9,
+	14,	14,	15,	15,	12,	12,	13,	13,
+	86,	82,	94,	90,	70,	66,	78,	74,
+	118,	114,	126,	122,	102,	98,	110,	106,
+	43,	41,	47,	45,	35,	33,	39,	37,
+	59,	57,	63,	61,	51,	49,	55,	53,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	5,	5,	5,	5,	4,	4,	4,	4,
+	7,	7,	7,	7,	6,	6,	6,	6,
+	2,	2,	2,	2,	2,	2,	2,	2,
+	3,	3,	3,	3,	3,	3,	3,	3
+};
+#endif /* HAS_8BIT_TABLES */
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(dmasound);
+EXPORT_SYMBOL(dmasound_init);
+#ifdef MODULE
+EXPORT_SYMBOL(dmasound_deinit);
+#endif
+EXPORT_SYMBOL(dmasound_write_sq);
+EXPORT_SYMBOL(dmasound_catchRadius);
+#ifdef HAS_8BIT_TABLES
+EXPORT_SYMBOL(dmasound_ulaw2dma8);
+EXPORT_SYMBOL(dmasound_alaw2dma8);
+#endif
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
new file mode 100644
index 00000000..87910e99
--- /dev/null
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -0,0 +1,751 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_paula.c
+ *
+ *  Amiga `Paula' DMA Sound Driver
+ *
+ *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in constraint on state buffer usage.
+ *	       [0.4] - put in default hard/soft settings
+*/
+
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/soundcard.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/uaccess.h>
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/machdep.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_PAULA_REVISION 0
+#define DMASOUND_PAULA_EDITION 4
+
+#define custom amiga_custom
+   /*
+    *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern volatile u_short amiga_audio_min_period;
+
+
+   /*
+    *	amiga_mksound() should be able to restore the period after beeping
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern u_short amiga_audio_period;
+
+
+   /*
+    *	Audio DMA masks
+    */
+
+#define AMI_AUDIO_OFF	(DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
+#define AMI_AUDIO_8	(DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
+#define AMI_AUDIO_14	(AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
+
+
+    /*
+     *  Helper pointers for 16(14)-bit sound
+     */
+
+static int write_sq_block_size_half, write_sq_block_size_quarter;
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void *AmiAlloc(unsigned int size, gfp_t flags);
+static void AmiFree(void *obj, unsigned int size);
+static int AmiIrqInit(void);
+#ifdef MODULE
+static void AmiIrqCleanUp(void);
+#endif
+static void AmiSilence(void);
+static void AmiInit(void);
+static int AmiSetFormat(int format);
+static int AmiSetVolume(int volume);
+static int AmiSetTreble(int treble);
+static void AmiPlayNextFrame(int index);
+static void AmiPlay(void);
+static irqreturn_t AmiInterrupt(int irq, void *dummy);
+
+#ifdef CONFIG_HEARTBEAT
+
+    /*
+     *  Heartbeat interferes with sound since the 7 kHz low-pass filter and the
+     *  power LED are controlled by the same line.
+     */
+
+static void (*saved_heartbeat)(int) = NULL;
+
+static inline void disable_heartbeat(void)
+{
+	if (mach_heartbeat) {
+	    saved_heartbeat = mach_heartbeat;
+	    mach_heartbeat = NULL;
+	}
+	AmiSetTreble(dmasound.treble);
+}
+
+static inline void enable_heartbeat(void)
+{
+	if (saved_heartbeat)
+	    mach_heartbeat = saved_heartbeat;
+}
+#else /* !CONFIG_HEARTBEAT */
+#define disable_heartbeat()	do { } while (0)
+#define enable_heartbeat()	do { } while (0)
+#endif /* !CONFIG_HEARTBEAT */
+
+
+/*** Mid level stuff *********************************************************/
+
+static void AmiMixerInit(void);
+static int AmiMixerIoctl(u_int cmd, u_long arg);
+static int AmiWriteSqSetup(void);
+static int AmiStateInfo(char *buffer, size_t space);
+
+
+/*** Translations ************************************************************/
+
+/* ++TeSche: radically changed for new expanding purposes...
+ *
+ * These two routines now deal with copying/expanding/translating the samples
+ * from user space into our buffer at the right frequency. They take care about
+ * how much data there's actually to read, how much buffer space there is and
+ * to convert samples into the right frequency/encoding. They will only work on
+ * complete samples so it may happen they leave some bytes in the input stream
+ * if the user didn't write a multiple of the current sample size. They both
+ * return the number of bytes they've used from both streams so you may detect
+ * such a situation. Luckily all programs should be able to cope with that.
+ *
+ * I think I've optimized anything as far as one can do in plain C, all
+ * variables should fit in registers and the loops are really short. There's
+ * one loop for every possible situation. Writing a more generalized and thus
+ * parameterized loop would only produce slower code. Feel free to optimize
+ * this in assembler if you like. :)
+ *
+ * I think these routines belong here because they're not yet really hardware
+ * independent, especially the fact that the Falcon can play 16bit samples
+ * only in stereo is hardcoded in both of them!
+ *
+ * ++geert: split in even more functions (one per format)
+ */
+
+
+    /*
+     *  Native format
+     */
+
+static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		void *p = &frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft) & ~1;
+		used = count;
+		if (copy_from_user(p, userPtr, count))
+			return -EFAULT;
+	} else {
+		u_char *left = &frame[*frameUsed>>1];
+		u_char *right = left+write_sq_block_size_half;
+		count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
+		used = count*2;
+		while (count > 0) {
+			if (get_user(*left++, userPtr++)
+			    || get_user(*right++, userPtr++))
+				return -EFAULT;
+			count--;
+		}
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+    /*
+     *  Copy and convert 8 bit data
+     */
+
+#define GENERATE_AMI_CT8(funcname, convsample)				\
+static ssize_t funcname(const u_char __user *userPtr, size_t userCount,	\
+			u_char frame[], ssize_t *frameUsed,		\
+			ssize_t frameLeft)				\
+{									\
+	ssize_t count, used;						\
+									\
+	if (!dmasound.soft.stereo) {					\
+		u_char *p = &frame[*frameUsed];				\
+		count = min_t(size_t, userCount, frameLeft) & ~1;	\
+		used = count;						\
+		while (count > 0) {					\
+			u_char data;					\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*p++ = convsample(data);			\
+			count--;					\
+		}							\
+	} else {							\
+		u_char *left = &frame[*frameUsed>>1];			\
+		u_char *right = left+write_sq_block_size_half;		\
+		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
+		used = count*2;						\
+		while (count > 0) {					\
+			u_char data;					\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*left++ = convsample(data);			\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*right++ = convsample(data);			\
+			count--;					\
+		}							\
+	}								\
+	*frameUsed += used;						\
+	return used;							\
+}
+
+#define AMI_CT_ULAW(x)	(dmasound_ulaw2dma8[(x)])
+#define AMI_CT_ALAW(x)	(dmasound_alaw2dma8[(x)])
+#define AMI_CT_U8(x)	((x) ^ 0x80)
+
+GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
+GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
+GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
+
+
+    /*
+     *  Copy and convert 16 bit data
+     */
+
+#define GENERATE_AMI_CT_16(funcname, convsample)			\
+static ssize_t funcname(const u_char __user *userPtr, size_t userCount,	\
+			u_char frame[], ssize_t *frameUsed,		\
+			ssize_t frameLeft)				\
+{									\
+	const u_short __user *ptr = (const u_short __user *)userPtr;	\
+	ssize_t count, used;						\
+	u_short data;							\
+									\
+	if (!dmasound.soft.stereo) {					\
+		u_char *high = &frame[*frameUsed>>1];			\
+		u_char *low = high+write_sq_block_size_half;		\
+		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
+		used = count*2;						\
+		while (count > 0) {					\
+			if (get_user(data, ptr++))			\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*high++ = data>>8;				\
+			*low++ = (data>>2) & 0x3f;			\
+			count--;					\
+		}							\
+	} else {							\
+		u_char *lefth = &frame[*frameUsed>>2];			\
+		u_char *leftl = lefth+write_sq_block_size_quarter;	\
+		u_char *righth = lefth+write_sq_block_size_half;	\
+		u_char *rightl = righth+write_sq_block_size_quarter;	\
+		count = min_t(size_t, userCount, frameLeft)>>2 & ~1;	\
+		used = count*4;						\
+		while (count > 0) {					\
+			if (get_user(data, ptr++))			\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*lefth++ = data>>8;				\
+			*leftl++ = (data>>2) & 0x3f;			\
+			if (get_user(data, ptr++))			\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*righth++ = data>>8;				\
+			*rightl++ = (data>>2) & 0x3f;			\
+			count--;					\
+		}							\
+	}								\
+	*frameUsed += used;						\
+	return used;							\
+}
+
+#define AMI_CT_S16BE(x)	(x)
+#define AMI_CT_U16BE(x)	((x) ^ 0x8000)
+#define AMI_CT_S16LE(x)	(le2be16((x)))
+#define AMI_CT_U16LE(x)	(le2be16((x)) ^ 0x8000)
+
+GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
+GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
+GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
+GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
+
+
+static TRANS transAmiga = {
+	.ct_ulaw	= ami_ct_ulaw,
+	.ct_alaw	= ami_ct_alaw,
+	.ct_s8		= ami_ct_s8,
+	.ct_u8		= ami_ct_u8,
+	.ct_s16be	= ami_ct_s16be,
+	.ct_u16be	= ami_ct_u16be,
+	.ct_s16le	= ami_ct_s16le,
+	.ct_u16le	= ami_ct_u16le,
+};
+
+/*** Low level stuff *********************************************************/
+
+static inline void StopDMA(void)
+{
+	custom.aud[0].audvol = custom.aud[1].audvol = 0;
+	custom.aud[2].audvol = custom.aud[3].audvol = 0;
+	custom.dmacon = AMI_AUDIO_OFF;
+	enable_heartbeat();
+}
+
+static void *AmiAlloc(unsigned int size, gfp_t flags)
+{
+	return amiga_chip_alloc((long)size, "dmasound [Paula]");
+}
+
+static void AmiFree(void *obj, unsigned int size)
+{
+	amiga_chip_free (obj);
+}
+
+static int __init AmiIrqInit(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+
+	/* Register interrupt handler. */
+	if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
+			AmiInterrupt))
+		return 0;
+	return 1;
+}
+
+#ifdef MODULE
+static void AmiIrqCleanUp(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+	/* release the interrupt */
+	free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
+}
+#endif /* MODULE */
+
+static void AmiSilence(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+}
+
+
+static void AmiInit(void)
+{
+	int period, i;
+
+	AmiSilence();
+
+	if (dmasound.soft.speed)
+		period = amiga_colorclock/dmasound.soft.speed-1;
+	else
+		period = amiga_audio_min_period;
+	dmasound.hard = dmasound.soft;
+	dmasound.trans_write = &transAmiga;
+
+	if (period < amiga_audio_min_period) {
+		/* we would need to squeeze the sound, but we won't do that */
+		period = amiga_audio_min_period;
+	} else if (period > 65535) {
+		period = 65535;
+	}
+	dmasound.hard.speed = amiga_colorclock/(period+1);
+
+	for (i = 0; i < 4; i++)
+		custom.aud[i].audper = period;
+	amiga_audio_period = period;
+}
+
+
+static int AmiSetFormat(int format)
+{
+	int size;
+
+	/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+		size = 8;
+		break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+		size = 16;
+		break;
+	default: /* :-) */
+		size = 8;
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = size;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = dmasound.soft.size;
+	}
+	AmiInit();
+
+	return format;
+}
+
+
+#define VOLUME_VOXWARE_TO_AMI(v) \
+	(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
+#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
+
+static int AmiSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
+	custom.aud[0].audvol = dmasound.volume_left;
+	dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
+	custom.aud[1].audvol = dmasound.volume_right;
+	if (dmasound.hard.size == 16) {
+		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
+			custom.aud[2].audvol = 1;
+			custom.aud[3].audvol = 1;
+		} else {
+			custom.aud[2].audvol = 0;
+			custom.aud[3].audvol = 0;
+		}
+	}
+	return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
+	       (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
+}
+
+static int AmiSetTreble(int treble)
+{
+	dmasound.treble = treble;
+	if (treble < 50)
+		ciaa.pra &= ~0x02;
+	else
+		ciaa.pra |= 0x02;
+	return treble;
+}
+
+
+#define AMI_PLAY_LOADED		1
+#define AMI_PLAY_PLAYING	2
+#define AMI_PLAY_MASK		3
+
+
+static void AmiPlayNextFrame(int index)
+{
+	u_char *start, *ch0, *ch1, *ch2, *ch3;
+	u_long size;
+
+	/* used by AmiPlay() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	size = (write_sq.count == index ? write_sq.rear_size
+					: write_sq.block_size)>>1;
+
+	if (dmasound.hard.stereo) {
+		ch0 = start;
+		ch1 = start+write_sq_block_size_half;
+		size >>= 1;
+	} else {
+		ch0 = start;
+		ch1 = start;
+	}
+
+	disable_heartbeat();
+	custom.aud[0].audvol = dmasound.volume_left;
+	custom.aud[1].audvol = dmasound.volume_right;
+	if (dmasound.hard.size == 8) {
+		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+		custom.aud[0].audlen = size;
+		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+		custom.aud[1].audlen = size;
+		custom.dmacon = AMI_AUDIO_8;
+	} else {
+		size >>= 1;
+		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+		custom.aud[0].audlen = size;
+		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+		custom.aud[1].audlen = size;
+		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
+			/* We can play pseudo 14-bit only with the maximum volume */
+			ch3 = ch0+write_sq_block_size_quarter;
+			ch2 = ch1+write_sq_block_size_quarter;
+			custom.aud[2].audvol = 1;  /* we are being affected by the beeps */
+			custom.aud[3].audvol = 1;  /* restoring volume here helps a bit */
+			custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
+			custom.aud[2].audlen = size;
+			custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
+			custom.aud[3].audlen = size;
+			custom.dmacon = AMI_AUDIO_14;
+		} else {
+			custom.aud[2].audvol = 0;
+			custom.aud[3].audvol = 0;
+			custom.dmacon = AMI_AUDIO_8;
+		}
+	}
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active |= AMI_PLAY_LOADED;
+}
+
+
+static void AmiPlay(void)
+{
+	int minframes = 1;
+
+	custom.intena = IF_AUD0;
+
+	if (write_sq.active & AMI_PLAY_LOADED) {
+		/* There's already a frame loaded */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	if (write_sq.active & AMI_PLAY_PLAYING)
+		/* Increase threshold: frame 1 is already being played */
+		minframes = 2;
+
+	if (write_sq.count < minframes) {
+		/* Nothing to do */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	if (write_sq.count <= minframes &&
+	    write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
+		/* hmmm, the only existing frame is not
+		 * yet filled and we're not syncing?
+		 */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	AmiPlayNextFrame(minframes);
+
+	custom.intena = IF_SETCLR | IF_AUD0;
+}
+
+
+static irqreturn_t AmiInterrupt(int irq, void *dummy)
+{
+	int minframes = 1;
+
+	custom.intena = IF_AUD0;
+
+	if (!write_sq.active) {
+		/* Playing was interrupted and sq_reset() has already cleared
+		 * the sq variables, so better don't do anything here.
+		 */
+		WAKE_UP(write_sq.sync_queue);
+		return IRQ_HANDLED;
+	}
+
+	if (write_sq.active & AMI_PLAY_PLAYING) {
+		/* We've just finished a frame */
+		write_sq.count--;
+		WAKE_UP(write_sq.action_queue);
+	}
+
+	if (write_sq.active & AMI_PLAY_LOADED)
+		/* Increase threshold: frame 1 is already being played */
+		minframes = 2;
+
+	/* Shift the flags */
+	write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
+
+	if (!write_sq.active)
+		/* No frame is playing, disable audio DMA */
+		StopDMA();
+
+	custom.intena = IF_SETCLR | IF_AUD0;
+
+	if (write_sq.count >= minframes)
+		/* Try to play the next frame */
+		AmiPlay();
+
+	if (!write_sq.active)
+		/* Nothing to play anymore.
+		   Wake up a process waiting for audio output to drain. */
+		WAKE_UP(write_sq.sync_queue);
+	return IRQ_HANDLED;
+}
+
+/*** Mid level stuff *********************************************************/
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+static void __init AmiMixerInit(void)
+{
+	dmasound.volume_left = 64;
+	dmasound.volume_right = 64;
+	custom.aud[0].audvol = dmasound.volume_left;
+	custom.aud[3].audvol = 1;	/* For pseudo 14bit */
+	custom.aud[1].audvol = dmasound.volume_right;
+	custom.aud[2].audvol = 1;	/* For pseudo 14bit */
+	dmasound.treble = 50;
+}
+
+static int AmiMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_DEVMASK:
+		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
+	    case SOUND_MIXER_READ_RECMASK:
+		    return IOCTL_OUT(arg, 0);
+	    case SOUND_MIXER_READ_STEREODEVS:
+		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
+	    case SOUND_MIXER_READ_VOLUME:
+		    return IOCTL_OUT(arg,
+			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
+			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
+	    case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_volume(data));
+	    case SOUND_MIXER_READ_TREBLE:
+		    return IOCTL_OUT(arg, dmasound.treble);
+	    case SOUND_MIXER_WRITE_TREBLE:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_treble(data));
+	}
+	return -EINVAL;
+}
+
+
+static int AmiWriteSqSetup(void)
+{
+	write_sq_block_size_half = write_sq.block_size>>1;
+	write_sq_block_size_quarter = write_sq_block_size_half>>1;
+	return 0;
+}
+
+
+static int AmiStateInfo(char *buffer, size_t space)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
+		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
+	return len;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+static SETTINGS def_hard = {
+	.format	= AFMT_S8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static SETTINGS def_soft = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static MACHINE machAmiga = {
+	.name		= "Amiga",
+	.name2		= "AMIGA",
+	.owner		= THIS_MODULE,
+	.dma_alloc	= AmiAlloc,
+	.dma_free	= AmiFree,
+	.irqinit	= AmiIrqInit,
+#ifdef MODULE
+	.irqcleanup	= AmiIrqCleanUp,
+#endif /* MODULE */
+	.init		= AmiInit,
+	.silence	= AmiSilence,
+	.setFormat	= AmiSetFormat,
+	.setVolume	= AmiSetVolume,
+	.setTreble	= AmiSetTreble,
+	.play		= AmiPlay,
+	.mixer_init	= AmiMixerInit,
+	.mixer_ioctl	= AmiMixerIoctl,
+	.write_sq_setup	= AmiWriteSqSetup,
+	.state_info	= AmiStateInfo,
+	.min_dsp_speed	= 8000,
+	.version	= ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
+	.hardware_afmts	= (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+	.capabilities	= DSP_CAP_BATCH          /* As per SNDCTL_DSP_GETCAPS */
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init amiga_audio_probe(struct platform_device *pdev)
+{
+	dmasound.mach = machAmiga;
+	dmasound.mach.default_hard = def_hard ;
+	dmasound.mach.default_soft = def_soft ;
+	return dmasound_init();
+}
+
+static int __exit amiga_audio_remove(struct platform_device *pdev)
+{
+	dmasound_deinit();
+	return 0;
+}
+
+static struct platform_driver amiga_audio_driver = {
+	.remove = __exit_p(amiga_audio_remove),
+	.driver   = {
+		.name	= "amiga-audio",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init amiga_audio_init(void)
+{
+	return platform_driver_probe(&amiga_audio_driver, amiga_audio_probe);
+}
+
+module_init(amiga_audio_init);
+
+static void __exit amiga_audio_exit(void)
+{
+	platform_driver_unregister(&amiga_audio_driver);
+}
+
+module_exit(amiga_audio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:amiga-audio");
diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c
new file mode 100644
index 00000000..99bcb21c
--- /dev/null
+++ b/sound/oss/dmasound/dmasound_q40.c
@@ -0,0 +1,638 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_q40.c
+ *
+ *  Q40 DMA Sound Driver
+ *
+ *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in default hard/soft settings.
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+#include <asm/q40ints.h>
+#include <asm/q40_master.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_Q40_REVISION 0
+#define DMASOUND_Q40_EDITION 3
+
+static int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void *Q40Alloc(unsigned int size, gfp_t flags);
+static void Q40Free(void *, unsigned int);
+static int Q40IrqInit(void);
+#ifdef MODULE
+static void Q40IrqCleanUp(void);
+#endif
+static void Q40Silence(void);
+static void Q40Init(void);
+static int Q40SetFormat(int format);
+static int Q40SetVolume(int volume);
+static void Q40PlayNextFrame(int index);
+static void Q40Play(void);
+static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
+static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
+static void Q40Interrupt(void);
+
+
+/*** Mid level stuff *********************************************************/
+
+
+
+/* userCount, frameUsed, frameLeft == byte counts */
+static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	while (count > 0) {
+		*p = table[*p]+128;
+		p++;
+		count--;
+	}
+	*frameUsed += used ;
+	return used;
+}
+
+
+static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	while (count > 0) {
+		*p = *p + 128;
+		p++;
+		count--;
+	}
+	*frameUsed += used;
+	return used;
+}
+
+static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	*frameUsed += used;
+	return used;
+}
+
+
+/* a bit too complicated to optimise right now ..*/
+static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned char *table = (unsigned char *)
+		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
+	unsigned int data = expand_data;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = table[c];
+			data += 0x80;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c ;
+			data += 0x80;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c ;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) ;
+	utotal -= userCount;
+	return utotal;
+}
+
+/* compressing versions */
+static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned char *table = (unsigned char *)
+		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
+	unsigned int data = expand_data;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+ 
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		while(bal<0) {
+			if (userCount == 0)
+				goto lout;
+			if (!(bal<(-hSpeed))) {
+				if (get_user(c, userPtr))
+					return -EFAULT;
+				data = 0x80 + table[c];
+			}
+			userPtr++;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+ lout:
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		while (bal < 0) {
+			if (userCount == 0)
+				goto lout;
+			if (!(bal<(-hSpeed))) {
+				if (get_user(c, userPtr))
+					return -EFAULT;
+				data = c + 0x80;
+			}
+			userPtr++;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+ lout:
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		while (bal < 0) {
+			if (userCount == 0)
+				goto lout;
+			if (!(bal<(-hSpeed))) {
+				if (get_user(c, userPtr))
+					return -EFAULT;
+				data = c ;
+			}
+			userPtr++;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+ lout:
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) ;
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static TRANS transQ40Normal = {
+	q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
+};
+
+static TRANS transQ40Expanding = {
+	q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
+};
+
+static TRANS transQ40Compressing = {
+	q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
+};
+
+
+/*** Low level stuff *********************************************************/
+
+static void *Q40Alloc(unsigned int size, gfp_t flags)
+{
+         return kmalloc(size, flags); /* change to vmalloc */
+}
+
+static void Q40Free(void *ptr, unsigned int size)
+{
+	kfree(ptr);
+}
+
+static int __init Q40IrqInit(void)
+{
+	/* Register interrupt handler. */
+	if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
+		    "DMA sound", Q40Interrupt))
+		return 0;
+
+	return(1);
+}
+
+
+#ifdef MODULE
+static void Q40IrqCleanUp(void)
+{
+        master_outb(0,SAMPLE_ENABLE_REG);
+	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
+}
+#endif /* MODULE */
+
+
+static void Q40Silence(void)
+{
+        master_outb(0,SAMPLE_ENABLE_REG);
+	*DAC_LEFT=*DAC_RIGHT=127;
+}
+
+static char *q40_pp;
+static unsigned int q40_sc;
+
+static void Q40PlayNextFrame(int index)
+{
+	u_char *start;
+	u_long size;
+	u_char speed;
+	int error;
+
+	/* used by Q40Play() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
+
+	q40_pp=start;
+	q40_sc=size;
+
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active++;
+
+	speed=(dmasound.hard.speed==10000 ? 0 : 1);
+
+	master_outb( 0,SAMPLE_ENABLE_REG);
+	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
+	if (dmasound.soft.stereo)
+		error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
+				    "Q40 sound", Q40Interrupt);
+	  else
+		error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
+				    "Q40 sound", Q40Interrupt);
+	if (error && printk_ratelimit())
+		pr_err("Couldn't register sound interrupt\n");
+
+	master_outb( speed, SAMPLE_RATE_REG);
+	master_outb( 1,SAMPLE_CLEAR_REG);
+	master_outb( 1,SAMPLE_ENABLE_REG);
+}
+
+static void Q40Play(void)
+{
+        unsigned long flags;
+
+	if (write_sq.active || write_sq.count<=0 ) {
+		/* There's already a frame loaded */
+		return;
+	}
+
+	/* nothing in the queue */
+	if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
+	         /* hmmm, the only existing frame is not
+		  * yet filled and we're not syncing?
+		  */
+	         return;
+	}
+	spin_lock_irqsave(&dmasound.lock, flags);
+	Q40PlayNextFrame(1);
+	spin_unlock_irqrestore(&dmasound.lock, flags);
+}
+
+static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
+{
+	spin_lock(&dmasound.lock);
+        if (q40_sc>1){
+            *DAC_LEFT=*q40_pp++;
+	    *DAC_RIGHT=*q40_pp++;
+	    q40_sc -=2;
+	    master_outb(1,SAMPLE_CLEAR_REG);
+	}else Q40Interrupt();
+	spin_unlock(&dmasound.lock);
+	return IRQ_HANDLED;
+}
+static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
+{
+	spin_lock(&dmasound.lock);
+        if (q40_sc>0){
+            *DAC_LEFT=*q40_pp;
+	    *DAC_RIGHT=*q40_pp++;
+	    q40_sc --;
+	    master_outb(1,SAMPLE_CLEAR_REG);
+	}else Q40Interrupt();
+	spin_unlock(&dmasound.lock);
+	return IRQ_HANDLED;
+}
+static void Q40Interrupt(void)
+{
+	if (!write_sq.active) {
+	          /* playing was interrupted and sq_reset() has already cleared
+		   * the sq variables, so better don't do anything here.
+		   */
+	           WAKE_UP(write_sq.sync_queue);
+		   master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
+		   goto exit;
+	} else write_sq.active=0;
+	write_sq.count--;
+	Q40Play();
+
+	if (q40_sc<2)
+	      { /* there was nothing to play, disable irq */
+		master_outb(0,SAMPLE_ENABLE_REG);
+		*DAC_LEFT=*DAC_RIGHT=127;
+	      }
+	WAKE_UP(write_sq.action_queue);
+
+ exit:
+	master_outb(1,SAMPLE_CLEAR_REG);
+}
+
+
+static void Q40Init(void)
+{
+	int i, idx;
+	const int freq[] = {10000, 20000};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < 2; i++)
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
+			idx = i;
+
+	dmasound.hard = dmasound.soft;
+	/*sound.hard.stereo=1;*/ /* no longer true */
+	dmasound.hard.size=8;
+
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transQ40Normal;
+	} else
+		dmasound.trans_write = &transQ40Expanding;
+
+	Q40Silence();
+
+	if (dmasound.hard.speed > 20200) {
+		/* squeeze the sound, we do that */
+		dmasound.hard.speed = 20000;
+		dmasound.trans_write = &transQ40Compressing;
+	} else if (dmasound.hard.speed > 10000) {
+		dmasound.hard.speed = 20000;
+	} else {
+		dmasound.hard.speed = 10000;
+	}
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int Q40SetFormat(int format)
+{
+	/* Q40 sound supports only 8bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return(dmasound.soft.format);
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_S8:
+	case AFMT_U8:
+		break;
+	default:
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = 8;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = 8;
+	}
+	Q40Init();
+
+	return(format);
+}
+
+static int Q40SetVolume(int volume)
+{
+    return 0;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+static SETTINGS def_hard = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 10000
+} ;
+
+static SETTINGS def_soft = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static MACHINE machQ40 = {
+	.name		= "Q40",
+	.name2		= "Q40",
+	.owner		= THIS_MODULE,
+	.dma_alloc	= Q40Alloc,
+	.dma_free	= Q40Free,
+	.irqinit	= Q40IrqInit,
+#ifdef MODULE
+	.irqcleanup	= Q40IrqCleanUp,
+#endif /* MODULE */
+	.init		= Q40Init,
+	.silence	= Q40Silence,
+	.setFormat	= Q40SetFormat,
+	.setVolume	= Q40SetVolume,
+	.play		= Q40Play,
+ 	.min_dsp_speed	= 10000,
+	.version	= ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
+	.hardware_afmts	= AFMT_U8, /* h'ware-supported formats *only* here */
+	.capabilities	= DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init dmasound_q40_init(void)
+{
+	if (MACH_IS_Q40) {
+	    dmasound.mach = machQ40;
+	    dmasound.mach.default_hard = def_hard ;
+	    dmasound.mach.default_soft = def_soft ;
+	    return dmasound_init();
+	} else
+	    return -ENODEV;
+}
+
+static void __exit dmasound_q40_cleanup(void)
+{
+	dmasound_deinit();
+}
+
+module_init(dmasound_q40_init);
+module_exit(dmasound_q40_cleanup);
+
+MODULE_DESCRIPTION("Q40/Q60 sound driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/hex2hex.c b/sound/oss/hex2hex.c
new file mode 100644
index 00000000..041ef5c5
--- /dev/null
+++ b/sound/oss/hex2hex.c
@@ -0,0 +1,101 @@
+/*
+ * hex2hex reads stdin in Intel HEX format and produces an
+ * (unsigned char) array which contains the bytes and writes it
+ * to stdout using C syntax
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); }
+#define MAX_SIZE (256*1024)
+unsigned char buf[MAX_SIZE];
+
+static int loadhex(FILE *inf, unsigned char *buf)
+{
+	int l=0, c, i;
+
+	while ((c=getc(inf))!=EOF)
+	{
+		if (c == ':')	/* Sync with beginning of line */
+		{
+			int n, check;
+			unsigned char sum;
+			int addr;
+			int linetype;
+
+			if (fscanf(inf, "%02x", &n) != 1)
+			   ABANDON("File format error");
+			sum = n;
+
+			if (fscanf(inf, "%04x", &addr) != 1)
+			   ABANDON("File format error");
+			sum += addr/256;
+			sum += addr%256;
+
+			if (fscanf(inf, "%02x", &linetype) != 1)
+			   ABANDON("File format error");
+			sum += linetype;
+
+			if (linetype != 0)
+			   continue;
+
+			for (i=0;i<n;i++)
+			{
+				if (fscanf(inf, "%02x", &c) != 1)
+			   	   ABANDON("File format error");
+				if (addr >= MAX_SIZE)
+				   ABANDON("File too large");
+				buf[addr++] = c;
+				if (addr > l)
+				   l = addr;
+				sum += c;
+			}
+
+			if (fscanf(inf, "%02x", &check) != 1)
+			   ABANDON("File format error");
+
+			sum = ~sum + 1;
+			if (check != sum)
+			   ABANDON("Line checksum error");
+		}
+	}
+
+	return l;
+}
+
+int main( int argc, const char * argv [] )
+{
+	const char * varline;
+	int i,l;
+	int id=0;
+
+	if(argv[1] && strcmp(argv[1], "-i")==0)
+	{
+		argv++;
+		argc--;
+		id=1;
+	}
+	if(argv[1]==NULL)
+	{
+		fprintf(stderr,"hex2hex: [-i] filename\n");
+		exit(1);
+	}
+	varline = argv[1];
+	l = loadhex(stdin, buf);
+
+	printf("/*\n *\t Computer generated file. Do not edit.\n */\n");
+        printf("static int %s_len = %d;\n", varline, l);
+	printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":"");
+
+	for (i=0;i<l;i++)
+	{
+		if (i) printf(",");
+		if (i && !(i % 16)) printf("\n");
+		printf("0x%02x", buf[i]);
+	}
+
+	printf("\n};\n\n");
+	return 0;
+}
diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c
new file mode 100644
index 00000000..52d06a33
--- /dev/null
+++ b/sound/oss/kahlua.c
@@ -0,0 +1,231 @@
+/*
+ *	Initialisation code for Cyrix/NatSemi VSA1 softaudio
+ *
+ *	(C) Copyright 2003 Red Hat Inc <alan@lxorguk.ukuu.org.uk>
+ *
+ * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
+ * The older version (VSA1) provides fairly good soundblaster emulation
+ * although there are a couple of bugs: large DMA buffers break record,
+ * and the MPU event handling seems suspect. VSA2 allows the native driver
+ * to control the AC97 audio engine directly and requires a different driver.
+ *
+ * Thanks to National Semiconductor for providing the needed information
+ * on the XpressAudio(tm) internals.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * TO DO:
+ *	Investigate whether we can portably support Cognac (5520) in the
+ *	same manner.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "sound_config.h"
+
+#include "sb.h"
+
+/*
+ *	Read a soundblaster compatible mixer register.
+ *	In this case we are actually reading an SMI trap
+ *	not real hardware.
+ */
+
+static u8 __devinit mixer_read(unsigned long io, u8 reg)
+{
+	outb(reg, io + 4);
+	udelay(20);
+	reg = inb(io + 5);
+	udelay(20);
+	return reg;
+}
+
+static int __devinit probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct address_info *hw_config;
+	unsigned long base;
+	void __iomem *mem;
+	unsigned long io;
+	u16 map;
+	u8 irq, dma8, dma16;
+	int oldquiet;
+	extern int sb_be_quiet;
+		
+	base = pci_resource_start(pdev, 0);
+	if(base == 0UL)
+		return 1;
+	
+	mem = ioremap(base, 128);
+	if (!mem)
+		return 1;
+	map = readw(mem + 0x18);	/* Read the SMI enables */
+	iounmap(mem);
+	
+	/* Map bits
+		0:1	* 0x20 + 0x200 = sb base
+		2	sb enable
+		3	adlib enable
+		5	MPU enable 0x330
+		6	MPU enable 0x300
+		
+	   The other bits may be used internally so must be masked */
+
+	io = 0x220 + 0x20 * (map & 3);	   
+	
+	if(map & (1<<2))
+		printk(KERN_INFO "kahlua: XpressAudio at 0x%lx\n", io);
+	else
+		return 1;
+		
+	if(map & (1<<5))
+		printk(KERN_INFO "kahlua: MPU at 0x300\n");
+	else if(map & (1<<6))
+		printk(KERN_INFO "kahlua: MPU at 0x330\n");
+	
+	irq = mixer_read(io, 0x80) & 0x0F;
+	dma8 = mixer_read(io, 0x81);
+
+	// printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8);
+	
+	if(dma8 & 0x20)
+		dma16 = 5;
+	else if(dma8 & 0x40)
+		dma16 = 6;
+	else if(dma8 & 0x80)
+		dma16 = 7;
+	else
+	{
+		printk(KERN_ERR "kahlua: No 16bit DMA enabled.\n");
+		return 1;
+	}
+		
+	if(dma8 & 0x01)
+		dma8 = 0;
+	else if(dma8 & 0x02)
+		dma8 = 1;
+	else if(dma8 & 0x08)
+		dma8 = 3;
+	else
+	{
+		printk(KERN_ERR "kahlua: No 8bit DMA enabled.\n");
+		return 1;
+	}
+	
+	if(irq & 1)
+		irq = 9;
+	else if(irq & 2)
+		irq = 5;
+	else if(irq & 4)
+		irq = 7;
+	else if(irq & 8)
+		irq = 10;
+	else
+	{
+		printk(KERN_ERR "kahlua: SB IRQ not set.\n");
+		return 1;
+	}
+	
+	printk(KERN_INFO "kahlua: XpressAudio on IRQ %d, DMA %d, %d\n",
+		irq, dma8, dma16);
+	
+	hw_config = kzalloc(sizeof(struct address_info), GFP_KERNEL);
+	if(hw_config == NULL)
+	{
+		printk(KERN_ERR "kahlua: out of memory.\n");
+		return 1;
+	}
+	
+	pci_set_drvdata(pdev, hw_config);
+	
+	hw_config->io_base = io;
+	hw_config->irq = irq;
+	hw_config->dma = dma8;
+	hw_config->dma2 = dma16;
+	hw_config->name = "Cyrix XpressAudio";
+	hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ;
+
+	if (!request_region(io, 16, "soundblaster"))
+		goto err_out_free;
+	
+	if(sb_dsp_detect(hw_config, 0, 0, NULL)==0)
+	{
+		printk(KERN_ERR "kahlua: audio not responding.\n");
+		release_region(io, 16);
+		goto err_out_free;
+	}
+
+	oldquiet = sb_be_quiet;	
+	sb_be_quiet = 1;
+	if(sb_dsp_init(hw_config, THIS_MODULE))
+	{
+		sb_be_quiet = oldquiet;
+		goto err_out_free;
+	}
+	sb_be_quiet = oldquiet;
+	
+	return 0;
+
+err_out_free:
+	pci_set_drvdata(pdev, NULL);
+	kfree(hw_config);
+	return 1;
+}
+
+static void __devexit remove_one(struct pci_dev *pdev)
+{
+	struct address_info *hw_config = pci_get_drvdata(pdev);
+	sb_dsp_unload(hw_config, 0);
+	pci_set_drvdata(pdev, NULL);
+	kfree(hw_config);
+}
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Kahlua VSA1 PCI Audio");
+MODULE_LICENSE("GPL");
+
+/*
+ *	5530 only. The 5510/5520 decode is different.
+ */
+
+static DEFINE_PCI_DEVICE_TABLE(id_tbl) = {
+	{ PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO), 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(pci, id_tbl);
+
+static struct pci_driver kahlua_driver = {
+	.name		= "kahlua",
+	.id_table	= id_tbl,
+	.probe		= probe_one,
+	.remove		= __devexit_p(remove_one),
+};
+
+
+static int __init kahlua_init_module(void)
+{
+	printk(KERN_INFO "Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc\n");
+	return pci_register_driver(&kahlua_driver);
+}
+
+static void __devexit kahlua_cleanup_module(void)
+{
+	pci_unregister_driver(&kahlua_driver);
+}
+
+
+module_init(kahlua_init_module);
+module_exit(kahlua_cleanup_module);
+
diff --git a/sound/oss/midi_ctrl.h b/sound/oss/midi_ctrl.h
new file mode 100644
index 00000000..3353e5a6
--- /dev/null
+++ b/sound/oss/midi_ctrl.h
@@ -0,0 +1,22 @@
+static unsigned char ctrl_def_values[128] =
+{
+	0x40,0x00,0x40,0x40,  0x40,0x40,0x40,0x7f,	/*   0 to   7 */
+	0x40,0x40,0x40,0x7f,  0x40,0x40,0x40,0x40,	/*   8 to  15 */
+	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*  16 to  23 */
+	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*  24 to  31 */
+
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  32 to  39 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  40 to  47 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  48 to  55 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  56 to  63 */
+	
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  64 to  71 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  72 to  79 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  80 to  87 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  88 to  95 */
+
+	0x00,0x00,0x7f,0x7f,  0x7f,0x7f,0x00,0x00,	/*  96 to 103 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 104 to 111 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 112 to 119 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 120 to 127 */
+};
diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c
new file mode 100644
index 00000000..2292c230
--- /dev/null
+++ b/sound/oss/midi_synth.c
@@ -0,0 +1,712 @@
+/*
+ * sound/oss/midi_synth.c
+ *
+ * High level midi sequencer manager for dumb MIDI interfaces.
+ */
+/*
+ * 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)
+ * Andrew Veliath  : fixed running status in MIDI input state machine
+ */
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+
+#define _MIDI_SYNTH_C_
+
+#include "midi_synth.h"
+
+static int      midi2synth[MAX_MIDI_DEV];
+static int      sysex_state[MAX_MIDI_DEV] =
+{0};
+static unsigned char prev_out_status[MAX_MIDI_DEV];
+
+#define STORE(cmd) \
+{ \
+  int len; \
+  unsigned char obuf[8]; \
+  cmd; \
+  seq_input_event(obuf, len); \
+}
+
+#define _seqbuf obuf
+#define _seqbufptr 0
+#define _SEQ_ADVBUF(x) len=x
+
+void
+do_midi_msg(int synthno, unsigned char *msg, int mlen)
+{
+	switch (msg[0] & 0xf0)
+	  {
+	  case 0x90:
+		  if (msg[2] != 0)
+		    {
+			    STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+			    break;
+		    }
+		  msg[2] = 64;
+
+	  case 0x80:
+		  STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+		  break;
+
+	  case 0xA0:
+		  STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+		  break;
+
+	  case 0xB0:
+		  STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f,
+				    msg[1], msg[2]));
+		  break;
+
+	  case 0xC0:
+		  STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1]));
+		  break;
+
+	  case 0xD0:
+		  STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1]));
+		  break;
+
+	  case 0xE0:
+		  STORE(SEQ_BENDER(synthno, msg[0] & 0x0f,
+			      (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7)));
+		  break;
+
+	  default:
+		  /* printk( "MPU: Unknown midi channel message %02x\n",  msg[0]); */
+		  ;
+	  }
+}
+EXPORT_SYMBOL(do_midi_msg);
+
+static void
+midi_outc(int midi_dev, int data)
+{
+	int             timeout;
+
+	for (timeout = 0; timeout < 3200; timeout++)
+		if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff)))
+		  {
+			  if (data & 0x80)	/*
+						 * Status byte
+						 */
+				  prev_out_status[midi_dev] =
+				      (unsigned char) (data & 0xff);	/*
+									 * Store for running status
+									 */
+			  return;	/*
+					 * Mission complete
+					 */
+		  }
+	/*
+	 * Sorry! No space on buffers.
+	 */
+	printk("Midi send timed out\n");
+}
+
+static int
+prefix_cmd(int midi_dev, unsigned char status)
+{
+	if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
+		return 1;
+
+	return midi_devs[midi_dev]->prefix_cmd(midi_dev, status);
+}
+
+static void
+midi_synth_input(int orig_dev, unsigned char data)
+{
+	int             dev;
+	struct midi_input_info *inc;
+
+	static unsigned char len_tab[] =	/* # of data bytes following a status
+						 */
+	{
+		2,		/* 8x */
+		2,		/* 9x */
+		2,		/* Ax */
+		2,		/* Bx */
+		1,		/* Cx */
+		1,		/* Dx */
+		2,		/* Ex */
+		0		/* Fx */
+	};
+
+	if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+		return;
+
+	if (data == 0xfe)	/* Ignore active sensing */
+		return;
+
+	dev = midi2synth[orig_dev];
+	inc = &midi_devs[orig_dev]->in_info;
+
+	switch (inc->m_state)
+	  {
+	  case MST_INIT:
+		  if (data & 0x80)	/* MIDI status byte */
+		    {
+			    if ((data & 0xf0) == 0xf0)	/* Common message */
+			      {
+				      switch (data)
+					{
+					case 0xf0:	/* Sysex */
+						inc->m_state = MST_SYSEX;
+						break;	/* Sysex */
+
+					case 0xf1:	/* MTC quarter frame */
+					case 0xf3:	/* Song select */
+						inc->m_state = MST_DATA;
+						inc->m_ptr = 1;
+						inc->m_left = 1;
+						inc->m_buf[0] = data;
+						break;
+
+					case 0xf2:	/* Song position pointer */
+						inc->m_state = MST_DATA;
+						inc->m_ptr = 1;
+						inc->m_left = 2;
+						inc->m_buf[0] = data;
+						break;
+
+					default:
+						inc->m_buf[0] = data;
+						inc->m_ptr = 1;
+						do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+						inc->m_ptr = 0;
+						inc->m_left = 0;
+					}
+			    } else
+			      {
+				      inc->m_state = MST_DATA;
+				      inc->m_ptr = 1;
+				      inc->m_left = len_tab[(data >> 4) - 8];
+				      inc->m_buf[0] = inc->m_prev_status = data;
+			      }
+		    } else if (inc->m_prev_status & 0x80) {
+			    /* Data byte (use running status) */
+			    inc->m_ptr = 2;
+			    inc->m_buf[1] = data;
+			    inc->m_buf[0] = inc->m_prev_status;
+			    inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1;
+			    if (inc->m_left > 0)
+				    inc->m_state = MST_DATA; /* Not done yet */
+			    else {
+				    inc->m_state = MST_INIT;
+				    do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+				    inc->m_ptr = 0;
+			    }
+		    }
+		  break;	/* MST_INIT */
+
+	  case MST_DATA:
+		  inc->m_buf[inc->m_ptr++] = data;
+		  if (--inc->m_left <= 0)
+		    {
+			    inc->m_state = MST_INIT;
+			    do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+			    inc->m_ptr = 0;
+		    }
+		  break;	/* MST_DATA */
+
+	  case MST_SYSEX:
+		  if (data == 0xf7)	/* Sysex end */
+		    {
+			    inc->m_state = MST_INIT;
+			    inc->m_left = 0;
+			    inc->m_ptr = 0;
+		    }
+		  break;	/* MST_SYSEX */
+
+	  default:
+		  printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data);
+		  inc->m_state = MST_INIT;
+	  }
+}
+
+static void
+leave_sysex(int dev)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             timeout = 0;
+
+	if (!sysex_state[dev])
+		return;
+
+	sysex_state[dev] = 0;
+
+	while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) &&
+	       timeout < 1000)
+		timeout++;
+
+	sysex_state[dev] = 0;
+}
+
+static void
+midi_synth_output(int dev)
+{
+	/*
+	 * Currently NOP
+	 */
+}
+
+int midi_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	/*
+	 * int orig_dev = synth_devs[dev]->midi_dev;
+	 */
+
+	switch (cmd) {
+
+	case SNDCTL_SYNTH_INFO:
+		if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info)))
+			return -EFAULT;
+		return 0;
+		
+	case SNDCTL_SYNTH_MEMAVL:
+		return 0x7fffffff;
+
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(midi_synth_ioctl);
+
+int
+midi_synth_kill_note(int dev, int channel, int note, int velocity)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, chn;
+
+	if (note < 0 || note > 127)
+		return 0;
+	if (channel < 0 || channel > 15)
+		return 0;
+	if (velocity < 0)
+		velocity = 0;
+	if (velocity > 127)
+		velocity = 127;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
+	  {			/*
+				 * Use running status
+				 */
+		  if (!prefix_cmd(orig_dev, note))
+			  return 0;
+
+		  midi_outc(orig_dev, note);
+
+		  if (msg == 0x90)	/*
+					 * Running status = Note on
+					 */
+			  midi_outc(orig_dev, 0);	/*
+							   * Note on with velocity 0 == note
+							   * off
+							 */
+		  else
+			  midi_outc(orig_dev, velocity);
+	} else
+	  {
+		  if (velocity == 64)
+		    {
+			    if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
+				    return 0;
+			    midi_outc(orig_dev, 0x90 | (channel & 0x0f));	/*
+										 * Note on
+										 */
+			    midi_outc(orig_dev, note);
+			    midi_outc(orig_dev, 0);	/*
+							 * Zero G
+							 */
+		  } else
+		    {
+			    if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f)))
+				    return 0;
+			    midi_outc(orig_dev, 0x80 | (channel & 0x0f));	/*
+										 * Note off
+										 */
+			    midi_outc(orig_dev, note);
+			    midi_outc(orig_dev, velocity);
+		    }
+	  }
+
+	return 0;
+}
+EXPORT_SYMBOL(midi_synth_kill_note);
+
+int
+midi_synth_set_instr(int dev, int channel, int instr_no)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+
+	if (instr_no < 0 || instr_no > 127)
+		instr_no = 0;
+	if (channel < 0 || channel > 15)
+		return 0;
+
+	leave_sysex(dev);
+
+	if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f)))
+		return 0;
+	midi_outc(orig_dev, 0xc0 | (channel & 0x0f));	/*
+							 * Program change
+							 */
+	midi_outc(orig_dev, instr_no);
+
+	return 0;
+}
+EXPORT_SYMBOL(midi_synth_set_instr);
+
+int
+midi_synth_start_note(int dev, int channel, int note, int velocity)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, chn;
+
+	if (note < 0 || note > 127)
+		return 0;
+	if (channel < 0 || channel > 15)
+		return 0;
+	if (velocity < 0)
+		velocity = 0;
+	if (velocity > 127)
+		velocity = 127;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (chn == channel && msg == 0x90)
+	  {			/*
+				 * Use running status
+				 */
+		  if (!prefix_cmd(orig_dev, note))
+			  return 0;
+		  midi_outc(orig_dev, note);
+		  midi_outc(orig_dev, velocity);
+	} else
+	  {
+		  if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
+			  return 0;
+		  midi_outc(orig_dev, 0x90 | (channel & 0x0f));		/*
+									 * Note on
+									 */
+		  midi_outc(orig_dev, note);
+		  midi_outc(orig_dev, velocity);
+	  }
+	return 0;
+}
+EXPORT_SYMBOL(midi_synth_start_note);
+
+void
+midi_synth_reset(int dev)
+{
+
+	leave_sysex(dev);
+}
+EXPORT_SYMBOL(midi_synth_reset);
+
+int
+midi_synth_open(int dev, int mode)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             err;
+	struct midi_input_info *inc;
+
+	if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL)
+		return -ENXIO;
+
+	midi2synth[orig_dev] = dev;
+	sysex_state[dev] = 0;
+	prev_out_status[orig_dev] = 0;
+
+	if ((err = midi_devs[orig_dev]->open(orig_dev, mode,
+			       midi_synth_input, midi_synth_output)) < 0)
+		return err;
+	inc = &midi_devs[orig_dev]->in_info;
+
+	/* save_flags(flags);
+	cli(); 
+	don't know against what irqhandler to protect*/
+	inc->m_busy = 0;
+	inc->m_state = MST_INIT;
+	inc->m_ptr = 0;
+	inc->m_left = 0;
+	inc->m_prev_status = 0x00;
+	/* restore_flags(flags); */
+
+	return 1;
+}
+EXPORT_SYMBOL(midi_synth_open);
+
+void
+midi_synth_close(int dev)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+
+	leave_sysex(dev);
+
+	/*
+	 * Shut up the synths by sending just single active sensing message.
+	 */
+	midi_devs[orig_dev]->outputc(orig_dev, 0xfe);
+
+	midi_devs[orig_dev]->close(orig_dev);
+}
+EXPORT_SYMBOL(midi_synth_close);
+
+void
+midi_synth_hw_control(int dev, unsigned char *event)
+{
+}
+EXPORT_SYMBOL(midi_synth_hw_control);
+
+int
+midi_synth_load_patch(int dev, int format, const char __user *addr,
+		      int count, int pmgr_flag)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+
+	struct sysex_info sysex;
+	int             i;
+	unsigned long   left, src_offs, eox_seen = 0;
+	int             first_byte = 1;
+	int             hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
+
+	leave_sysex(dev);
+
+	if (!prefix_cmd(orig_dev, 0xf0))
+		return 0;
+
+	/* Invalid patch format */
+	if (format != SYSEX_PATCH)
+		  return -EINVAL;
+
+	/* Patch header too short */
+	if (count < hdr_size)
+		return -EINVAL;
+
+	count -= hdr_size;
+
+	/*
+	 * Copy the header from user space
+	 */
+
+	if (copy_from_user(&sysex, addr, hdr_size))
+		return -EFAULT;
+
+	/* Sysex record too short */
+	if ((unsigned)count < (unsigned)sysex.len)
+		sysex.len = count;
+
+	left = sysex.len;
+	src_offs = 0;
+
+	for (i = 0; i < left && !signal_pending(current); i++)
+	{
+		unsigned char   data;
+
+		if (get_user(data,
+		    (unsigned char __user *)(addr + hdr_size + i)))
+			return -EFAULT;
+
+		eox_seen = (i > 0 && data & 0x80);	/* End of sysex */
+
+		if (eox_seen && data != 0xf7)
+			data = 0xf7;
+
+		if (i == 0)
+		{
+			if (data != 0xf0)
+			{
+				printk(KERN_WARNING "midi_synth: Sysex start missing\n");
+				return -EINVAL;
+			}
+		}
+		while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) &&
+			!signal_pending(current))
+			schedule();
+
+		if (!first_byte && data & 0x80)
+			return 0;
+		first_byte = 0;
+	}
+
+	if (!eox_seen)
+		midi_outc(orig_dev, 0xf7);
+	return 0;
+}
+EXPORT_SYMBOL(midi_synth_load_patch);
+
+void midi_synth_panning(int dev, int channel, int pressure)
+{
+}
+EXPORT_SYMBOL(midi_synth_panning);
+
+void midi_synth_aftertouch(int dev, int channel, int pressure)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, chn;
+
+	if (pressure < 0 || pressure > 127)
+		return;
+	if (channel < 0 || channel > 15)
+		return;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (msg != 0xd0 || chn != channel)	/*
+						 * Test for running status
+						 */
+	  {
+		  if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f)))
+			  return;
+		  midi_outc(orig_dev, 0xd0 | (channel & 0x0f));		/*
+									 * Channel pressure
+									 */
+	} else if (!prefix_cmd(orig_dev, pressure))
+		return;
+
+	midi_outc(orig_dev, pressure);
+}
+EXPORT_SYMBOL(midi_synth_aftertouch);
+
+void
+midi_synth_controller(int dev, int channel, int ctrl_num, int value)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             chn, msg;
+
+	if (ctrl_num < 0 || ctrl_num > 127)
+		return;
+	if (channel < 0 || channel > 15)
+		return;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (msg != 0xb0 || chn != channel)
+	  {
+		  if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f)))
+			  return;
+		  midi_outc(orig_dev, 0xb0 | (channel & 0x0f));
+	} else if (!prefix_cmd(orig_dev, ctrl_num))
+		return;
+
+	midi_outc(orig_dev, ctrl_num);
+	midi_outc(orig_dev, value & 0x7f);
+}
+EXPORT_SYMBOL(midi_synth_controller);
+
+void
+midi_synth_bender(int dev, int channel, int value)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, prev_chn;
+
+	if (channel < 0 || channel > 15)
+		return;
+
+	if (value < 0 || value > 16383)
+		return;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	prev_chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (msg != 0xd0 || prev_chn != channel)		/*
+							 * Test for running status
+							 */
+	  {
+		  if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f)))
+			  return;
+		  midi_outc(orig_dev, 0xe0 | (channel & 0x0f));
+	} else if (!prefix_cmd(orig_dev, value & 0x7f))
+		return;
+
+	midi_outc(orig_dev, value & 0x7f);
+	midi_outc(orig_dev, (value >> 7) & 0x7f);
+}
+EXPORT_SYMBOL(midi_synth_bender);
+
+void
+midi_synth_setup_voice(int dev, int voice, int channel)
+{
+}
+EXPORT_SYMBOL(midi_synth_setup_voice);
+
+int
+midi_synth_send_sysex(int dev, unsigned char *bytes, int len)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             i;
+
+	for (i = 0; i < len; i++)
+	  {
+		  switch (bytes[i])
+		    {
+		    case 0xf0:	/* Start sysex */
+			    if (!prefix_cmd(orig_dev, 0xf0))
+				    return 0;
+			    sysex_state[dev] = 1;
+			    break;
+
+		    case 0xf7:	/* End sysex */
+			    if (!sysex_state[dev])	/* Orphan sysex end */
+				    return 0;
+			    sysex_state[dev] = 0;
+			    break;
+
+		    default:
+			    if (!sysex_state[dev])
+				    return 0;
+
+			    if (bytes[i] & 0x80)	/* Error. Another message before sysex end */
+			      {
+				      bytes[i] = 0xf7;	/* Sysex end */
+				      sysex_state[dev] = 0;
+			      }
+		    }
+
+		  if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]))
+		    {
+/*
+ * Hardware level buffer is full. Abort the sysex message.
+ */
+
+			    int             timeout = 0;
+
+			    bytes[i] = 0xf7;
+			    sysex_state[dev] = 0;
+
+			    while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) &&
+				   timeout < 1000)
+				    timeout++;
+		    }
+		  if (!sysex_state[dev])
+			  return 0;
+	  }
+
+	return 0;
+}
+EXPORT_SYMBOL(midi_synth_send_sysex);
+
diff --git a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h
new file mode 100644
index 00000000..b64ddd6c
--- /dev/null
+++ b/sound/oss/midi_synth.h
@@ -0,0 +1,47 @@
+int midi_synth_ioctl (int dev,
+	    unsigned int cmd, void __user * arg);
+int midi_synth_kill_note (int dev, int channel, int note, int velocity);
+int midi_synth_set_instr (int dev, int channel, int instr_no);
+int midi_synth_start_note (int dev, int channel, int note, int volume);
+void midi_synth_reset (int dev);
+int midi_synth_open (int dev, int mode);
+void midi_synth_close (int dev);
+void midi_synth_hw_control (int dev, unsigned char *event);
+int midi_synth_load_patch (int dev, int format, const char __user * addr,
+		 int count, int pmgr_flag);
+void midi_synth_panning (int dev, int channel, int pressure);
+void midi_synth_aftertouch (int dev, int channel, int pressure);
+void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
+void midi_synth_bender (int dev, int chn, int value);
+void midi_synth_setup_voice (int dev, int voice, int chn);
+int midi_synth_send_sysex(int dev, unsigned char *bytes,int len);
+
+#ifndef _MIDI_SYNTH_C_
+static struct synth_info std_synth_info =
+{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
+
+static struct synth_operations std_midi_synth =
+{
+	.owner		= THIS_MODULE,
+	.id		= "MIDI",
+	.info		= &std_synth_info,
+	.midi_dev	= 0,
+	.synth_type	= SYNTH_TYPE_MIDI,
+	.synth_subtype	= 0,
+	.open		= midi_synth_open,
+	.close		= midi_synth_close,
+	.ioctl		= midi_synth_ioctl,
+	.kill_note	= midi_synth_kill_note,
+	.start_note	= midi_synth_start_note,
+	.set_instr	= midi_synth_set_instr,
+	.reset		= midi_synth_reset,
+	.hw_control	= midi_synth_hw_control,
+	.load_patch	= midi_synth_load_patch,
+	.aftertouch	= midi_synth_aftertouch,
+	.controller	= midi_synth_controller,
+	.panning		= midi_synth_panning,
+	.bender		= midi_synth_bender,
+	.setup_voice	= midi_synth_setup_voice,
+	.send_sysex	= midi_synth_send_sysex
+};
+#endif
diff --git a/sound/oss/midibuf.c b/sound/oss/midibuf.c
new file mode 100644
index 00000000..8cdb2cfe
--- /dev/null
+++ b/sound/oss/midibuf.c
@@ -0,0 +1,425 @@
+/*
+ * sound/oss/midibuf.c
+ *
+ * Device file manager for /dev/midi#
+ */
+/*
+ * 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)
+ */
+#include <linux/stddef.h>
+#include <linux/kmod.h>
+#include <linux/spinlock.h>
+#define MIDIBUF_C
+
+#include "sound_config.h"
+
+
+/*
+ * Don't make MAX_QUEUE_SIZE larger than 4000
+ */
+
+#define MAX_QUEUE_SIZE	4000
+
+static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV];
+static wait_queue_head_t input_sleeper[MAX_MIDI_DEV];
+
+struct midi_buf
+{
+	int len, head, tail;
+	unsigned char queue[MAX_QUEUE_SIZE];
+};
+
+struct midi_parms
+{
+	long prech_timeout;	/*
+				 * Timeout before the first ch
+				 */
+};
+
+static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL};
+static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL};
+static struct midi_parms parms[MAX_MIDI_DEV];
+
+static void midi_poll(unsigned long dummy);
+
+
+static DEFINE_TIMER(poll_timer, midi_poll, 0, 0);
+
+static volatile int open_devs;
+static DEFINE_SPINLOCK(lock);
+
+#define DATA_AVAIL(q) (q->len)
+#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
+
+#define QUEUE_BYTE(q, data) \
+	if (SPACE_AVAIL(q)) \
+	{ \
+	  unsigned long flags; \
+	  spin_lock_irqsave(&lock, flags); \
+	  q->queue[q->tail] = (data); \
+	  q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
+	  spin_unlock_irqrestore(&lock, flags); \
+	}
+
+#define REMOVE_BYTE(q, data) \
+	if (DATA_AVAIL(q)) \
+	{ \
+	  unsigned long flags; \
+	  spin_lock_irqsave(&lock, flags); \
+	  data = q->queue[q->head]; \
+	  q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
+	  spin_unlock_irqrestore(&lock, flags); \
+	}
+
+static void drain_midi_queue(int dev)
+{
+
+	/*
+	 * Give the Midi driver time to drain its output queues
+	 */
+
+	if (midi_devs[dev]->buffer_status != NULL)
+		while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev)) 
+			interruptible_sleep_on_timeout(&midi_sleeper[dev],
+						       HZ/10);
+}
+
+static void midi_input_intr(int dev, unsigned char data)
+{
+	if (midi_in_buf[dev] == NULL)
+		return;
+
+	if (data == 0xfe)	/*
+				 * Active sensing
+				 */
+		return;		/*
+				 * Ignore
+				 */
+
+	if (SPACE_AVAIL(midi_in_buf[dev])) {
+		QUEUE_BYTE(midi_in_buf[dev], data);
+		wake_up(&input_sleeper[dev]);
+	}
+}
+
+static void midi_output_intr(int dev)
+{
+	/*
+	 * Currently NOP
+	 */
+}
+
+static void midi_poll(unsigned long dummy)
+{
+	unsigned long   flags;
+	int             dev;
+
+	spin_lock_irqsave(&lock, flags);
+	if (open_devs)
+	{
+		for (dev = 0; dev < num_midis; dev++)
+			if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
+			{
+				while (DATA_AVAIL(midi_out_buf[dev]))
+				{
+					int ok;
+					int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
+
+					spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
+					ok = midi_devs[dev]->outputc(dev, c);
+					spin_lock_irqsave(&lock, flags);
+					if (!ok)
+						break;
+					midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
+					midi_out_buf[dev]->len--;
+				}
+
+				if (DATA_AVAIL(midi_out_buf[dev]) < 100)
+					wake_up(&midi_sleeper[dev]);
+			}
+		poll_timer.expires = (1) + jiffies;
+		add_timer(&poll_timer);
+		/*
+		 * Come back later
+		 */
+	}
+	spin_unlock_irqrestore(&lock, flags);
+}
+
+int MIDIbuf_open(int dev, struct file *file)
+{
+	int mode, err;
+
+	dev = dev >> 4;
+	mode = translate_mode(file);
+
+	if (num_midis > MAX_MIDI_DEV)
+	{
+		printk(KERN_ERR "midi: Too many midi interfaces\n");
+		num_midis = MAX_MIDI_DEV;
+	}
+	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+		  return -ENXIO;
+	/*
+	 *    Interrupts disabled. Be careful
+	 */
+
+	module_put(midi_devs[dev]->owner);
+
+	if ((err = midi_devs[dev]->open(dev, mode,
+				 midi_input_intr, midi_output_intr)) < 0)
+		return err;
+
+	parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT;
+	midi_in_buf[dev] = vmalloc(sizeof(struct midi_buf));
+
+	if (midi_in_buf[dev] == NULL)
+	{
+		printk(KERN_WARNING "midi: Can't allocate buffer\n");
+		midi_devs[dev]->close(dev);
+		return -EIO;
+	}
+	midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
+
+	midi_out_buf[dev] = vmalloc(sizeof(struct midi_buf));
+
+	if (midi_out_buf[dev] == NULL)
+	{
+		printk(KERN_WARNING "midi: Can't allocate buffer\n");
+		midi_devs[dev]->close(dev);
+		vfree(midi_in_buf[dev]);
+		midi_in_buf[dev] = NULL;
+		return -EIO;
+	}
+	midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
+	open_devs++;
+
+	init_waitqueue_head(&midi_sleeper[dev]);
+	init_waitqueue_head(&input_sleeper[dev]);
+
+	if (open_devs < 2)	/* This was first open */
+	{
+		poll_timer.expires = 1 + jiffies;
+		add_timer(&poll_timer);	/* Start polling */
+	}
+	return err;
+}
+
+void MIDIbuf_release(int dev, struct file *file)
+{
+	int mode;
+
+	dev = dev >> 4;
+	mode = translate_mode(file);
+
+	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+		return;
+
+	/*
+	 * Wait until the queue is empty
+	 */
+
+	if (mode != OPEN_READ)
+	{
+		midi_devs[dev]->outputc(dev, 0xfe);	/*
+							   * Active sensing to shut the
+							   * devices
+							 */
+
+		while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev]))
+			  interruptible_sleep_on(&midi_sleeper[dev]);
+		/*
+		 *	Sync
+		 */
+
+		drain_midi_queue(dev);	/*
+					 * Ensure the output queues are empty
+					 */
+	}
+
+	midi_devs[dev]->close(dev);
+
+	open_devs--;
+	if (open_devs == 0)
+		del_timer_sync(&poll_timer);
+	vfree(midi_in_buf[dev]);
+	vfree(midi_out_buf[dev]);
+	midi_in_buf[dev] = NULL;
+	midi_out_buf[dev] = NULL;
+
+	module_put(midi_devs[dev]->owner);
+}
+
+int MIDIbuf_write(int dev, struct file *file, const char __user *buf, int count)
+{
+	int c, n, i;
+	unsigned char tmp_data;
+
+	dev = dev >> 4;
+
+	if (!count)
+		return 0;
+
+	c = 0;
+
+	while (c < count)
+	{
+		n = SPACE_AVAIL(midi_out_buf[dev]);
+
+		if (n == 0) {	/*
+				 * No space just now.
+				 */
+
+			if (file->f_flags & O_NONBLOCK) {
+				c = -EAGAIN;
+				goto out;
+			}
+
+			interruptible_sleep_on(&midi_sleeper[dev]);
+			if (signal_pending(current)) 
+			{
+				c = -EINTR;
+				goto out;
+			}
+			n = SPACE_AVAIL(midi_out_buf[dev]);
+		}
+		if (n > (count - c))
+			n = count - c;
+
+		for (i = 0; i < n; i++)
+		{
+			/* BROKE BROKE BROKE - CAN'T DO THIS WITH CLI !! */
+			/* yes, think the same, so I removed the cli() brackets 
+				QUEUE_BYTE is protected against interrupts */
+			if (copy_from_user((char *) &tmp_data, &(buf)[c], 1)) {
+				c = -EFAULT;
+				goto out;
+			}
+			QUEUE_BYTE(midi_out_buf[dev], tmp_data);
+			c++;
+		}
+	}
+out:
+	return c;
+}
+
+
+int MIDIbuf_read(int dev, struct file *file, char __user *buf, int count)
+{
+	int n, c = 0;
+	unsigned char tmp_data;
+
+	dev = dev >> 4;
+
+	if (!DATA_AVAIL(midi_in_buf[dev])) {	/*
+						 * No data yet, wait
+						 */
+ 		if (file->f_flags & O_NONBLOCK) {
+ 			c = -EAGAIN;
+			goto out;
+ 		}
+		interruptible_sleep_on_timeout(&input_sleeper[dev],
+					       parms[dev].prech_timeout);
+
+		if (signal_pending(current))
+			c = -EINTR;	/* The user is getting restless */
+	}
+	if (c == 0 && DATA_AVAIL(midi_in_buf[dev]))	/*
+							 * Got some bytes
+							 */
+	{
+		n = DATA_AVAIL(midi_in_buf[dev]);
+		if (n > count)
+			n = count;
+		c = 0;
+
+		while (c < n)
+		{
+			char *fixit;
+			REMOVE_BYTE(midi_in_buf[dev], tmp_data);
+			fixit = (char *) &tmp_data;
+			/* BROKE BROKE BROKE */
+			/* yes removed the cli() brackets again
+			 should q->len,tail&head be atomic_t? */
+			if (copy_to_user(&(buf)[c], fixit, 1)) {
+				c = -EFAULT;
+				goto out;
+			}
+			c++;
+		}
+	}
+out:
+	return c;
+}
+
+int MIDIbuf_ioctl(int dev, struct file *file,
+		  unsigned int cmd, void __user *arg)
+{
+	int val;
+
+	dev = dev >> 4;
+	
+	if (((cmd >> 8) & 0xff) == 'C') 
+	{
+		if (midi_devs[dev]->coproc)	/* Coprocessor ioctl */
+			return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
+/*		printk("/dev/midi%d: No coprocessor for this device\n", dev);*/
+		return -ENXIO;
+	}
+	else
+	{
+		switch (cmd) 
+		{
+			case SNDCTL_MIDI_PRETIME:
+				if (get_user(val, (int __user *)arg))
+					return -EFAULT;
+				if (val < 0)
+					val = 0;
+				val = (HZ * val) / 10;
+				parms[dev].prech_timeout = val;
+				return put_user(val, (int __user *)arg);
+			
+			default:
+				if (!midi_devs[dev]->ioctl)
+					return -EINVAL;
+				return midi_devs[dev]->ioctl(dev, cmd, arg);
+		}
+	}
+}
+
+/* No kernel lock - fine */
+unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+
+	dev = dev >> 4;
+
+	/* input */
+	poll_wait(file, &input_sleeper[dev], wait);
+	if (DATA_AVAIL(midi_in_buf[dev]))
+		mask |= POLLIN | POLLRDNORM;
+
+	/* output */
+	poll_wait(file, &midi_sleeper[dev], wait);
+	if (!SPACE_AVAIL(midi_out_buf[dev]))
+		mask |= POLLOUT | POLLWRNORM;
+	
+	return mask;
+}
+
+
+int MIDIbuf_avail(int dev)
+{
+	if (midi_in_buf[dev])
+		return DATA_AVAIL (midi_in_buf[dev]);
+	return 0;
+}
+EXPORT_SYMBOL(MIDIbuf_avail);
+
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
new file mode 100644
index 00000000..25e4609f
--- /dev/null
+++ b/sound/oss/mpu401.c
@@ -0,0 +1,1806 @@
+/*
+ * sound/oss/mpu401.c
+ *
+ * The low level driver for Roland MPU-401 compatible Midi cards.
+ */
+/*
+ * 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)
+ * Alan Cox		modularisation, use normal request_irq, use dev_id
+ * Bartlomiej Zolnierkiewicz	removed some __init to allow using many drivers
+ * Chris Rankin		Update the module-usage counter for the coprocessor
+ * Zwane Mwaikambo	Changed attach/unload resource freeing
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+
+#include "coproc.h"
+#include "mpu401.h"
+
+static int      timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;
+
+struct mpu_config
+{
+	int             base;	/*
+				 * I/O base
+				 */
+	int             irq;
+	int             opened;	/*
+				 * Open mode
+				 */
+	int             devno;
+	int             synthno;
+	int             uart_mode;
+	int             initialized;
+	int             mode;
+#define MODE_MIDI	1
+#define MODE_SYNTH	2
+	unsigned char   version, revision;
+	unsigned int    capabilities;
+#define MPU_CAP_INTLG	0x10000000
+#define MPU_CAP_SYNC	0x00000010
+#define MPU_CAP_FSK	0x00000020
+#define MPU_CAP_CLS	0x00000040
+#define MPU_CAP_SMPTE 	0x00000080
+#define MPU_CAP_2PORT	0x00000001
+	int             timer_flag;
+
+#define MBUF_MAX	10
+#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
+	{printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n",  dc->m_ptr,  dc->m_left,  dc->m_state);dc->m_ptr--;}
+	  int             m_busy;
+	  unsigned char   m_buf[MBUF_MAX];
+	  int             m_ptr;
+	  int             m_state;
+	  int             m_left;
+	  unsigned char   last_status;
+	  void            (*inputintr) (int dev, unsigned char data);
+	  int             shared_irq;
+	  int            *osp;
+	  spinlock_t	lock;
+  };
+
+#define	DATAPORT(base)   (base)
+#define	COMDPORT(base)   (base+1)
+#define	STATPORT(base)   (base+1)
+
+
+static void mpu401_close(int dev);
+
+static inline int mpu401_status(struct mpu_config *devc)
+{
+	return inb(STATPORT(devc->base));
+}
+
+#define input_avail(devc)		(!(mpu401_status(devc)&INPUT_AVAIL))
+#define output_ready(devc)		(!(mpu401_status(devc)&OUTPUT_READY))
+
+static inline void write_command(struct mpu_config *devc, unsigned char cmd)
+{
+	outb(cmd, COMDPORT(devc->base));
+}
+
+static inline int read_data(struct mpu_config *devc)
+{
+	return inb(DATAPORT(devc->base));
+}
+
+static inline void write_data(struct mpu_config *devc, unsigned char byte)
+{
+	outb(byte, DATAPORT(devc->base));
+}
+
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	MPU_RESET	0xFF
+#define	UART_MODE_ON	0x3F
+
+static struct mpu_config dev_conf[MAX_MIDI_DEV];
+
+static int n_mpu_devs;
+
+static int reset_mpu401(struct mpu_config *devc);
+static void set_uart_mode(int dev, struct mpu_config *devc, int arg);
+
+static int mpu_timer_init(int midi_dev);
+static void mpu_timer_interrupt(void);
+static void timer_ext_event(struct mpu_config *devc, int event, int parm);
+
+static struct synth_info mpu_synth_info_proto = {
+	"MPU-401 MIDI interface", 
+	0, 
+	SYNTH_TYPE_MIDI, 
+	MIDI_TYPE_MPU401, 
+	0, 128, 
+	0, 128, 
+	SYNTH_CAP_INPUT
+};
+
+static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
+
+/*
+ * States for the input scanner
+ */
+
+#define ST_INIT			0	/* Ready for timing byte or msg */
+#define ST_TIMED		1	/* Leading timing byte rcvd */
+#define ST_DATABYTE		2	/* Waiting for (nr_left) data bytes */
+
+#define ST_SYSMSG		100	/* System message (sysx etc). */
+#define ST_SYSEX		101	/* System exclusive msg */
+#define ST_MTC			102	/* Midi Time Code (MTC) qframe msg */
+#define ST_SONGSEL		103	/* Song select */
+#define ST_SONGPOS		104	/* Song position pointer */
+
+static unsigned char len_tab[] =	/* # of data bytes following a status
+					 */
+{
+	2,			/* 8x */
+	2,			/* 9x */
+	2,			/* Ax */
+	2,			/* Bx */
+	1,			/* Cx */
+	1,			/* Dx */
+	2,			/* Ex */
+	0			/* Fx */
+};
+
+#define STORE(cmd) \
+{ \
+	int len; \
+	unsigned char obuf[8]; \
+	cmd; \
+	seq_input_event(obuf, len); \
+}
+
+#define _seqbuf obuf
+#define _seqbufptr 0
+#define _SEQ_ADVBUF(x) len=x
+
+static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic)
+{
+
+	switch (devc->m_state)
+	{
+		case ST_INIT:
+			switch (midic)
+			{
+				case 0xf8:
+				/* Timer overflow */
+					break;
+
+				case 0xfc:
+					printk("<all end>");
+			 		break;
+
+				case 0xfd:
+					if (devc->timer_flag)
+						mpu_timer_interrupt();
+					break;
+
+				case 0xfe:
+					return MPU_ACK;
+
+				case 0xf0:
+				case 0xf1:
+				case 0xf2:
+				case 0xf3:
+				case 0xf4:
+				case 0xf5:
+				case 0xf6:
+				case 0xf7:
+					printk("<Trk data rq #%d>", midic & 0x0f);
+					break;
+
+				case 0xf9:
+					printk("<conductor rq>");
+					break;
+
+				case 0xff:
+					devc->m_state = ST_SYSMSG;
+					break;
+
+				default:
+					if (midic <= 0xef)
+					{
+						/* printk( "mpu time: %d ",  midic); */
+						devc->m_state = ST_TIMED;
+					}
+					else
+						printk("<MPU: Unknown event %02x> ", midic);
+			}
+			break;
+
+		case ST_TIMED:
+			{
+				int msg = ((int) (midic & 0xf0) >> 4);
+
+				devc->m_state = ST_DATABYTE;
+
+				if (msg < 8)	/* Data byte */
+				{
+					/* printk( "midi msg (running status) "); */
+					msg = ((int) (devc->last_status & 0xf0) >> 4);
+					msg -= 8;
+					devc->m_left = len_tab[msg] - 1;
+
+					devc->m_ptr = 2;
+					devc->m_buf[0] = devc->last_status;
+					devc->m_buf[1] = midic;
+
+					if (devc->m_left <= 0)
+					{
+						devc->m_state = ST_INIT;
+						do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+						devc->m_ptr = 0;
+					}
+				}
+				else if (msg == 0xf)	/* MPU MARK */
+				{
+					devc->m_state = ST_INIT;
+
+					switch (midic)
+					{
+						case 0xf8:
+							/* printk( "NOP "); */
+							break;
+
+						case 0xf9:
+							/* printk( "meas end "); */
+							break;
+
+						case 0xfc:
+							/* printk( "data end "); */
+							break;
+
+						default:
+							printk("Unknown MPU mark %02x\n", midic);
+					}
+				}
+				else
+				{
+					devc->last_status = midic;
+					/* printk( "midi msg "); */
+					msg -= 8;
+					devc->m_left = len_tab[msg];
+
+					devc->m_ptr = 1;
+					devc->m_buf[0] = midic;
+
+					if (devc->m_left <= 0)
+					{
+						devc->m_state = ST_INIT;
+						do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+						devc->m_ptr = 0;
+					}
+				}
+			}
+			break;
+
+		case ST_SYSMSG:
+			switch (midic)
+			{
+				case 0xf0:
+					printk("<SYX>");
+					devc->m_state = ST_SYSEX;
+					break;
+
+				case 0xf1:
+					devc->m_state = ST_MTC;
+					break;
+
+				case 0xf2:
+					devc->m_state = ST_SONGPOS;
+					devc->m_ptr = 0;
+					break;
+
+				case 0xf3:
+					devc->m_state = ST_SONGSEL;
+					break;
+
+				case 0xf6:
+					/* printk( "tune_request\n"); */
+					devc->m_state = ST_INIT;
+
+					/*
+					 *    Real time messages
+					 */
+				case 0xf8:
+					/* midi clock */
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_CLOCK, 0);
+					break;
+
+				case 0xfA:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_START, 0);
+					break;
+
+				case 0xFB:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_CONTINUE, 0);
+					break;
+
+				case 0xFC:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_STOP, 0);
+					break;
+
+				case 0xFE:
+					/* active sensing */
+					devc->m_state = ST_INIT;
+					break;
+
+				case 0xff:
+					/* printk( "midi hard reset"); */
+					devc->m_state = ST_INIT;
+					break;
+
+				default:
+					printk("unknown MIDI sysmsg %0x\n", midic);
+					devc->m_state = ST_INIT;
+			}
+			break;
+
+		case ST_MTC:
+			devc->m_state = ST_INIT;
+			printk("MTC frame %x02\n", midic);
+			break;
+
+		case ST_SYSEX:
+			if (midic == 0xf7)
+			{
+				printk("<EOX>");
+				devc->m_state = ST_INIT;
+			}
+			else
+				printk("%02x ", midic);
+			break;
+
+		case ST_SONGPOS:
+			BUFTEST(devc);
+			devc->m_buf[devc->m_ptr++] = midic;
+			if (devc->m_ptr == 2)
+			{
+				devc->m_state = ST_INIT;
+				devc->m_ptr = 0;
+				timer_ext_event(devc, TMR_SPP,
+					((devc->m_buf[1] & 0x7f) << 7) |
+					(devc->m_buf[0] & 0x7f));
+			}
+			break;
+
+		case ST_DATABYTE:
+			BUFTEST(devc);
+			devc->m_buf[devc->m_ptr++] = midic;
+			if ((--devc->m_left) <= 0)
+			{
+				devc->m_state = ST_INIT;
+				do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+				devc->m_ptr = 0;
+			}
+			break;
+
+		default:
+			printk("Bad state %d ", devc->m_state);
+			devc->m_state = ST_INIT;
+	}
+	return 1;
+}
+
+static void mpu401_input_loop(struct mpu_config *devc)
+{
+	unsigned long flags;
+	int busy;
+	int n;
+
+	spin_lock_irqsave(&devc->lock,flags);
+	busy = devc->m_busy;
+	devc->m_busy = 1;
+	spin_unlock_irqrestore(&devc->lock,flags);
+
+	if (busy)		/* Already inside the scanner */
+		return;
+
+	n = 50;
+
+	while (input_avail(devc) && n-- > 0)
+	{
+		unsigned char c = read_data(devc);
+
+		if (devc->mode == MODE_SYNTH)
+		{
+			mpu_input_scanner(devc, c);
+		}
+		else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
+			devc->inputintr(devc->devno, c);
+	}
+	devc->m_busy = 0;
+}
+
+static irqreturn_t mpuintr(int irq, void *dev_id)
+{
+	struct mpu_config *devc;
+	int dev = (int)(unsigned long) dev_id;
+	int handled = 0;
+
+	devc = &dev_conf[dev];
+
+	if (input_avail(devc))
+	{
+		handled = 1;
+		if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
+			mpu401_input_loop(devc);
+		else
+		{
+			/* Dummy read (just to acknowledge the interrupt) */
+			read_data(devc);
+		}
+	}
+	return IRQ_RETVAL(handled);
+}
+
+static int mpu401_open(int dev, int mode,
+	    void            (*input) (int dev, unsigned char data),
+	    void            (*output) (int dev)
+)
+{
+	int err;
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+		return -ENXIO;
+
+	devc = &dev_conf[dev];
+
+	if (devc->opened)
+		  return -EBUSY;
+	/*
+	 *  Verify that the device is really running.
+	 *  Some devices (such as Ensoniq SoundScape don't
+	 *  work before the on board processor (OBP) is initialized
+	 *  by downloading its microcode.
+	 */
+
+	if (!devc->initialized)
+	{
+		if (mpu401_status(devc) == 0xff)	/* Bus float */
+		{
+			printk(KERN_ERR "mpu401: Device not initialized properly\n");
+			return -EIO;
+		}
+		reset_mpu401(devc);
+	}
+
+	if ( (coprocessor = midi_devs[dev]->coproc) != NULL )
+	{
+		if (!try_module_get(coprocessor->owner)) {
+			mpu401_close(dev);
+			return -ENODEV;
+		}
+
+		if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
+		{
+			printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n");
+			mpu401_close(dev);
+			return err;
+		}
+	}
+	
+	set_uart_mode(dev, devc, 1);
+	devc->mode = MODE_MIDI;
+	devc->synthno = 0;
+
+	mpu401_input_loop(devc);
+
+	devc->inputintr = input;
+	devc->opened = mode;
+
+	return 0;
+}
+
+static void mpu401_close(int dev)
+{
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	devc = &dev_conf[dev];
+	if (devc->uart_mode)
+		reset_mpu401(devc);	/*
+					 * This disables the UART mode
+					 */
+	devc->mode = 0;
+	devc->inputintr = NULL;
+
+	coprocessor = midi_devs[dev]->coproc;
+	if (coprocessor) {
+		coprocessor->close(coprocessor->devc, COPR_MIDI);
+		module_put(coprocessor->owner);
+	}
+	devc->opened = 0;
+}
+
+static int mpu401_out(int dev, unsigned char midi_byte)
+{
+	int timeout;
+	unsigned long flags;
+
+	struct mpu_config *devc;
+
+	devc = &dev_conf[dev];
+
+	/*
+	 * Sometimes it takes about 30000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+
+	spin_lock_irqsave(&devc->lock,flags);
+	if (!output_ready(devc))
+	{
+		printk(KERN_WARNING "mpu401: Send data timeout\n");
+		spin_unlock_irqrestore(&devc->lock,flags);
+		return 0;
+	}
+	write_data(devc, midi_byte);
+	spin_unlock_irqrestore(&devc->lock,flags);
+	return 1;
+}
+
+static int mpu401_command(int dev, mpu_command_rec * cmd)
+{
+	int i, timeout, ok;
+	int ret = 0;
+	unsigned long   flags;
+	struct mpu_config *devc;
+
+	devc = &dev_conf[dev];
+
+	if (devc->uart_mode)	/*
+				 * Not possible in UART mode
+				 */
+	{
+		printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n");
+		return -EINVAL;
+	}
+	/*
+	 * Test for input since pending input seems to block the output.
+	 */
+	if (input_avail(devc))
+		mpu401_input_loop(devc);
+
+	/*
+	 * Sometimes it takes about 50000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	timeout = 50000;
+retry:
+	if (timeout-- <= 0)
+	{
+		printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd);
+		return -EIO;
+	}
+	spin_lock_irqsave(&devc->lock,flags);
+
+	if (!output_ready(devc))
+	{
+		spin_unlock_irqrestore(&devc->lock,flags);
+		goto retry;
+	}
+	write_command(devc, cmd->cmd);
+
+	ok = 0;
+	for (timeout = 50000; timeout > 0 && !ok; timeout--)
+	{
+		if (input_avail(devc))
+		{
+			if (devc->opened && devc->mode == MODE_SYNTH)
+			{
+				if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK)
+					ok = 1;
+			}
+			else
+			{
+				/* Device is not currently open. Use simpler method */
+				if (read_data(devc) == MPU_ACK)
+					ok = 1;
+			}
+		}
+	}
+	if (!ok)
+	{
+		spin_unlock_irqrestore(&devc->lock,flags);
+		return -EIO;
+	}
+	if (cmd->nr_args)
+	{
+		for (i = 0; i < cmd->nr_args; i++)
+		{
+			for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--);
+
+			if (!mpu401_out(dev, cmd->data[i]))
+			{
+				spin_unlock_irqrestore(&devc->lock,flags);
+				printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
+				return -EIO;
+			}
+		}
+	}
+	ret = 0;
+	cmd->data[0] = 0;
+
+	if (cmd->nr_returns)
+	{
+		for (i = 0; i < cmd->nr_returns; i++)
+		{
+			ok = 0;
+			for (timeout = 5000; timeout > 0 && !ok; timeout--)
+				if (input_avail(devc))
+				{
+					cmd->data[i] = read_data(devc);
+					ok = 1;
+				}
+			if (!ok)
+			{
+				spin_unlock_irqrestore(&devc->lock,flags);
+				return -EIO;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&devc->lock,flags);
+	return ret;
+}
+
+static int mpu_cmd(int dev, int cmd, int data)
+{
+	int ret;
+
+	static mpu_command_rec rec;
+
+	rec.cmd = cmd & 0xff;
+	rec.nr_args = ((cmd & 0xf0) == 0xE0);
+	rec.nr_returns = ((cmd & 0xf0) == 0xA0);
+	rec.data[0] = data & 0xff;
+
+	if ((ret = mpu401_command(dev, &rec)) < 0)
+		return ret;
+	return (unsigned char) rec.data[0];
+}
+
+static int mpu401_prefix_cmd(int dev, unsigned char status)
+{
+	struct mpu_config *devc = &dev_conf[dev];
+
+	if (devc->uart_mode)
+		return 1;
+
+	if (status < 0xf0)
+	{
+		if (mpu_cmd(dev, 0xD0, 0) < 0)
+			return 0;
+		return 1;
+	}
+	switch (status)
+	{
+		case 0xF0:
+			if (mpu_cmd(dev, 0xDF, 0) < 0)
+				return 0;
+			return 1;
+
+		default:
+			return 0;
+	}
+}
+
+static int mpu401_start_read(int dev)
+{
+	return 0;
+}
+
+static int mpu401_end_read(int dev)
+{
+	return 0;
+}
+
+static int mpu401_ioctl(int dev, unsigned cmd, void __user *arg)
+{
+	struct mpu_config *devc;
+	mpu_command_rec rec;
+	int val, ret;
+
+	devc = &dev_conf[dev];
+	switch (cmd) 
+	{
+		case SNDCTL_MIDI_MPUMODE:
+			if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */
+				printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n");
+				return -EINVAL;
+			}
+			if (get_user(val, (int __user *)arg))
+				return -EFAULT;
+			set_uart_mode(dev, devc, !val);
+			return 0;
+
+		case SNDCTL_MIDI_MPUCMD:
+			if (copy_from_user(&rec, arg, sizeof(rec)))
+				return -EFAULT;
+			if ((ret = mpu401_command(dev, &rec)) < 0)
+				return ret;
+			if (copy_to_user(arg, &rec, sizeof(rec)))
+				return -EFAULT;
+			return 0;
+
+		default:
+			return -EINVAL;
+	}
+}
+
+static void mpu401_kick(int dev)
+{
+}
+
+static int mpu401_buffer_status(int dev)
+{
+	return 0;		/*
+				 * No data in buffers
+				 */
+}
+
+static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	int midi_dev;
+	struct mpu_config *devc;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	if (midi_dev < 0 || midi_dev >= num_midis || midi_devs[midi_dev] == NULL)
+		return -ENXIO;
+
+	devc = &dev_conf[midi_dev];
+
+	switch (cmd)
+	{
+
+		case SNDCTL_SYNTH_INFO:
+			if (copy_to_user(arg, &mpu_synth_info[midi_dev],
+					sizeof(struct synth_info)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			return 0x7fffffff;
+
+		default:
+			return -EINVAL;
+	}
+}
+
+static int mpu_synth_open(int dev, int mode)
+{
+	int midi_dev, err;
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+		return -ENXIO;
+
+	devc = &dev_conf[midi_dev];
+
+	/*
+	 *  Verify that the device is really running.
+	 *  Some devices (such as Ensoniq SoundScape don't
+	 *  work before the on board processor (OBP) is initialized
+	 *  by downloading its microcode.
+	 */
+
+	if (!devc->initialized)
+	{
+		if (mpu401_status(devc) == 0xff)	/* Bus float */
+		{
+			printk(KERN_ERR "mpu401: Device not initialized properly\n");
+			return -EIO;
+		}
+		reset_mpu401(devc);
+	}
+	if (devc->opened)
+		return -EBUSY;
+	devc->mode = MODE_SYNTH;
+	devc->synthno = dev;
+
+	devc->inputintr = NULL;
+
+	coprocessor = midi_devs[midi_dev]->coproc;
+	if (coprocessor) {
+		if (!try_module_get(coprocessor->owner))
+			return -ENODEV;
+
+		if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
+		{
+			printk(KERN_WARNING "mpu401: Can't access coprocessor device\n");
+			return err;
+		}
+	}
+	devc->opened = mode;
+	reset_mpu401(devc);
+
+	if (mode & OPEN_READ)
+	{
+		mpu_cmd(midi_dev, 0x8B, 0);	/* Enable data in stop mode */
+		mpu_cmd(midi_dev, 0x34, 0);	/* Return timing bytes in stop mode */
+		mpu_cmd(midi_dev, 0x87, 0);	/* Enable pitch & controller */
+	}
+	return 0;
+}
+
+static void mpu_synth_close(int dev)
+{ 
+	int midi_dev;
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	devc = &dev_conf[midi_dev];
+	mpu_cmd(midi_dev, 0x15, 0);	/* Stop recording, playback and MIDI */
+	mpu_cmd(midi_dev, 0x8a, 0);	/* Disable data in stopped mode */
+
+	devc->inputintr = NULL;
+
+	coprocessor = midi_devs[midi_dev]->coproc;
+	if (coprocessor) {
+		coprocessor->close(coprocessor->devc, COPR_MIDI);
+		module_put(coprocessor->owner);
+	}
+	devc->opened = 0;
+	devc->mode = 0;
+}
+
+#define MIDI_SYNTH_NAME	"MPU-401 UART Midi"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct synth_operations mpu401_synth_proto =
+{
+	.owner		= THIS_MODULE,
+	.id		= "MPU401",
+	.info		= NULL,
+	.midi_dev	= 0,
+	.synth_type	= SYNTH_TYPE_MIDI,
+	.synth_subtype	= 0,
+	.open		= mpu_synth_open,
+	.close		= mpu_synth_close,
+	.ioctl		= mpu_synth_ioctl,
+	.kill_note	= midi_synth_kill_note,
+	.start_note	= midi_synth_start_note,
+	.set_instr	= midi_synth_set_instr,
+	.reset		= midi_synth_reset,
+	.hw_control	= midi_synth_hw_control,
+	.load_patch	= midi_synth_load_patch,
+	.aftertouch	= midi_synth_aftertouch,
+	.controller	= midi_synth_controller,
+	.panning	= midi_synth_panning,
+	.bender		= midi_synth_bender,
+	.setup_voice	= midi_synth_setup_voice,
+	.send_sysex	= midi_synth_send_sysex
+};
+
+static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV];
+
+static struct midi_operations mpu401_midi_proto =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
+	.in_info	= {0},
+	.open		= mpu401_open,
+	.close		= mpu401_close,
+	.ioctl		= mpu401_ioctl,
+	.outputc	= mpu401_out,
+	.start_read	= mpu401_start_read,
+	.end_read	= mpu401_end_read,
+	.kick		= mpu401_kick,
+	.buffer_status	= mpu401_buffer_status,
+	.prefix_cmd	= mpu401_prefix_cmd
+};
+
+static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
+
+static void mpu401_chk_version(int n, struct mpu_config *devc)
+{
+	int tmp;
+
+	devc->version = devc->revision = 0;
+
+	tmp = mpu_cmd(n, 0xAC, 0);
+	if (tmp < 0)
+		return;
+	if ((tmp & 0xf0) > 0x20)	/* Why it's larger than 2.x ??? */
+		return;
+	devc->version = tmp;
+
+	if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) {
+		devc->version = 0;
+		return;
+	}
+	devc->revision = tmp;
+}
+
+int attach_mpu401(struct address_info *hw_config, struct module *owner)
+{
+	unsigned long flags;
+	char revision_char;
+
+	int m, ret;
+	struct mpu_config *devc;
+
+	hw_config->slots[1] = -1;
+	m = sound_alloc_mididev();
+	if (m == -1)
+	{
+		printk(KERN_WARNING "MPU-401: Too many midi devices detected\n");
+		ret = -ENOMEM;
+		goto out_err;
+	}
+	devc = &dev_conf[m];
+	devc->base = hw_config->io_base;
+	devc->osp = hw_config->osp;
+	devc->irq = hw_config->irq;
+	devc->opened = 0;
+	devc->uart_mode = 0;
+	devc->initialized = 0;
+	devc->version = 0;
+	devc->revision = 0;
+	devc->capabilities = 0;
+	devc->timer_flag = 0;
+	devc->m_busy = 0;
+	devc->m_state = ST_INIT;
+	devc->shared_irq = hw_config->always_detect;
+	devc->irq = hw_config->irq;
+	spin_lock_init(&devc->lock);
+
+	if (devc->irq < 0)
+	{
+		devc->irq *= -1;
+		devc->shared_irq = 1;
+	}
+
+	if (!hw_config->always_detect)
+	{
+		/* Verify the hardware again */
+		if (!reset_mpu401(devc))
+		{
+			printk(KERN_WARNING "mpu401: Device didn't respond\n");
+			ret = -ENODEV;
+			goto out_mididev;
+		}
+		if (!devc->shared_irq)
+		{
+			if (request_irq(devc->irq, mpuintr, 0, "mpu401",
+					hw_config) < 0)
+			{
+				printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq);
+				ret = -ENOMEM;
+				goto out_mididev;
+			}
+		}
+		spin_lock_irqsave(&devc->lock,flags);
+		mpu401_chk_version(m, devc);
+		if (devc->version == 0)
+			mpu401_chk_version(m, devc);
+		spin_unlock_irqrestore(&devc->lock, flags);
+	}
+
+	if (devc->version != 0)
+		if (mpu_cmd(m, 0xC5, 0) >= 0)	/* Set timebase OK */
+			if (mpu_cmd(m, 0xE0, 120) >= 0)		/* Set tempo OK */
+				devc->capabilities |= MPU_CAP_INTLG;	/* Supports intelligent mode */
+
+
+	mpu401_synth_operations[m] = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
+
+	if (mpu401_synth_operations[m] == NULL)
+	{
+		printk(KERN_ERR "mpu401: Can't allocate memory\n");
+		ret = -ENOMEM;
+		goto out_irq;
+	}
+	if (!(devc->capabilities & MPU_CAP_INTLG))	/* No intelligent mode */
+	{
+		memcpy((char *) mpu401_synth_operations[m],
+			(char *) &std_midi_synth,
+			 sizeof(struct synth_operations));
+	}
+	else
+	{
+		memcpy((char *) mpu401_synth_operations[m],
+			(char *) &mpu401_synth_proto,
+			 sizeof(struct synth_operations));
+	}
+	if (owner)
+		mpu401_synth_operations[m]->owner = owner;
+
+	memcpy((char *) &mpu401_midi_operations[m],
+	       (char *) &mpu401_midi_proto,
+	       sizeof(struct midi_operations));
+
+	mpu401_midi_operations[m].converter = mpu401_synth_operations[m];
+
+	memcpy((char *) &mpu_synth_info[m],
+	       (char *) &mpu_synth_info_proto,
+	       sizeof(struct synth_info));
+
+	n_mpu_devs++;
+
+	if (devc->version == 0x20 && devc->revision >= 0x07)	/* MusicQuest interface */
+	{
+		int ports = (devc->revision & 0x08) ? 32 : 16;
+
+		devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
+				MPU_CAP_CLS | MPU_CAP_2PORT;
+
+		revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
+		sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d",
+				ports,
+				revision_char,
+				n_mpu_devs);
+	}
+	else
+	{
+		revision_char = devc->revision ? devc->revision + '@' : ' ';
+		if ((int) devc->revision > ('Z' - '@'))
+			revision_char = '+';
+
+		devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
+
+		if (hw_config->name)
+			sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name);
+		else
+			sprintf(mpu_synth_info[m].name,
+				"MPU-401 %d.%d%c MIDI #%d",
+				(int) (devc->version & 0xf0) >> 4,
+				devc->version & 0x0f,
+				revision_char,
+				n_mpu_devs);
+	}
+
+	strcpy(mpu401_midi_operations[m].info.name,
+	       mpu_synth_info[m].name);
+
+	conf_printf(mpu_synth_info[m].name, hw_config);
+
+	mpu401_synth_operations[m]->midi_dev = devc->devno = m;
+	mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno];
+
+	if (devc->capabilities & MPU_CAP_INTLG)		/* Intelligent mode */
+		hw_config->slots[2] = mpu_timer_init(m);
+
+	midi_devs[m] = &mpu401_midi_operations[devc->devno];
+	
+	if (owner)
+		midi_devs[m]->owner = owner;
+
+	hw_config->slots[1] = m;
+	sequencer_init();
+	
+	return 0;
+
+out_irq:
+	free_irq(devc->irq, hw_config);
+out_mididev:
+	sound_unload_mididev(m);
+out_err:
+	release_region(hw_config->io_base, 2);
+	return ret;
+}
+
+static int reset_mpu401(struct mpu_config *devc)
+{
+	unsigned long flags;
+	int ok, timeout, n;
+	int timeout_limit;
+
+	/*
+	 * Send the RESET command. Try again if no success at the first time.
+	 * (If the device is in the UART mode, it will not ack the reset cmd).
+	 */
+
+	ok = 0;
+
+	timeout_limit = devc->initialized ? 30000 : 100000;
+	devc->initialized = 1;
+
+	for (n = 0; n < 2 && !ok; n++)
+	{
+		for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
+			  ok = output_ready(devc);
+
+		write_command(devc, MPU_RESET);	/*
+							   * Send MPU-401 RESET Command
+							 */
+
+		/*
+		 * Wait at least 25 msec. This method is not accurate so let's make the
+		 * loop bit longer. Cannot sleep since this is called during boot.
+		 */
+
+		for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
+		{
+			spin_lock_irqsave(&devc->lock,flags);
+			if (input_avail(devc))
+				if (read_data(devc) == MPU_ACK)
+					ok = 1;
+			spin_unlock_irqrestore(&devc->lock,flags);
+		}
+
+	}
+
+	devc->m_state = ST_INIT;
+	devc->m_ptr = 0;
+	devc->m_left = 0;
+	devc->last_status = 0;
+	devc->uart_mode = 0;
+
+	return ok;
+}
+
+static void set_uart_mode(int dev, struct mpu_config *devc, int arg)
+{
+	if (!arg && (devc->capabilities & MPU_CAP_INTLG))
+		return;
+	if ((devc->uart_mode == 0) == (arg == 0))
+		return;		/* Already set */
+	reset_mpu401(devc);	/* This exits the uart mode */
+
+	if (arg)
+	{
+		if (mpu_cmd(dev, UART_MODE_ON, 0) < 0)
+		{
+			printk(KERN_ERR "mpu401: Can't enter UART mode\n");
+			devc->uart_mode = 0;
+			return;
+		}
+	}
+	devc->uart_mode = arg;
+
+}
+
+int probe_mpu401(struct address_info *hw_config, struct resource *ports)
+{
+	int ok = 0;
+	struct mpu_config tmp_devc;
+
+	tmp_devc.base = hw_config->io_base;
+	tmp_devc.irq = hw_config->irq;
+	tmp_devc.initialized = 0;
+	tmp_devc.opened = 0;
+	tmp_devc.osp = hw_config->osp;
+
+	if (hw_config->always_detect)
+		return 1;
+
+	if (inb(hw_config->io_base + 1) == 0xff)
+	{
+		DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));
+		return 0;	/* Just bus float? */
+	}
+	ok = reset_mpu401(&tmp_devc);
+
+	if (!ok)
+	{
+		DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));
+	}
+	return ok;
+}
+
+void unload_mpu401(struct address_info *hw_config)
+{
+	void *p;
+	int n=hw_config->slots[1];
+		
+	if (n != -1) {
+		release_region(hw_config->io_base, 2);
+		if (hw_config->always_detect == 0 && hw_config->irq > 0)
+			free_irq(hw_config->irq, hw_config);
+		p=mpu401_synth_operations[n];
+		sound_unload_mididev(n);
+		sound_unload_timerdev(hw_config->slots[2]);
+		kfree(p);
+	}
+}
+
+/*****************************************************
+ *      Timer stuff
+ ****************************************************/
+
+static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;
+static volatile int curr_tempo, curr_timebase, hw_timebase;
+static int      max_timebase = 8;	/* 8*24=192 ppqn */
+static volatile unsigned long next_event_time;
+static volatile unsigned long curr_ticks, curr_clocks;
+static unsigned long prev_event_time;
+static int      metronome_mode;
+
+static unsigned long clocks2ticks(unsigned long clocks)
+{
+	/*
+	 * The MPU-401 supports just a limited set of possible timebase values.
+	 * Since the applications require more choices, the driver has to
+	 * program the HW to do its best and to convert between the HW and
+	 * actual timebases.
+	 */
+	return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
+}
+
+static void set_timebase(int midi_dev, int val)
+{
+	int hw_val;
+
+	if (val < 48)
+		val = 48;
+	if (val > 1000)
+		val = 1000;
+
+	hw_val = val;
+	hw_val = (hw_val + 12) / 24;
+	if (hw_val > max_timebase)
+		hw_val = max_timebase;
+
+	if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
+	{
+		printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24);
+		return;
+	}
+	hw_timebase = hw_val * 24;
+	curr_timebase = val;
+
+}
+
+static void tmr_reset(struct mpu_config *devc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock,flags);
+	next_event_time = (unsigned long) -1;
+	prev_event_time = 0;
+	curr_ticks = curr_clocks = 0;
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static void set_timer_mode(int midi_dev)
+{
+	if (timer_mode & TMR_MODE_CLS)
+		mpu_cmd(midi_dev, 0x3c, 0);	/* Use CLS sync */
+	else if (timer_mode & TMR_MODE_SMPTE)
+		mpu_cmd(midi_dev, 0x3d, 0);	/* Use SMPTE sync */
+
+	if (timer_mode & TMR_INTERNAL)
+	{
+		  mpu_cmd(midi_dev, 0x80, 0);	/* Use MIDI sync */
+	}
+	else
+	{
+		if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
+		{
+			mpu_cmd(midi_dev, 0x82, 0);		/* Use MIDI sync */
+			mpu_cmd(midi_dev, 0x91, 0);		/* Enable ext MIDI ctrl */
+		}
+		else if (timer_mode & TMR_MODE_FSK)
+			mpu_cmd(midi_dev, 0x81, 0);	/* Use FSK sync */
+	}
+}
+
+static void stop_metronome(int midi_dev)
+{
+	mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */
+}
+
+static void setup_metronome(int midi_dev)
+{
+	int numerator, denominator;
+	int clks_per_click, num_32nds_per_beat;
+	int beats_per_measure;
+
+	numerator = ((unsigned) metronome_mode >> 24) & 0xff;
+	denominator = ((unsigned) metronome_mode >> 16) & 0xff;
+	clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
+	num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
+	beats_per_measure = (numerator * 4) >> denominator;
+
+	if (!metronome_mode)
+		mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */
+	else
+	{
+		mpu_cmd(midi_dev, 0xE4, clks_per_click);
+		mpu_cmd(midi_dev, 0xE6, beats_per_measure);
+		mpu_cmd(midi_dev, 0x83, 0);	/* Enable metronome without accents */
+	}
+}
+
+static int mpu_start_timer(int midi_dev)
+{
+	struct mpu_config *devc= &dev_conf[midi_dev];
+
+	tmr_reset(devc);
+	set_timer_mode(midi_dev);
+
+	if (tmr_running)
+		return TIMER_NOT_ARMED;		/* Already running */
+
+	if (timer_mode & TMR_INTERNAL)
+	{
+		mpu_cmd(midi_dev, 0x02, 0);	/* Send MIDI start */
+		tmr_running = 1;
+		return TIMER_NOT_ARMED;
+	}
+	else
+	{
+		mpu_cmd(midi_dev, 0x35, 0);	/* Enable mode messages to PC */
+		mpu_cmd(midi_dev, 0x38, 0);	/* Enable sys common messages to PC */
+		mpu_cmd(midi_dev, 0x39, 0);	/* Enable real time messages to PC */
+		mpu_cmd(midi_dev, 0x97, 0);	/* Enable system exclusive messages to PC */
+	}
+	return TIMER_ARMED;
+}
+
+static int mpu_timer_open(int dev, int mode)
+{
+	int midi_dev = sound_timer_devs[dev]->devlink;
+	struct mpu_config *devc= &dev_conf[midi_dev];
+
+	if (timer_open)
+		return -EBUSY;
+
+	tmr_reset(devc);
+	curr_tempo = 50;
+	mpu_cmd(midi_dev, 0xE0, 50);
+	curr_timebase = hw_timebase = 120;
+	set_timebase(midi_dev, 120);
+	timer_open = 1;
+	metronome_mode = 0;
+	set_timer_mode(midi_dev);
+
+	mpu_cmd(midi_dev, 0xe7, 0x04);	/* Send all clocks to host */
+	mpu_cmd(midi_dev, 0x95, 0);	/* Enable clock to host */
+
+	return 0;
+}
+
+static void mpu_timer_close(int dev)
+{
+	int midi_dev = sound_timer_devs[dev]->devlink;
+
+	timer_open = tmr_running = 0;
+	mpu_cmd(midi_dev, 0x15, 0);	/* Stop all */
+	mpu_cmd(midi_dev, 0x94, 0);	/* Disable clock to host */
+	mpu_cmd(midi_dev, 0x8c, 0);	/* Disable measure end messages to host */
+	stop_metronome(midi_dev);
+}
+
+static int mpu_timer_event(int dev, unsigned char *event)
+{
+	unsigned char command = event[1];
+	unsigned long parm = *(unsigned int *) &event[4];
+	int midi_dev = sound_timer_devs[dev]->devlink;
+
+	switch (command)
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				if (parm <= curr_ticks)	/* It's the time */
+					return TIMER_NOT_ARMED;
+				time = parm;
+				next_event_time = prev_event_time = time;
+
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			if (tmr_running)
+				break;
+			return mpu_start_timer(midi_dev);
+
+		case TMR_STOP:
+			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
+			stop_metronome(midi_dev);
+			tmr_running = 0;
+			break;
+
+		case TMR_CONTINUE:
+			if (tmr_running)
+				break;
+			mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
+			setup_metronome(midi_dev);
+			tmr_running = 1;
+			break;
+
+		case TMR_TEMPO:
+			if (parm)
+			{
+				if (parm < 8)
+					parm = 8;
+			 	if (parm > 250)
+					parm = 250;
+				if (mpu_cmd(midi_dev, 0xE0, parm) < 0)
+					printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm);
+				curr_tempo = parm;
+			}
+			break;
+
+		case TMR_ECHO:
+			seq_copy_to_input(event, 8);
+			break;
+
+		case TMR_TIMESIG:
+			if (metronome_mode)	/* Metronome enabled */
+			{
+				metronome_mode = parm;
+				setup_metronome(midi_dev);
+			}
+			break;
+
+		default:;
+	}
+	return TIMER_NOT_ARMED;
+}
+
+static unsigned long mpu_timer_get_time(int dev)
+{
+	if (!timer_open)
+		return 0;
+
+	return curr_ticks;
+}
+
+static int mpu_timer_ioctl(int dev, unsigned int command, void __user *arg)
+{
+	int midi_dev = sound_timer_devs[dev]->devlink;
+	int __user *p = (int __user *)arg;
+
+	switch (command)
+	{
+		case SNDCTL_TMR_SOURCE:
+			{
+				int parm;
+
+				if (get_user(parm, p))
+					return -EFAULT;
+				parm &= timer_caps;
+
+				if (parm != 0)
+				{
+					timer_mode = parm;
+	
+					if (timer_mode & TMR_MODE_CLS)
+						mpu_cmd(midi_dev, 0x3c, 0);		/* Use CLS sync */
+					else if (timer_mode & TMR_MODE_SMPTE)
+						mpu_cmd(midi_dev, 0x3d, 0);		/* Use SMPTE sync */
+				}
+				if (put_user(timer_mode, p))
+					return -EFAULT;
+				return timer_mode;
+			}
+			break;
+
+		case SNDCTL_TMR_START:
+			mpu_start_timer(midi_dev);
+			return 0;
+
+		case SNDCTL_TMR_STOP:
+			tmr_running = 0;
+			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
+			stop_metronome(midi_dev);
+			return 0;
+
+		case SNDCTL_TMR_CONTINUE:
+			if (tmr_running)
+				return 0;
+			tmr_running = 1;
+			mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
+			return 0;
+
+		case SNDCTL_TMR_TIMEBASE:
+			{
+				int val;
+				if (get_user(val, p))
+					return -EFAULT;
+				if (val)
+					set_timebase(midi_dev, val);
+				if (put_user(curr_timebase, p))
+					return -EFAULT;
+				return curr_timebase;
+			}
+			break;
+
+		case SNDCTL_TMR_TEMPO:
+			{
+				int val;
+				int ret;
+
+				if (get_user(val, p))
+					return -EFAULT;
+
+				if (val)
+				{
+					if (val < 8)
+						val = 8;
+					if (val > 250)
+						val = 250;
+					if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)
+					{
+						printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val);
+						return ret;
+					}
+					curr_tempo = val;
+				}
+				if (put_user(curr_tempo, p))
+					return -EFAULT;
+				return curr_tempo;
+			}
+			break;
+
+		case SNDCTL_SEQ_CTRLRATE:
+			{
+				int val;
+				if (get_user(val, p))
+					return -EFAULT;
+
+				if (val != 0)		/* Can't change */
+					return -EINVAL;
+				val = ((curr_tempo * curr_timebase) + 30)/60;
+				if (put_user(val, p))
+					return -EFAULT;
+				return val;
+			}
+			break;
+
+		case SNDCTL_SEQ_GETTIME:
+			if (put_user(curr_ticks, p))
+				return -EFAULT;
+			return curr_ticks;
+
+		case SNDCTL_TMR_METRONOME:
+			if (get_user(metronome_mode, p))
+				return -EFAULT;
+			setup_metronome(midi_dev);
+			return 0;
+
+		default:;
+	}
+	return -EINVAL;
+}
+
+static void mpu_timer_arm(int dev, long time)
+{
+	if (time < 0)
+		time = curr_ticks + 1;
+	else if (time <= curr_ticks)	/* It's the time */
+		return;
+	next_event_time = prev_event_time = time;
+	return;
+}
+
+static struct sound_timer_operations mpu_timer =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"MPU-401 Timer", 0},
+	.priority	= 10,	/* Priority */
+	.devlink	= 0,	/* Local device link */
+	.open		= mpu_timer_open,
+	.close		= mpu_timer_close,
+	.event		= mpu_timer_event,
+	.get_time	= mpu_timer_get_time,
+	.ioctl		= mpu_timer_ioctl,
+	.arm_timer	= mpu_timer_arm
+};
+
+static void mpu_timer_interrupt(void)
+{
+	if (!timer_open)
+		return;
+
+	if (!tmr_running)
+		return;
+
+	curr_clocks++;
+	curr_ticks = clocks2ticks(curr_clocks);
+
+	if (curr_ticks >= next_event_time)
+	{
+		next_event_time = (unsigned long) -1;
+		sequencer_timer(0);
+	}
+}
+
+static void timer_ext_event(struct mpu_config *devc, int event, int parm)
+{
+	int midi_dev = devc->devno;
+
+	if (!devc->timer_flag)
+		return;
+
+	switch (event)
+	{
+		case TMR_CLOCK:
+			printk("<MIDI clk>");
+			break;
+
+		case TMR_START:
+			printk("Ext MIDI start\n");
+			if (!tmr_running)
+			{
+				if (timer_mode & TMR_EXTERNAL)
+				{
+					tmr_running = 1;
+					setup_metronome(midi_dev);
+					next_event_time = 0;
+					STORE(SEQ_START_TIMER());
+				}
+			}
+			break;
+
+		case TMR_STOP:
+			printk("Ext MIDI stop\n");
+			if (timer_mode & TMR_EXTERNAL)
+			{
+				tmr_running = 0;
+				stop_metronome(midi_dev);
+				STORE(SEQ_STOP_TIMER());
+			}
+			break;
+
+		case TMR_CONTINUE:
+			printk("Ext MIDI continue\n");
+			if (timer_mode & TMR_EXTERNAL)
+			{
+				tmr_running = 1;
+				setup_metronome(midi_dev);
+				STORE(SEQ_CONTINUE_TIMER());
+		  	}
+		  	break;
+
+		case TMR_SPP:
+			printk("Songpos: %d\n", parm);
+			if (timer_mode & TMR_EXTERNAL)
+			{
+				STORE(SEQ_SONGPOS(parm));
+			}
+			break;
+	}
+}
+
+static int mpu_timer_init(int midi_dev)
+{
+	struct mpu_config *devc;
+	int n;
+
+	devc = &dev_conf[midi_dev];
+
+	if (timer_initialized)
+		return -1;	/* There is already a similar timer */
+
+	timer_initialized = 1;
+
+	mpu_timer.devlink = midi_dev;
+	dev_conf[midi_dev].timer_flag = 1;
+
+	n = sound_alloc_timerdev();
+	if (n == -1)
+		n = 0;
+	sound_timer_devs[n] = &mpu_timer;
+
+	if (devc->version < 0x20)	/* Original MPU-401 */
+		timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
+	else
+	{
+		/*
+		 * The version number 2.0 is used (at least) by the
+		 * MusicQuest cards and the Roland Super-MPU.
+		 *
+		 * MusicQuest has given a special meaning to the bits of the
+		 * revision number. The Super-MPU returns 0.
+		 */
+
+		if (devc->revision)
+			timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
+
+		if (devc->revision & 0x02)
+			timer_caps |= TMR_MODE_CLS;
+
+
+		if (devc->revision & 0x40)
+			max_timebase = 10;	/* Has the 216 and 240 ppqn modes */
+	}
+
+	timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
+	return n;
+
+}
+
+EXPORT_SYMBOL(probe_mpu401);
+EXPORT_SYMBOL(attach_mpu401);
+EXPORT_SYMBOL(unload_mpu401);
+
+static struct address_info cfg;
+
+static int io = -1;
+static int irq = -1;
+
+module_param(irq, int, 0);
+module_param(io, int, 0);
+
+static int __init init_mpu401(void)
+{
+	int ret;
+	/* Can be loaded either for module use or to provide functions
+	   to others */
+	if (io != -1 && irq != -1) {
+		struct resource *ports;
+	        cfg.irq = irq;
+		cfg.io_base = io;
+		ports = request_region(io, 2, "mpu401");
+		if (!ports)
+			return -EBUSY;
+		if (probe_mpu401(&cfg, ports) == 0) {
+			release_region(io, 2);
+			return -ENODEV;
+		}
+		if ((ret = attach_mpu401(&cfg, THIS_MODULE)))
+			return ret;
+	}
+	
+	return 0;
+}
+
+static void __exit cleanup_mpu401(void)
+{
+	if (io != -1 && irq != -1) {
+		/* Check for use by, for example, sscape driver */
+		unload_mpu401(&cfg);
+	}
+}
+
+module_init(init_mpu401);
+module_exit(cleanup_mpu401);
+
+#ifndef MODULE
+static int __init setup_mpu401(char *str)
+{
+        /* io, irq */
+	int ints[3];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+	irq = ints[2];
+
+	return 1;
+}
+
+__setup("mpu401=", setup_mpu401);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/mpu401.h b/sound/oss/mpu401.h
new file mode 100644
index 00000000..0ad1e9ee
--- /dev/null
+++ b/sound/oss/mpu401.h
@@ -0,0 +1,11 @@
+
+/*	From uart401.c */
+int probe_uart401 (struct address_info *hw_config, struct module *owner);
+void unload_uart401 (struct address_info *hw_config);
+
+irqreturn_t uart401intr (int irq, void *dev_id);
+
+/*	From mpu401.c */
+int probe_mpu401(struct address_info *hw_config, struct resource *ports);
+int attach_mpu401(struct address_info * hw_config, struct module *owner);
+void unload_mpu401(struct address_info *hw_info);
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
new file mode 100644
index 00000000..c0cc951b
--- /dev/null
+++ b/sound/oss/msnd.c
@@ -0,0 +1,413 @@
+/*********************************************************************
+ *
+ * msnd.c - Driver Base
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/spinlock.h>
+#include <asm/irq.h>
+#include "msnd.h"
+
+#define LOGNAME			"msnd"
+
+#define MSND_MAX_DEVS		4
+
+static multisound_dev_t		*devs[MSND_MAX_DEVS];
+static int			num_devs;
+
+int msnd_register(multisound_dev_t *dev)
+{
+	int i;
+
+	for (i = 0; i < MSND_MAX_DEVS; ++i)
+		if (devs[i] == NULL)
+			break;
+
+	if (i == MSND_MAX_DEVS)
+		return -ENOMEM;
+
+	devs[i] = dev;
+	++num_devs;
+	return 0;
+}
+
+void msnd_unregister(multisound_dev_t *dev)
+{
+	int i;
+
+	for (i = 0; i < MSND_MAX_DEVS; ++i)
+		if (devs[i] == dev)
+			break;
+
+	if (i == MSND_MAX_DEVS) {
+		printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
+		return;
+	}
+
+	devs[i] = NULL;
+	--num_devs;
+}
+
+void msnd_init_queue(void __iomem *base, int start, int size)
+{
+	writew(PCTODSP_BASED(start), base + JQS_wStart);
+	writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
+	writew(0, base + JQS_wHead);
+	writew(0, base + JQS_wTail);
+}
+
+void msnd_fifo_init(msnd_fifo *f)
+{
+	f->data = NULL;
+}
+
+void msnd_fifo_free(msnd_fifo *f)
+{
+	vfree(f->data);
+	f->data = NULL;
+}
+
+int msnd_fifo_alloc(msnd_fifo *f, size_t n)
+{
+	msnd_fifo_free(f);
+	f->data = vmalloc(n);
+	f->n = n;
+	f->tail = 0;
+	f->head = 0;
+	f->len = 0;
+
+	if (!f->data)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void msnd_fifo_make_empty(msnd_fifo *f)
+{
+	f->len = f->tail = f->head = 0;
+}
+
+int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len)
+{
+	int count = 0;
+
+	while ((count < len) && (f->len != f->n)) {
+
+		int nwritten;
+
+		if (f->head <= f->tail) {
+			nwritten = len - count;
+			if (nwritten > f->n - f->tail)
+				nwritten = f->n - f->tail;
+		}
+		else {
+			nwritten = f->head - f->tail;
+			if (nwritten > len - count)
+				nwritten = len - count;
+		}
+
+		memcpy_fromio(f->data + f->tail, buf, nwritten);
+
+		count += nwritten;
+		buf += nwritten;
+		f->len += nwritten;
+		f->tail += nwritten;
+		f->tail %= f->n;
+	}
+
+	return count;
+}
+
+int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
+{
+	int count = 0;
+
+	while ((count < len) && (f->len != f->n)) {
+
+		int nwritten;
+
+		if (f->head <= f->tail) {
+			nwritten = len - count;
+			if (nwritten > f->n - f->tail)
+				nwritten = f->n - f->tail;
+		}
+		else {
+			nwritten = f->head - f->tail;
+			if (nwritten > len - count)
+				nwritten = len - count;
+		}
+
+		memcpy(f->data + f->tail, buf, nwritten);
+
+		count += nwritten;
+		buf += nwritten;
+		f->len += nwritten;
+		f->tail += nwritten;
+		f->tail %= f->n;
+	}
+
+	return count;
+}
+
+int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len)
+{
+	int count = 0;
+
+	while ((count < len) && (f->len > 0)) {
+
+		int nread;
+
+		if (f->tail <= f->head) {
+			nread = len - count;
+			if (nread > f->n - f->head)
+				nread = f->n - f->head;
+		}
+		else {
+			nread = f->tail - f->head;
+			if (nread > len - count)
+				nread = len - count;
+		}
+
+		memcpy_toio(buf, f->data + f->head, nread);
+
+		count += nread;
+		buf += nread;
+		f->len -= nread;
+		f->head += nread;
+		f->head %= f->n;
+	}
+
+	return count;
+}
+
+int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
+{
+	int count = 0;
+
+	while ((count < len) && (f->len > 0)) {
+
+		int nread;
+
+		if (f->tail <= f->head) {
+			nread = len - count;
+			if (nread > f->n - f->head)
+				nread = f->n - f->head;
+		}
+		else {
+			nread = f->tail - f->head;
+			if (nread > len - count)
+				nread = len - count;
+		}
+
+		memcpy(buf, f->data + f->head, nread);
+
+		count += nread;
+		buf += nread;
+		f->len -= nread;
+		f->head += nread;
+		f->head %= f->n;
+	}
+
+	return count;
+}
+
+static int msnd_wait_TXDE(multisound_dev_t *dev)
+{
+	register unsigned int io = dev->io;
+	register int timeout = 1000;
+    
+	while(timeout-- > 0)
+		if (msnd_inb(io + HP_ISR) & HPISR_TXDE)
+			return 0;
+
+	return -EIO;
+}
+
+static int msnd_wait_HC0(multisound_dev_t *dev)
+{
+	register unsigned int io = dev->io;
+	register int timeout = 1000;
+
+	while(timeout-- > 0)
+		if (!(msnd_inb(io + HP_CVR) & HPCVR_HC))
+			return 0;
+
+	return -EIO;
+}
+
+int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (msnd_wait_HC0(dev) == 0) {
+		msnd_outb(cmd, dev->io + HP_CVR);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
+
+	return -EIO;
+}
+
+int msnd_send_word(multisound_dev_t *dev, unsigned char high,
+		   unsigned char mid, unsigned char low)
+{
+	register unsigned int io = dev->io;
+
+	if (msnd_wait_TXDE(dev) == 0) {
+		msnd_outb(high, io + HP_TXH);
+		msnd_outb(mid, io + HP_TXM);
+		msnd_outb(low, io + HP_TXL);
+		return 0;
+	}
+
+	printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
+
+	return -EIO;
+}
+
+int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
+{
+	int i;
+
+	if (len % 3 != 0) {
+		printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");		
+		return -EINVAL;
+	}
+
+	for (i = 0; i < len; i += 3)
+		if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
+			return -EIO;
+
+	msnd_inb(dev->io + HP_RXL);
+	msnd_inb(dev->io + HP_CVR);
+
+	return 0;
+}
+
+int msnd_enable_irq(multisound_dev_t *dev)
+{
+	unsigned long flags;
+
+	if (dev->irq_ref++)
+		return 0;
+
+	printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (msnd_wait_TXDE(dev) == 0) {
+		msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
+		if (dev->type == msndClassic)
+			msnd_outb(dev->irqid, dev->io + HP_IRQM);
+		msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
+		msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
+		enable_irq(dev->irq);
+		msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
+
+	return -EIO;
+}
+
+int msnd_disable_irq(multisound_dev_t *dev)
+{
+	unsigned long flags;
+
+	if (--dev->irq_ref > 0)
+		return 0;
+
+	if (dev->irq_ref < 0)
+		printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
+
+	printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (msnd_wait_TXDE(dev) == 0) {
+		msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
+		if (dev->type == msndClassic)
+			msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM);
+		disable_irq(dev->irq);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
+
+	return -EIO;
+}
+
+#ifndef LINUX20
+EXPORT_SYMBOL(msnd_register);
+EXPORT_SYMBOL(msnd_unregister);
+
+EXPORT_SYMBOL(msnd_init_queue);
+
+EXPORT_SYMBOL(msnd_fifo_init);
+EXPORT_SYMBOL(msnd_fifo_free);
+EXPORT_SYMBOL(msnd_fifo_alloc);
+EXPORT_SYMBOL(msnd_fifo_make_empty);
+EXPORT_SYMBOL(msnd_fifo_write_io);
+EXPORT_SYMBOL(msnd_fifo_read_io);
+EXPORT_SYMBOL(msnd_fifo_write);
+EXPORT_SYMBOL(msnd_fifo_read);
+
+EXPORT_SYMBOL(msnd_send_dsp_cmd);
+EXPORT_SYMBOL(msnd_send_word);
+EXPORT_SYMBOL(msnd_upload_host);
+
+EXPORT_SYMBOL(msnd_enable_irq);
+EXPORT_SYMBOL(msnd_disable_irq);
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR				("Andrew Veliath <andrewtv@usa.net>");
+MODULE_DESCRIPTION			("Turtle Beach MultiSound Driver Base");
+MODULE_LICENSE("GPL");
+
+
+int init_module(void)
+{
+	return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
diff --git a/sound/oss/msnd.h b/sound/oss/msnd.h
new file mode 100644
index 00000000..c8be47ec
--- /dev/null
+++ b/sound/oss/msnd.h
@@ -0,0 +1,278 @@
+/*********************************************************************
+ *
+ * msnd.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ ********************************************************************/
+#ifndef __MSND_H
+#define __MSND_H
+
+#define VERSION			"0.8.3.1"
+
+#define DEFSAMPLERATE		DSP_DEFAULT_SPEED
+#define DEFSAMPLESIZE		AFMT_U8
+#define DEFCHANNELS		1
+
+#define DEFFIFOSIZE		128
+
+#define SNDCARD_MSND		38
+
+#define SRAM_BANK_SIZE		0x8000
+#define SRAM_CNTL_START		0x7F00
+
+#define DSP_BASE_ADDR		0x4000
+#define DSP_BANK_BASE		0x4000
+
+#define	HP_ICR			0x00
+#define	HP_CVR			0x01
+#define	HP_ISR			0x02
+#define	HP_IVR			0x03
+#define HP_NU			0x04
+#define HP_INFO			0x04
+#define	HP_TXH			0x05
+#define	HP_RXH			0x05
+#define	HP_TXM			0x06
+#define	HP_RXM			0x06
+#define	HP_TXL			0x07
+#define	HP_RXL			0x07
+
+#define HP_ICR_DEF		0x00
+#define HP_CVR_DEF		0x12
+#define HP_ISR_DEF		0x06
+#define HP_IVR_DEF		0x0f
+#define HP_NU_DEF		0x00
+
+#define	HP_IRQM			0x09
+
+#define	HPR_BLRC		0x08
+#define	HPR_SPR1		0x09
+#define	HPR_SPR2		0x0A
+#define	HPR_TCL0		0x0B
+#define	HPR_TCL1		0x0C
+#define	HPR_TCL2		0x0D
+#define	HPR_TCL3		0x0E
+#define	HPR_TCL4		0x0F
+
+#define	HPICR_INIT		0x80
+#define HPICR_HM1		0x40
+#define HPICR_HM0		0x20
+#define HPICR_HF1		0x10
+#define HPICR_HF0		0x08
+#define	HPICR_TREQ		0x02
+#define	HPICR_RREQ		0x01
+
+#define HPCVR_HC		0x80
+
+#define	HPISR_HREQ		0x80
+#define HPISR_DMA		0x40
+#define HPISR_HF3		0x10
+#define HPISR_HF2		0x08
+#define	HPISR_TRDY		0x04
+#define	HPISR_TXDE		0x02
+#define	HPISR_RXDF		0x01
+
+#define	HPIO_290		0
+#define	HPIO_260		1
+#define	HPIO_250		2
+#define	HPIO_240		3
+#define	HPIO_230		4
+#define	HPIO_220		5
+#define	HPIO_210		6
+#define	HPIO_3E0		7
+
+#define	HPMEM_NONE		0
+#define	HPMEM_B000		1
+#define	HPMEM_C800		2
+#define	HPMEM_D000		3
+#define	HPMEM_D400		4
+#define	HPMEM_D800		5
+#define	HPMEM_E000		6
+#define	HPMEM_E800		7
+
+#define	HPIRQ_NONE		0
+#define HPIRQ_5			1
+#define HPIRQ_7			2
+#define HPIRQ_9			3
+#define HPIRQ_10		4
+#define HPIRQ_11		5
+#define HPIRQ_12		6
+#define HPIRQ_15		7
+
+#define	HIMT_PLAY_DONE		0x00
+#define	HIMT_RECORD_DONE	0x01
+#define	HIMT_MIDI_EOS		0x02
+#define	HIMT_MIDI_OUT		0x03
+
+#define	HIMT_MIDI_IN_UCHAR	0x0E
+#define	HIMT_DSP		0x0F
+
+#define	HDEX_BASE	       	0x92
+#define	HDEX_PLAY_START		(0 + HDEX_BASE)
+#define	HDEX_PLAY_STOP		(1 + HDEX_BASE)
+#define	HDEX_PLAY_PAUSE		(2 + HDEX_BASE)
+#define	HDEX_PLAY_RESUME	(3 + HDEX_BASE)
+#define	HDEX_RECORD_START	(4 + HDEX_BASE)
+#define	HDEX_RECORD_STOP	(5 + HDEX_BASE)
+#define	HDEX_MIDI_IN_START 	(6 + HDEX_BASE)
+#define	HDEX_MIDI_IN_STOP	(7 + HDEX_BASE)
+#define	HDEX_MIDI_OUT_START	(8 + HDEX_BASE)
+#define	HDEX_MIDI_OUT_STOP	(9 + HDEX_BASE)
+#define	HDEX_AUX_REQ		(10 + HDEX_BASE)
+
+#define HIWORD(l)		((WORD)((((DWORD)(l)) >> 16) & 0xFFFF))
+#define LOWORD(l)		((WORD)(DWORD)(l))
+#define HIBYTE(w)		((BYTE)(((WORD)(w) >> 8) & 0xFF))
+#define LOBYTE(w)		((BYTE)(w))
+#define MAKELONG(low,hi)	((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16)))
+#define MAKEWORD(low,hi)	((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8)))
+
+#define PCTODSP_OFFSET(w)	(USHORT)((w)/2)
+#define PCTODSP_BASED(w)	(USHORT)(((w)/2) + DSP_BASE_ADDR)
+#define DSPTOPC_BASED(w)	(((w) - DSP_BASE_ADDR) * 2)
+
+#ifdef SLOWIO
+#define msnd_outb			outb_p
+#define msnd_inb			inb_p
+#else
+#define msnd_outb			outb
+#define msnd_inb			inb
+#endif
+
+/* JobQueueStruct */
+#define JQS_wStart		0x00
+#define JQS_wSize		0x02
+#define JQS_wHead		0x04
+#define JQS_wTail		0x06
+#define JQS__size		0x08
+
+/* DAQueueDataStruct */
+#define DAQDS_wStart		0x00
+#define DAQDS_wSize		0x02
+#define DAQDS_wFormat		0x04
+#define DAQDS_wSampleSize	0x06
+#define DAQDS_wChannels		0x08
+#define DAQDS_wSampleRate	0x0A
+#define DAQDS_wIntMsg		0x0C
+#define DAQDS_wFlags		0x0E
+#define DAQDS__size		0x10
+
+typedef u8			BYTE;
+typedef u16			USHORT;
+typedef u16			WORD;
+typedef u32			DWORD;
+typedef void __iomem *		LPDAQD;
+
+/* Generic FIFO */
+typedef struct {
+	size_t n, len;
+	char *data;
+	int head, tail;
+} msnd_fifo;
+
+typedef struct multisound_dev {
+	/* Linux device info */
+	char *name;
+	int dsp_minor, mixer_minor;
+	int ext_midi_dev, hdr_midi_dev;
+
+	/* Hardware resources */
+	int io, numio;
+	int memid, irqid;
+	int irq, irq_ref;
+	unsigned char info;
+	void __iomem *base;
+
+	/* Motorola 56k DSP SMA */
+	void __iomem *SMA;
+	void __iomem *DAPQ, *DARQ, *MODQ, *MIDQ, *DSPQ;
+	void __iomem *pwDSPQData, *pwMIDQData, *pwMODQData;
+	int dspq_data_buff, dspq_buff_size;
+
+	/* State variables */
+	enum { msndClassic, msndPinnacle } type;
+	fmode_t mode;
+	unsigned long flags;
+#define F_RESETTING			0
+#define F_HAVEDIGITAL			1
+#define F_AUDIO_WRITE_INUSE		2
+#define F_WRITING			3
+#define F_WRITEBLOCK			4
+#define F_WRITEFLUSH			5
+#define F_AUDIO_READ_INUSE		6
+#define F_READING			7
+#define F_READBLOCK			8
+#define F_EXT_MIDI_INUSE		9
+#define F_HDR_MIDI_INUSE		10
+#define F_DISABLE_WRITE_NDELAY		11
+	wait_queue_head_t writeblock;
+	wait_queue_head_t readblock;
+	wait_queue_head_t writeflush;
+	spinlock_t lock;
+	int nresets;
+	unsigned long recsrc;
+	int left_levels[32];
+	int right_levels[32];
+	int mixer_mod_count;
+	int calibrate_signal;
+	int play_sample_size, play_sample_rate, play_channels;
+	int play_ndelay;
+	int rec_sample_size, rec_sample_rate, rec_channels;
+	int rec_ndelay;
+	BYTE bCurrentMidiPatch;
+
+	/* Digital audio FIFOs */
+	msnd_fifo DAPF, DARF;
+	int fifosize;
+	int last_playbank, last_recbank;
+
+	/* MIDI in callback */
+	void (*midi_in_interrupt)(struct multisound_dev *);
+} multisound_dev_t;
+
+#ifndef mdelay
+#  define mdelay(a)		udelay((a) * 1000)
+#endif
+
+int				msnd_register(multisound_dev_t *dev);
+void				msnd_unregister(multisound_dev_t *dev);
+
+void				msnd_init_queue(void __iomem *, int start, int size);
+
+void				msnd_fifo_init(msnd_fifo *f);
+void				msnd_fifo_free(msnd_fifo *f);
+int				msnd_fifo_alloc(msnd_fifo *f, size_t n);
+void				msnd_fifo_make_empty(msnd_fifo *f);
+int				msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len);
+int				msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len);
+int				msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len);
+int				msnd_fifo_read(msnd_fifo *f, char *buf, size_t len);
+
+int				msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd);
+int				msnd_send_word(multisound_dev_t *dev, unsigned char high,
+					       unsigned char mid, unsigned char low);
+int				msnd_upload_host(multisound_dev_t *dev, char *bin, int len);
+int				msnd_enable_irq(multisound_dev_t *dev);
+int				msnd_disable_irq(multisound_dev_t *dev);
+
+#endif /* __MSND_H */
diff --git a/sound/oss/msnd_classic.c b/sound/oss/msnd_classic.c
new file mode 100644
index 00000000..3b23a096
--- /dev/null
+++ b/sound/oss/msnd_classic.c
@@ -0,0 +1,3 @@
+/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
+#define MSND_CLASSIC
+#include "msnd_pinnacle.c"
diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h
new file mode 100644
index 00000000..1a17dde2
--- /dev/null
+++ b/sound/oss/msnd_classic.h
@@ -0,0 +1,185 @@
+/*********************************************************************
+ *
+ * msnd_classic.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ * 
+ ********************************************************************/
+#ifndef __MSND_CLASSIC_H
+#define __MSND_CLASSIC_H
+
+
+#define DSP_NUMIO				0x10
+
+#define	HP_MEMM					0x08
+
+#define	HP_BITM					0x0E
+#define	HP_WAIT					0x0D
+#define	HP_DSPR					0x0A
+#define	HP_PROR					0x0B
+#define	HP_BLKS					0x0C
+
+#define	HPPRORESET_OFF				0
+#define HPPRORESET_ON				1
+
+#define HPDSPRESET_OFF				0
+#define HPDSPRESET_ON				1
+
+#define HPBLKSEL_0				0
+#define HPBLKSEL_1				1
+
+#define HPWAITSTATE_0				0
+#define HPWAITSTATE_1				1
+
+#define HPBITMODE_16				0
+#define HPBITMODE_8				1
+
+#define	HIDSP_INT_PLAY_UNDER			0x00
+#define	HIDSP_INT_RECORD_OVER			0x01
+#define	HIDSP_INPUT_CLIPPING			0x02
+#define	HIDSP_MIDI_IN_OVER			0x10
+#define	HIDSP_MIDI_OVERRUN_ERR  0x13
+
+#define	HDEXAR_CLEAR_PEAKS			1
+#define	HDEXAR_IN_SET_POTS			2
+#define	HDEXAR_AUX_SET_POTS			3
+#define	HDEXAR_CAL_A_TO_D			4
+#define	HDEXAR_RD_EXT_DSP_BITS			5
+
+#define TIME_PRO_RESET_DONE			0x028A
+#define TIME_PRO_SYSEX				0x0040
+#define TIME_PRO_RESET				0x0032
+
+#define AGND					0x01
+#define SIGNAL					0x02
+
+#define EXT_DSP_BIT_DCAL			0x0001
+#define EXT_DSP_BIT_MIDI_CON			0x0002
+
+#define BUFFSIZE				0x8000
+#define HOSTQ_SIZE				0x40
+
+#define SRAM_CNTL_START				0x7F00
+#define SMA_STRUCT_START			0x7F40
+
+#define DAP_BUFF_SIZE				0x2400
+#define DAR_BUFF_SIZE				0x2000
+
+#define DAPQ_STRUCT_SIZE			0x10
+#define DARQ_STRUCT_SIZE			0x10
+#define DAPQ_BUFF_SIZE				(3 * 0x10)
+#define DARQ_BUFF_SIZE				(3 * 0x10)
+#define MODQ_BUFF_SIZE				0x400
+#define MIDQ_BUFF_SIZE				0x200
+#define DSPQ_BUFF_SIZE				0x40
+
+#define DAPQ_DATA_BUFF				0x6C00
+#define DARQ_DATA_BUFF				0x6C30
+#define MODQ_DATA_BUFF				0x6C60
+#define MIDQ_DATA_BUFF				0x7060
+#define DSPQ_DATA_BUFF				0x7260
+
+#define DAPQ_OFFSET				SRAM_CNTL_START
+#define DARQ_OFFSET				(SRAM_CNTL_START + 0x08)
+#define MODQ_OFFSET				(SRAM_CNTL_START + 0x10)
+#define MIDQ_OFFSET				(SRAM_CNTL_START + 0x18)
+#define DSPQ_OFFSET				(SRAM_CNTL_START + 0x20)
+
+#define MOP_SYNTH				0x10
+#define MOP_EXTOUT				0x32
+#define MOP_EXTTHRU				0x02
+#define MOP_OUTMASK				0x01
+
+#define MIP_EXTIN				0x01
+#define MIP_SYNTH				0x00
+#define MIP_INMASK				0x32
+
+/* Classic SMA Common Data */
+#define SMA_wCurrPlayBytes			0x0000
+#define SMA_wCurrRecordBytes			0x0002
+#define SMA_wCurrPlayVolLeft			0x0004
+#define SMA_wCurrPlayVolRight			0x0006
+#define SMA_wCurrInVolLeft			0x0008
+#define SMA_wCurrInVolRight			0x000a
+#define SMA_wUser_3				0x000c
+#define SMA_wUser_4				0x000e
+#define SMA_dwUser_5				0x0010
+#define SMA_dwUser_6				0x0014
+#define SMA_wUser_7				0x0018
+#define SMA_wReserved_A				0x001a
+#define SMA_wReserved_B				0x001c
+#define SMA_wReserved_C				0x001e
+#define SMA_wReserved_D				0x0020
+#define SMA_wReserved_E				0x0022
+#define SMA_wReserved_F				0x0024
+#define SMA_wReserved_G				0x0026
+#define SMA_wReserved_H				0x0028
+#define SMA_wCurrDSPStatusFlags			0x002a
+#define SMA_wCurrHostStatusFlags		0x002c
+#define SMA_wCurrInputTagBits			0x002e
+#define SMA_wCurrLeftPeak			0x0030
+#define SMA_wCurrRightPeak			0x0032
+#define SMA_wExtDSPbits				0x0034
+#define SMA_bExtHostbits			0x0036
+#define SMA_bBoardLevel				0x0037
+#define SMA_bInPotPosRight			0x0038
+#define SMA_bInPotPosLeft			0x0039
+#define SMA_bAuxPotPosRight			0x003a
+#define SMA_bAuxPotPosLeft			0x003b
+#define SMA_wCurrMastVolLeft			0x003c
+#define SMA_wCurrMastVolRight			0x003e
+#define SMA_bUser_12				0x0040
+#define SMA_bUser_13				0x0041
+#define SMA_wUser_14				0x0042
+#define SMA_wUser_15				0x0044
+#define SMA_wCalFreqAtoD			0x0046
+#define SMA_wUser_16				0x0048
+#define SMA_wUser_17				0x004a
+#define SMA__size				0x004c
+
+#ifdef HAVE_DSPCODEH
+#  include "msndperm.c"
+#  include "msndinit.c"
+#  define PERMCODE		msndperm
+#  define INITCODE		msndinit
+#  define PERMCODESIZE		sizeof(msndperm)
+#  define INITCODESIZE		sizeof(msndinit)
+#else
+#  ifndef CONFIG_MSNDCLAS_INIT_FILE
+#    define CONFIG_MSNDCLAS_INIT_FILE				\
+				"/etc/sound/msndinit.bin"
+#  endif
+#  ifndef CONFIG_MSNDCLAS_PERM_FILE
+#    define CONFIG_MSNDCLAS_PERM_FILE				\
+				"/etc/sound/msndperm.bin"
+#  endif
+#  define PERMCODEFILE		CONFIG_MSNDCLAS_PERM_FILE
+#  define INITCODEFILE		CONFIG_MSNDCLAS_INIT_FILE
+#  define PERMCODE		dspini
+#  define INITCODE		permini
+#  define PERMCODESIZE		sizeof_dspini
+#  define INITCODESIZE		sizeof_permini
+#endif
+#define LONGNAME		"MultiSound (Classic/Monterey/Tahiti)"
+
+#endif /* __MSND_CLASSIC_H */
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
new file mode 100644
index 00000000..536c4c05
--- /dev/null
+++ b/sound/oss/msnd_pinnacle.c
@@ -0,0 +1,1935 @@
+/*********************************************************************
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ * Linux 2.0/2.2 Version
+ *
+ * msnd_pinnacle.c / msnd_classic.c
+ *
+ * -- If MSND_CLASSIC is defined:
+ *
+ *     -> driver for Turtle Beach Classic/Monterey/Tahiti
+ *
+ * -- Else
+ *
+ *     -> driver for Turtle Beach Pinnacle/Fiji
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ *
+ * 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.
+ *
+ * 12-3-2000  Modified IO port validation  Steve Sycamore
+ *
+ ********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/gfp.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include "sound_config.h"
+#include "sound_firmware.h"
+#ifdef MSND_CLASSIC
+# ifndef __alpha__
+#  define SLOWIO
+# endif
+#endif
+#include "msnd.h"
+#ifdef MSND_CLASSIC
+#  ifdef CONFIG_MSNDCLAS_HAVE_BOOT
+#    define HAVE_DSPCODEH
+#  endif
+#  include "msnd_classic.h"
+#  define LOGNAME			"msnd_classic"
+#else
+#  ifdef CONFIG_MSNDPIN_HAVE_BOOT
+#    define HAVE_DSPCODEH
+#  endif
+#  include "msnd_pinnacle.h"
+#  define LOGNAME			"msnd_pinnacle"
+#endif
+
+#ifndef CONFIG_MSND_WRITE_NDELAY
+#  define CONFIG_MSND_WRITE_NDELAY	1
+#endif
+
+#define get_play_delay_jiffies(size)	((size) * HZ *			\
+					 dev.play_sample_size / 8 /	\
+					 dev.play_sample_rate /		\
+					 dev.play_channels)
+
+#define get_rec_delay_jiffies(size)	((size) * HZ *			\
+					 dev.rec_sample_size / 8 /	\
+					 dev.rec_sample_rate /		\
+					 dev.rec_channels)
+
+static DEFINE_MUTEX(msnd_pinnacle_mutex);
+static multisound_dev_t			dev;
+
+#ifndef HAVE_DSPCODEH
+static char				*dspini, *permini;
+static int				sizeof_dspini, sizeof_permini;
+#endif
+
+static int				dsp_full_reset(void);
+static void				dsp_write_flush(void);
+
+static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd)
+{
+	if (msnd_send_dsp_cmd(dev, cmd) == 0)
+		return 0;
+	dsp_full_reset();
+	return msnd_send_dsp_cmd(dev, cmd);
+}
+
+static void reset_play_queue(void)
+{
+	int n;
+	LPDAQD lpDAQ;
+
+	dev.last_playbank = -1;
+	writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead);
+	writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail);
+
+	for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
+		writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
+		writew(0, lpDAQ + DAQDS_wSize);
+		writew(1, lpDAQ + DAQDS_wFormat);
+		writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.play_channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		writew(n, lpDAQ + DAQDS_wFlags);
+	}
+}
+
+static void reset_record_queue(void)
+{
+	int n;
+	LPDAQD lpDAQ;
+	unsigned long flags;
+
+	dev.last_recbank = 2;
+	writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead);
+	writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail);
+
+	/* Critical section: bank 1 access */
+	spin_lock_irqsave(&dev.lock, flags);
+	msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
+	memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
+	msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	spin_unlock_irqrestore(&dev.lock, flags);
+
+	for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
+		writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
+		writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
+		writew(1, lpDAQ + DAQDS_wFormat);
+		writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.rec_channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		writew(n, lpDAQ + DAQDS_wFlags);
+	}
+}
+
+static void reset_queues(void)
+{
+	if (dev.mode & FMODE_WRITE) {
+		msnd_fifo_make_empty(&dev.DAPF);
+		reset_play_queue();
+	}
+	if (dev.mode & FMODE_READ) {
+		msnd_fifo_make_empty(&dev.DARF);
+		reset_record_queue();
+	}
+}
+
+static int dsp_set_format(struct file *file, int val)
+{
+	int data, i;
+	LPDAQD lpDAQ, lpDARQ;
+
+	lpDAQ = dev.base + DAPQ_DATA_BUFF;
+	lpDARQ = dev.base + DARQ_DATA_BUFF;
+
+	switch (val) {
+	case AFMT_U8:
+	case AFMT_S16_LE:
+		data = val;
+		break;
+	default:
+		data = DEFSAMPLESIZE;
+		break;
+	}
+
+	for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+		if (file->f_mode & FMODE_WRITE)
+			writew(data, lpDAQ + DAQDS_wSampleSize);
+		if (file->f_mode & FMODE_READ)
+			writew(data, lpDARQ + DAQDS_wSampleSize);
+	}
+	if (file->f_mode & FMODE_WRITE)
+		dev.play_sample_size = data;
+	if (file->f_mode & FMODE_READ)
+		dev.rec_sample_size = data;
+
+	return data;
+}
+
+static int dsp_get_frag_size(void)
+{
+	int size;
+	size = dev.fifosize / 4;
+	if (size > 32 * 1024)
+		size = 32 * 1024;
+	return size;
+}
+
+static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int val, i, data, tmp;
+	LPDAQD lpDAQ, lpDARQ;
+        audio_buf_info abinfo;
+	unsigned long flags;
+	int __user *p = (int __user *)arg;
+
+	lpDAQ = dev.base + DAPQ_DATA_BUFF;
+	lpDARQ = dev.base + DARQ_DATA_BUFF;
+
+	switch (cmd) {
+	case SNDCTL_DSP_SUBDIVIDE:
+	case SNDCTL_DSP_SETFRAGMENT:
+	case SNDCTL_DSP_SETDUPLEX:
+	case SNDCTL_DSP_POST:
+		return 0;
+
+	case SNDCTL_DSP_GETIPTR:
+	case SNDCTL_DSP_GETOPTR:
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&dev.lock, flags);
+		abinfo.fragsize = dsp_get_frag_size();
+                abinfo.bytes = dev.DAPF.n - dev.DAPF.len;
+                abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize;
+                abinfo.fragments = abinfo.bytes / abinfo.fragsize;
+		spin_unlock_irqrestore(&dev.lock, flags);
+		return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&dev.lock, flags);
+		abinfo.fragsize = dsp_get_frag_size();
+                abinfo.bytes = dev.DARF.n - dev.DARF.len;
+                abinfo.fragstotal = dev.DARF.n / abinfo.fragsize;
+                abinfo.fragments = abinfo.bytes / abinfo.fragsize;
+		spin_unlock_irqrestore(&dev.lock, flags);
+		return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_RESET:
+		dev.nresets = 0;
+		reset_queues();
+		return 0;
+
+	case SNDCTL_DSP_SYNC:
+		dsp_write_flush();
+		return 0;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		tmp = dsp_get_frag_size();
+		if (put_user(tmp, p))
+                        return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_GETFMTS:
+		val = AFMT_S16_LE | AFMT_U8;
+		if (put_user(val, p))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, p))
+			return -EFAULT;
+
+		if (file->f_mode & FMODE_WRITE)
+			data = val == AFMT_QUERY
+				? dev.play_sample_size
+				: dsp_set_format(file, val);
+		else
+			data = val == AFMT_QUERY
+				? dev.rec_sample_size
+				: dsp_set_format(file, val);
+
+		if (put_user(data, p))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_NONBLOCK:
+		if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) &&
+		    file->f_mode & FMODE_WRITE)
+			dev.play_ndelay = 1;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_ndelay = 1;
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		val = DSP_CAP_DUPLEX | DSP_CAP_BATCH;
+		if (put_user(val, p))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_SPEED:
+		if (get_user(val, p))
+			return -EFAULT;
+
+		if (val < 8000)
+			val = 8000;
+
+		if (val > 48000)
+			val = 48000;
+
+		data = val;
+
+		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+			if (file->f_mode & FMODE_WRITE)
+				writew(data, lpDAQ + DAQDS_wSampleRate);
+			if (file->f_mode & FMODE_READ)
+				writew(data, lpDARQ + DAQDS_wSampleRate);
+		}
+		if (file->f_mode & FMODE_WRITE)
+			dev.play_sample_rate = data;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_sample_rate = data;
+
+		if (put_user(data, p))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_CHANNELS:
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, p))
+			return -EFAULT;
+
+		if (cmd == SNDCTL_DSP_CHANNELS) {
+			switch (val) {
+			case 1:
+			case 2:
+				data = val;
+				break;
+			default:
+				val = data = 2;
+				break;
+			}
+		} else {
+			switch (val) {
+			case 0:
+				data = 1;
+				break;
+			default:
+				val = 1;
+			case 1:
+				data = 2;
+				break;
+			}
+		}
+
+		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+			if (file->f_mode & FMODE_WRITE)
+				writew(data, lpDAQ + DAQDS_wChannels);
+			if (file->f_mode & FMODE_READ)
+				writew(data, lpDARQ + DAQDS_wChannels);
+		}
+		if (file->f_mode & FMODE_WRITE)
+			dev.play_channels = data;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_channels = data;
+
+		if (put_user(val, p))
+			return -EFAULT;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int mixer_get(int d)
+{
+	if (d > 31)
+		return -EINVAL;
+
+	switch (d) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_IMIX:
+	case SOUND_MIXER_LINE1:
+#ifndef MSND_CLASSIC
+	case SOUND_MIXER_MIC:
+	case SOUND_MIXER_SYNTH:
+#endif
+		return (dev.left_levels[d] >> 8) * 100 / 0xff | 
+			(((dev.right_levels[d] >> 8) * 100 / 0xff) << 8);
+	default:
+		return 0;
+	}
+}
+
+#define update_volm(a,b)						\
+	writew((dev.left_levels[a] >> 1) *				\
+	       readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff,	\
+	       dev.SMA + SMA_##b##Left);				\
+	writew((dev.right_levels[a] >> 1)  *			\
+	       readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff,	\
+	       dev.SMA + SMA_##b##Right);
+
+#define update_potm(d,s,ar)						\
+	writeb((dev.left_levels[d] >> 8) *				\
+	       readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff,	\
+	       dev.SMA + SMA_##s##Left);				\
+	writeb((dev.right_levels[d] >> 8) *				\
+	       readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff,	\
+	       dev.SMA + SMA_##s##Right);				\
+	if (msnd_send_word(&dev, 0, 0, ar) == 0)			\
+		chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+
+#define update_pot(d,s,ar)				\
+	writeb(dev.left_levels[d] >> 8,		\
+	       dev.SMA + SMA_##s##Left);		\
+	writeb(dev.right_levels[d] >> 8,		\
+	       dev.SMA + SMA_##s##Right);		\
+	if (msnd_send_word(&dev, 0, 0, ar) == 0)	\
+		chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+
+static int mixer_set(int d, int value)
+{
+	int left = value & 0x000000ff;
+	int right = (value & 0x0000ff00) >> 8;
+	int bLeft, bRight;
+	int wLeft, wRight;
+	int updatemaster = 0;
+
+	if (d > 31)
+		return -EINVAL;
+
+	bLeft = left * 0xff / 100;
+	wLeft = left * 0xffff / 100;
+
+	bRight = right * 0xff / 100;
+	wRight = right * 0xffff / 100;
+
+	dev.left_levels[d] = wLeft;
+	dev.right_levels[d] = wRight;
+
+	switch (d) {
+		/* master volume unscaled controls */
+	case SOUND_MIXER_LINE:			/* line pot control */
+		/* scaled by IMIX in digital mix */
+		writeb(bLeft, dev.SMA + SMA_bInPotPosLeft);
+		writeb(bRight, dev.SMA + SMA_bInPotPosRight);
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+		break;
+#ifndef MSND_CLASSIC
+	case SOUND_MIXER_MIC:			/* mic pot control */
+		/* scaled by IMIX in digital mix */
+		writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft);
+		writeb(bRight, dev.SMA + SMA_bMicPotPosRight);
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+		break;
+#endif
+	case SOUND_MIXER_VOLUME:		/* master volume */
+		writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft);
+		writew(wRight, dev.SMA + SMA_wCurrMastVolRight);
+		/* fall through */
+
+	case SOUND_MIXER_LINE1:			/* aux pot control */
+		/* scaled by master volume */
+		/* fall through */
+
+		/* digital controls */
+	case SOUND_MIXER_SYNTH:			/* synth vol (dsp mix) */
+	case SOUND_MIXER_PCM:			/* pcm vol (dsp mix) */
+	case SOUND_MIXER_IMIX:			/* input monitor (dsp mix) */
+		/* scaled by master volume */
+		updatemaster = 1;
+		break;
+
+	default:
+		return 0;
+	}
+
+	if (updatemaster) {
+		/* update master volume scaled controls */
+		update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
+		update_volm(SOUND_MIXER_IMIX, wCurrInVol);
+#ifndef MSND_CLASSIC
+		update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
+#endif
+		update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
+	}
+
+	return mixer_get(d);
+}
+
+static void mixer_setup(void)
+{
+	update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
+	update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
+	update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
+	update_volm(SOUND_MIXER_IMIX, wCurrInVol);
+#ifndef MSND_CLASSIC
+	update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
+	update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
+#endif
+}
+
+static unsigned long set_recsrc(unsigned long recsrc)
+{
+	if (dev.recsrc == recsrc)
+		return dev.recsrc;
+#ifdef HAVE_NORECSRC
+	else if (recsrc == 0)
+		dev.recsrc = 0;
+#endif
+	else
+		dev.recsrc ^= recsrc;
+
+#ifndef MSND_CLASSIC
+	if (dev.recsrc & SOUND_MASK_IMIX) {
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+	}
+	else if (dev.recsrc & SOUND_MASK_SYNTH) {
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+	}
+	else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) {
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0)
+      			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+	}
+	else {
+#ifdef HAVE_NORECSRC
+		/* Select no input (?) */
+		dev.recsrc = 0;
+#else
+		dev.recsrc = SOUND_MASK_IMIX;
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+#endif
+	}
+#endif /* MSND_CLASSIC */
+
+	return dev.recsrc;
+}
+
+static unsigned long force_recsrc(unsigned long recsrc)
+{
+	dev.recsrc = 0;
+	return set_recsrc(recsrc);
+}
+
+#define set_mixer_info()							\
+		memset(&info, 0, sizeof(info));					\
+		strlcpy(info.id, "MSNDMIXER", sizeof(info.id));			\
+		strlcpy(info.name, "MultiSound Mixer", sizeof(info.name));
+
+static int mixer_ioctl(unsigned int cmd, unsigned long arg)
+{
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		set_mixer_info();
+		info.modify_counter = dev.mixer_mod_count;
+		if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	} else if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		set_mixer_info();
+		if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	} else if (cmd == SOUND_MIXER_PRIVATE1) {
+		dev.nresets = 0;
+		dsp_full_reset();
+		return 0;
+	} else if (((cmd >> 8) & 0xff) == 'M') {
+		int val = 0;
+
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				if (get_user(val, (int __user *)arg))
+					return -EFAULT;
+				val = set_recsrc(val);
+				break;
+
+			default:
+				if (get_user(val, (int __user *)arg))
+					return -EFAULT;
+				val = mixer_set(cmd & 0xff, val);
+				break;
+			}
+			++dev.mixer_mod_count;
+			return put_user(val, (int __user *)arg);
+		} else {
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				val = dev.recsrc;
+				break;
+
+			case SOUND_MIXER_DEVMASK:
+			case SOUND_MIXER_STEREODEVS:
+				val =   SOUND_MASK_PCM |
+					SOUND_MASK_LINE |
+					SOUND_MASK_IMIX |
+					SOUND_MASK_LINE1 |
+#ifndef MSND_CLASSIC
+					SOUND_MASK_MIC |
+					SOUND_MASK_SYNTH |
+#endif
+					SOUND_MASK_VOLUME;
+				break;
+				  
+			case SOUND_MIXER_RECMASK:
+#ifdef MSND_CLASSIC
+				val =   0;
+#else
+				val =   SOUND_MASK_IMIX |
+					SOUND_MASK_SYNTH;
+				if (test_bit(F_HAVEDIGITAL, &dev.flags))
+					val |= SOUND_MASK_DIGITAL1;
+#endif
+				break;
+				  
+			case SOUND_MIXER_CAPS:
+				val =   SOUND_CAP_EXCL_INPUT;
+				break;
+
+			default:
+				if ((val = mixer_get(cmd & 0xff)) < 0)
+					return -EINVAL;
+				break;
+			}
+		}
+
+		return put_user(val, (int __user *)arg); 
+	}
+
+	return -EINVAL;
+}
+
+static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int minor = iminor(file->f_path.dentry->d_inode);
+	int ret;
+
+	if (cmd == OSS_GETVERSION) {
+		int sound_version = SOUND_VERSION;
+		return put_user(sound_version, (int __user *)arg);
+	}
+
+	ret = -EINVAL;
+
+	mutex_lock(&msnd_pinnacle_mutex);
+	if (minor == dev.dsp_minor)
+		ret = dsp_ioctl(file, cmd, arg);
+	else if (minor == dev.mixer_minor)
+		ret = mixer_ioctl(cmd, arg);
+	mutex_unlock(&msnd_pinnacle_mutex);
+
+	return ret;
+}
+
+static void dsp_write_flush(void)
+{
+	if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags))
+		return;
+	set_bit(F_WRITEFLUSH, &dev.flags);
+	interruptible_sleep_on_timeout(
+		&dev.writeflush,
+		get_play_delay_jiffies(dev.DAPF.len));
+	clear_bit(F_WRITEFLUSH, &dev.flags);
+	if (!signal_pending(current)) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE));
+	}
+	clear_bit(F_WRITING, &dev.flags);
+}
+
+static void dsp_halt(struct file *file)
+{
+	if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
+		clear_bit(F_READING, &dev.flags);
+		chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP);
+		msnd_disable_irq(&dev);
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file);
+			dev.mode &= ~FMODE_READ;
+		}
+		clear_bit(F_AUDIO_READ_INUSE, &dev.flags);
+	}
+	if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
+		if (test_bit(F_WRITING, &dev.flags)) {
+			dsp_write_flush();
+			chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP);
+		}
+		msnd_disable_irq(&dev);
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file);
+			dev.mode &= ~FMODE_WRITE;
+		}
+		clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
+	}
+}
+
+static int dsp_release(struct file *file)
+{
+	dsp_halt(file);
+	return 0;
+}
+
+static int dsp_open(struct file *file)
+{
+	if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
+		set_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
+		clear_bit(F_WRITING, &dev.flags);
+		msnd_fifo_make_empty(&dev.DAPF);
+		reset_play_queue();
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file);
+			dev.mode |= FMODE_WRITE;
+		}
+		msnd_enable_irq(&dev);
+	}
+	if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
+		set_bit(F_AUDIO_READ_INUSE, &dev.flags);
+		clear_bit(F_READING, &dev.flags);
+		msnd_fifo_make_empty(&dev.DARF);
+		reset_record_queue();
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file);
+			dev.mode |= FMODE_READ;
+		}
+		msnd_enable_irq(&dev);
+	}
+	return 0;
+}
+
+static void set_default_play_audio_parameters(void)
+{
+	dev.play_sample_size = DEFSAMPLESIZE;
+	dev.play_sample_rate = DEFSAMPLERATE;
+	dev.play_channels = DEFCHANNELS;
+}
+
+static void set_default_rec_audio_parameters(void)
+{
+	dev.rec_sample_size = DEFSAMPLESIZE;
+	dev.rec_sample_rate = DEFSAMPLERATE;
+	dev.rec_channels = DEFCHANNELS;
+}
+
+static void set_default_audio_parameters(void)
+{
+	set_default_play_audio_parameters();
+	set_default_rec_audio_parameters();
+}
+
+static int dev_open(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	int err = 0;
+
+	mutex_lock(&msnd_pinnacle_mutex);
+	if (minor == dev.dsp_minor) {
+		if ((file->f_mode & FMODE_WRITE &&
+		     test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
+		    (file->f_mode & FMODE_READ &&
+		     test_bit(F_AUDIO_READ_INUSE, &dev.flags))) {
+			err = -EBUSY;
+			goto out;
+		}
+
+		if ((err = dsp_open(file)) >= 0) {
+			dev.nresets = 0;
+			if (file->f_mode & FMODE_WRITE) {
+				set_default_play_audio_parameters();
+				if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags))
+					dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
+				else
+					dev.play_ndelay = 0;
+			}
+			if (file->f_mode & FMODE_READ) {
+				set_default_rec_audio_parameters();
+				dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
+			}
+		}
+	}
+	else if (minor == dev.mixer_minor) {
+		/* nothing */
+	} else
+		err = -EINVAL;
+out:
+	mutex_unlock(&msnd_pinnacle_mutex);
+	return err;
+}
+
+static int dev_release(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	int err = 0;
+
+	mutex_lock(&msnd_pinnacle_mutex);
+	if (minor == dev.dsp_minor)
+		err = dsp_release(file);
+	else if (minor == dev.mixer_minor) {
+		/* nothing */
+	} else
+		err = -EINVAL;
+	mutex_unlock(&msnd_pinnacle_mutex);
+	return err;
+}
+
+static __inline__ int pack_DARQ_to_DARF(register int bank)
+{
+	register int size, timeout = 3;
+	register WORD wTmp;
+	LPDAQD DAQD;
+
+	/* Increment the tail and check for queue wrap */
+	wTmp = readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
+	if (wTmp > readw(dev.DARQ + JQS_wSize))
+		wTmp = 0;
+	while (wTmp == readw(dev.DARQ + JQS_wHead) && timeout--)
+		udelay(1);
+	writew(wTmp, dev.DARQ + JQS_wTail);
+
+	/* Get our digital audio queue struct */
+	DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF;
+
+	/* Get length of data */
+	size = readw(DAQD + DAQDS_wSize);
+
+	/* Read data from the head (unprotected bank 1 access okay
+           since this is only called inside an interrupt) */
+	msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
+	msnd_fifo_write_io(
+		&dev.DARF,
+		dev.base + bank * DAR_BUFF_SIZE,
+		size);
+	msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
+
+	return 1;
+}
+
+static __inline__ int pack_DAPF_to_DAPQ(register int start)
+{
+	register WORD DAPQ_tail;
+	register int protect = start, nbanks = 0;
+	LPDAQD DAQD;
+
+	DAPQ_tail = readw(dev.DAPQ + JQS_wTail);
+	while (DAPQ_tail != readw(dev.DAPQ + JQS_wHead) || start) {
+		register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
+		register int n;
+		unsigned long flags;
+
+		/* Write the data to the new tail */
+		if (protect) {
+			/* Critical section: protect fifo in non-interrupt */
+			spin_lock_irqsave(&dev.lock, flags);
+			n = msnd_fifo_read_io(
+				&dev.DAPF,
+				dev.base + bank_num * DAP_BUFF_SIZE,
+				DAP_BUFF_SIZE);
+			spin_unlock_irqrestore(&dev.lock, flags);
+		} else {
+			n = msnd_fifo_read_io(
+				&dev.DAPF,
+				dev.base + bank_num * DAP_BUFF_SIZE,
+				DAP_BUFF_SIZE);
+		}
+		if (!n)
+			break;
+
+		if (start)
+			start = 0;
+
+		/* Get our digital audio queue struct */
+		DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF;
+
+		/* Write size of this bank */
+		writew(n, DAQD + DAQDS_wSize);
+		++nbanks;
+
+		/* Then advance the tail */
+		DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
+		writew(DAPQ_tail, dev.DAPQ + JQS_wTail);
+		/* Tell the DSP to play the bank */
+		msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
+	}
+	return nbanks;
+}
+
+static int dsp_read(char __user *buf, size_t len)
+{
+	int count = len;
+	char *page = (char *)__get_free_page(GFP_KERNEL);
+
+	if (!page)
+		return -ENOMEM;
+
+	while (count > 0) {
+		int n, k;
+		unsigned long flags;
+
+		k = PAGE_SIZE;
+		if (k > count)
+			k = count;
+
+		/* Critical section: protect fifo in non-interrupt */
+		spin_lock_irqsave(&dev.lock, flags);
+		n = msnd_fifo_read(&dev.DARF, page, k);
+		spin_unlock_irqrestore(&dev.lock, flags);
+		if (copy_to_user(buf, page, n)) {
+			free_page((unsigned long)page);
+			return -EFAULT;
+		}
+		buf += n;
+		count -= n;
+
+		if (n == k && count)
+			continue;
+
+		if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) {
+			dev.last_recbank = -1;
+			if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0)
+				set_bit(F_READING, &dev.flags);
+		}
+
+		if (dev.rec_ndelay) {
+			free_page((unsigned long)page);
+			return count == len ? -EAGAIN : len - count;
+		}
+
+		if (count > 0) {
+			set_bit(F_READBLOCK, &dev.flags);
+			if (!interruptible_sleep_on_timeout(
+				&dev.readblock,
+				get_rec_delay_jiffies(DAR_BUFF_SIZE)))
+				clear_bit(F_READING, &dev.flags);
+			clear_bit(F_READBLOCK, &dev.flags);
+			if (signal_pending(current)) {
+				free_page((unsigned long)page);
+				return -EINTR;
+			}
+		}
+	}
+	free_page((unsigned long)page);
+	return len - count;
+}
+
+static int dsp_write(const char __user *buf, size_t len)
+{
+	int count = len;
+	char *page = (char *)__get_free_page(GFP_KERNEL);
+
+	if (!page)
+		return -ENOMEM;
+
+	while (count > 0) {
+		int n, k;
+		unsigned long flags;
+
+		k = PAGE_SIZE;
+		if (k > count)
+			k = count;
+
+		if (copy_from_user(page, buf, k)) {
+			free_page((unsigned long)page);
+			return -EFAULT;
+		}
+
+		/* Critical section: protect fifo in non-interrupt */
+		spin_lock_irqsave(&dev.lock, flags);
+		n = msnd_fifo_write(&dev.DAPF, page, k);
+		spin_unlock_irqrestore(&dev.lock, flags);
+		buf += n;
+		count -= n;
+
+		if (count && n == k)
+			continue;
+
+		if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) {
+			dev.last_playbank = -1;
+			if (pack_DAPF_to_DAPQ(1) > 0)
+				set_bit(F_WRITING, &dev.flags);
+		}
+
+		if (dev.play_ndelay) {
+			free_page((unsigned long)page);
+			return count == len ? -EAGAIN : len - count;
+		}
+
+		if (count > 0) {
+			set_bit(F_WRITEBLOCK, &dev.flags);
+			interruptible_sleep_on_timeout(
+				&dev.writeblock,
+				get_play_delay_jiffies(DAP_BUFF_SIZE));
+			clear_bit(F_WRITEBLOCK, &dev.flags);
+			if (signal_pending(current)) {
+				free_page((unsigned long)page);
+				return -EINTR;
+			}
+		}
+	}
+
+	free_page((unsigned long)page);
+	return len - count;
+}
+
+static ssize_t dev_read(struct file *file, char __user *buf, size_t count, loff_t *off)
+{
+	int minor = iminor(file->f_path.dentry->d_inode);
+	if (minor == dev.dsp_minor)
+		return dsp_read(buf, count);
+	else
+		return -EINVAL;
+}
+
+static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
+{
+	int minor = iminor(file->f_path.dentry->d_inode);
+	if (minor == dev.dsp_minor)
+		return dsp_write(buf, count);
+	else
+		return -EINVAL;
+}
+
+static __inline__ void eval_dsp_msg(register WORD wMessage)
+{
+	switch (HIBYTE(wMessage)) {
+	case HIMT_PLAY_DONE:
+		if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags))
+			break;
+		dev.last_playbank = LOBYTE(wMessage);
+
+		if (pack_DAPF_to_DAPQ(0) <= 0) {
+			if (!test_bit(F_WRITEBLOCK, &dev.flags)) {
+				if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags))
+					wake_up_interruptible(&dev.writeflush);
+			}
+			clear_bit(F_WRITING, &dev.flags);
+		}
+
+		if (test_bit(F_WRITEBLOCK, &dev.flags))
+			wake_up_interruptible(&dev.writeblock);
+		break;
+
+	case HIMT_RECORD_DONE:
+		if (dev.last_recbank == LOBYTE(wMessage))
+			break;
+		dev.last_recbank = LOBYTE(wMessage);
+
+		pack_DARQ_to_DARF(dev.last_recbank);
+
+		if (test_bit(F_READBLOCK, &dev.flags))
+			wake_up_interruptible(&dev.readblock);
+		break;
+
+	case HIMT_DSP:
+		switch (LOBYTE(wMessage)) {
+#ifndef MSND_CLASSIC
+		case HIDSP_PLAY_UNDER:
+#endif
+		case HIDSP_INT_PLAY_UNDER:
+/*			printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */
+			clear_bit(F_WRITING, &dev.flags);
+			break;
+
+		case HIDSP_INT_RECORD_OVER:
+/*			printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */
+			clear_bit(F_READING, &dev.flags);
+			break;
+
+		default:
+/*			printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n",
+			LOBYTE(wMessage), LOBYTE(wMessage)); */
+			break;
+		}
+		break;
+
+        case HIMT_MIDI_IN_UCHAR:
+		if (dev.midi_in_interrupt)
+			(*dev.midi_in_interrupt)(&dev);
+		break;
+
+	default:
+/*		printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */
+		break;
+	}
+}
+
+static irqreturn_t intr(int irq, void *dev_id)
+{
+	/* Send ack to DSP */
+	msnd_inb(dev.io + HP_RXL);
+
+	/* Evaluate queued DSP messages */
+	while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) {
+		register WORD wTmp;
+
+		eval_dsp_msg(readw(dev.pwDSPQData + 2*readw(dev.DSPQ + JQS_wHead)));
+
+		if ((wTmp = readw(dev.DSPQ + JQS_wHead) + 1) > readw(dev.DSPQ + JQS_wSize))
+			writew(0, dev.DSPQ + JQS_wHead);
+		else
+			writew(wTmp, dev.DSPQ + JQS_wHead);
+	}
+	return IRQ_HANDLED;
+}
+
+static const struct file_operations dev_fileops = {
+	.owner		= THIS_MODULE,
+	.read		= dev_read,
+	.write		= dev_write,
+	.unlocked_ioctl	= dev_ioctl,
+	.open		= dev_open,
+	.release	= dev_release,
+	.llseek		= noop_llseek,
+};
+
+static int reset_dsp(void)
+{
+	int timeout = 100;
+
+	msnd_outb(HPDSPRESET_ON, dev.io + HP_DSPR);
+	mdelay(1);
+#ifndef MSND_CLASSIC
+	dev.info = msnd_inb(dev.io + HP_INFO);
+#endif
+	msnd_outb(HPDSPRESET_OFF, dev.io + HP_DSPR);
+	mdelay(1);
+	while (timeout-- > 0) {
+		if (msnd_inb(dev.io + HP_CVR) == HP_CVR_DEF)
+			return 0;
+		mdelay(1);
+	}
+	printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
+
+	return -EIO;
+}
+
+static int __init probe_multisound(void)
+{
+#ifndef MSND_CLASSIC
+	char *xv, *rev = NULL;
+	char *pin = "Pinnacle", *fiji = "Fiji";
+	char *pinfiji = "Pinnacle/Fiji";
+#endif
+
+	if (!request_region(dev.io, dev.numio, "probing")) {
+		printk(KERN_ERR LOGNAME ": I/O port conflict\n");
+		return -ENODEV;
+	}
+
+	if (reset_dsp() < 0) {
+		release_region(dev.io, dev.numio);
+		return -ENODEV;
+	}
+
+#ifdef MSND_CLASSIC
+	dev.name = "Classic/Tahiti/Monterey";
+	printk(KERN_INFO LOGNAME ": %s, "
+#else
+	switch (dev.info >> 4) {
+	case 0xf: xv = "<= 1.15"; break;
+	case 0x1: xv = "1.18/1.2"; break;
+	case 0x2: xv = "1.3"; break;
+	case 0x3: xv = "1.4"; break;
+	default: xv = "unknown"; break;
+	}
+
+	switch (dev.info & 0x7) {
+	case 0x0: rev = "I"; dev.name = pin; break;
+	case 0x1: rev = "F"; dev.name = pin; break;
+	case 0x2: rev = "G"; dev.name = pin; break;
+	case 0x3: rev = "H"; dev.name = pin; break;
+	case 0x4: rev = "E"; dev.name = fiji; break;
+	case 0x5: rev = "C"; dev.name = fiji; break;
+	case 0x6: rev = "D"; dev.name = fiji; break;
+	case 0x7:
+		rev = "A-B (Fiji) or A-E (Pinnacle)";
+		dev.name = pinfiji;
+		break;
+	}
+	printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
+#endif /* MSND_CLASSIC */
+	       "I/O 0x%x-0x%x, IRQ %d, memory mapped to %p-%p\n",
+	       dev.name,
+#ifndef MSND_CLASSIC
+	       rev, xv,
+#endif
+	       dev.io, dev.io + dev.numio - 1,
+	       dev.irq,
+	       dev.base, dev.base + 0x7fff);
+
+	release_region(dev.io, dev.numio);
+	return 0;
+}
+
+static int init_sma(void)
+{
+	static int initted;
+	WORD mastVolLeft, mastVolRight;
+	unsigned long flags;
+
+#ifdef MSND_CLASSIC
+	msnd_outb(dev.memid, dev.io + HP_MEMM);
+#endif
+	msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	if (initted) {
+		mastVolLeft = readw(dev.SMA + SMA_wCurrMastVolLeft);
+		mastVolRight = readw(dev.SMA + SMA_wCurrMastVolRight);
+	} else
+		mastVolLeft = mastVolRight = 0;
+	memset_io(dev.base, 0, 0x8000);
+
+	/* Critical section: bank 1 access */
+	spin_lock_irqsave(&dev.lock, flags);
+	msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
+	memset_io(dev.base, 0, 0x8000);
+	msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	spin_unlock_irqrestore(&dev.lock, flags);
+
+	dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF);
+	dev.pwMODQData = (dev.base + MODQ_DATA_BUFF);
+	dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF);
+
+	/* Motorola 56k shared memory base */
+	dev.SMA = dev.base + SMA_STRUCT_START;
+
+	/* Digital audio play queue */
+	dev.DAPQ = dev.base + DAPQ_OFFSET;
+	msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
+
+	/* Digital audio record queue */
+	dev.DARQ = dev.base + DARQ_OFFSET;
+	msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
+
+	/* MIDI out queue */
+	dev.MODQ = dev.base + MODQ_OFFSET;
+	msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
+
+	/* MIDI in queue */
+	dev.MIDQ = dev.base + MIDQ_OFFSET;
+	msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
+
+	/* DSP -> host message queue */
+	dev.DSPQ = dev.base + DSPQ_OFFSET;
+	msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
+
+	/* Setup some DSP values */
+#ifndef MSND_CLASSIC
+	writew(1, dev.SMA + SMA_wCurrPlayFormat);
+	writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize);
+	writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels);
+	writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate);
+#endif
+	writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD);
+	writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft);
+	writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight);
+#ifndef MSND_CLASSIC
+	writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch);
+	writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate);
+#endif
+	writew(0x303, dev.SMA + SMA_wCurrInputTagBits);
+
+	initted = 1;
+
+	return 0;
+}
+
+static int __init calibrate_adc(WORD srate)
+{
+	writew(srate, dev.SMA + SMA_wCalFreqAtoD);
+	if (dev.calibrate_signal == 0)
+		writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
+		       | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
+	else
+		writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
+		       & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
+	if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
+	    chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ / 3);
+		return 0;
+	}
+	printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
+
+	return -EIO;
+}
+
+static int upload_dsp_code(void)
+{
+	int ret = 0;
+
+	msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
+#ifndef HAVE_DSPCODEH
+	INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE);
+	if (!INITCODE) {
+		printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
+		return -EBUSY;
+	}
+
+	PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE);
+	if (!PERMCODE) {
+		printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
+		vfree(INITCODE);
+		return -EBUSY;
+	}
+#endif
+	memcpy_toio(dev.base, PERMCODE, PERMCODESIZE);
+	if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) {
+		printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
+		ret = -ENODEV;
+		goto out;
+	}
+#ifdef HAVE_DSPCODEH
+	printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n");
+#else
+	printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
+#endif
+
+out:
+#ifndef HAVE_DSPCODEH
+	vfree(INITCODE);
+	vfree(PERMCODE);
+#endif
+
+	return ret;
+}
+
+#ifdef MSND_CLASSIC
+static void reset_proteus(void)
+{
+	msnd_outb(HPPRORESET_ON, dev.io + HP_PROR);
+	mdelay(TIME_PRO_RESET);
+	msnd_outb(HPPRORESET_OFF, dev.io + HP_PROR);
+	mdelay(TIME_PRO_RESET_DONE);
+}
+#endif
+
+static int initialize(void)
+{
+	int err, timeout;
+
+#ifdef MSND_CLASSIC
+	msnd_outb(HPWAITSTATE_0, dev.io + HP_WAIT);
+	msnd_outb(HPBITMODE_16, dev.io + HP_BITM);
+
+	reset_proteus();
+#endif
+	if ((err = init_sma()) < 0) {
+		printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
+		return err;
+	}
+
+	if ((err = reset_dsp()) < 0)
+		return err;
+
+	if ((err = upload_dsp_code()) < 0) {
+		printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
+		return err;
+	}
+
+	timeout = 200;
+	while (readw(dev.base)) {
+		mdelay(1);
+		if (!timeout--) {
+			printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n");
+			return -EIO;
+		}
+	}
+
+	mixer_setup();
+
+	return 0;
+}
+
+static int dsp_full_reset(void)
+{
+	int rv;
+
+	if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10)
+		return 0;
+
+	set_bit(F_RESETTING, &dev.flags);
+	printk(KERN_INFO LOGNAME ": DSP reset\n");
+	dsp_halt(NULL);			/* Unconditionally halt */
+	if ((rv = initialize()))
+		printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
+	force_recsrc(dev.recsrc);
+	dsp_open(NULL);
+	clear_bit(F_RESETTING, &dev.flags);
+
+	return rv;
+}
+
+static int __init attach_multisound(void)
+{
+	int err;
+
+	if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) {
+		printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq);
+		return err;
+	}
+	if (request_region(dev.io, dev.numio, dev.name) == NULL) {
+		free_irq(dev.irq, &dev);
+		return -EBUSY;
+	}
+
+	err = dsp_full_reset();
+	if (err < 0) {
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return err;
+	}
+
+	if ((err = msnd_register(&dev)) < 0) {
+		printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n");
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return err;
+	}
+
+	if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) {
+		printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
+		msnd_unregister(&dev);
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return dev.dsp_minor;
+	}
+
+	if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) {
+		printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
+		unregister_sound_mixer(dev.mixer_minor);
+		msnd_unregister(&dev);
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return dev.mixer_minor;
+	}
+
+	dev.ext_midi_dev = dev.hdr_midi_dev = -1;
+
+	disable_irq(dev.irq);
+	calibrate_adc(dev.play_sample_rate);
+#ifndef MSND_CLASSIC
+	force_recsrc(SOUND_MASK_IMIX);
+#endif
+
+	return 0;
+}
+
+static void __exit unload_multisound(void)
+{
+	release_region(dev.io, dev.numio);
+	free_irq(dev.irq, &dev);
+	unregister_sound_mixer(dev.mixer_minor);
+	unregister_sound_dsp(dev.dsp_minor);
+	msnd_unregister(&dev);
+}
+
+#ifndef MSND_CLASSIC
+
+/* Pinnacle/Fiji Logical Device Configuration */
+
+static int __init msnd_write_cfg(int cfg, int reg, int value)
+{
+	msnd_outb(reg, cfg);
+	msnd_outb(value, cfg + 1);
+	if (value != msnd_inb(cfg + 1)) {
+		printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int __init msnd_write_cfg_io0(int cfg, int num, WORD io)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_io1(int cfg, int num, WORD io)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_mem(int cfg, int num, int mem)
+{
+	WORD wmem;
+
+	mem >>= 8;
+	mem &= 0xfff;
+	wmem = (WORD)mem;
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
+		return -EIO;
+	if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT)))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_activate_logical(int cfg, int num)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg_io0(cfg, num, io0))
+		return -EIO;
+	if (msnd_write_cfg_io1(cfg, num, io1))
+		return -EIO;
+	if (msnd_write_cfg_irq(cfg, num, irq))
+		return -EIO;
+	if (msnd_write_cfg_mem(cfg, num, mem))
+		return -EIO;
+	if (msnd_activate_logical(cfg, num))
+		return -EIO;
+	return 0;
+}
+
+typedef struct msnd_pinnacle_cfg_device {
+	WORD io0, io1, irq;
+	int mem;
+} msnd_pinnacle_cfg_t[4];
+
+static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device)
+{
+	int i;
+
+	/* Reset devices if told to */
+	if (reset) {
+		printk(KERN_INFO LOGNAME ": Resetting all devices\n");
+		for (i = 0; i < 4; ++i)
+			if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
+				return -EIO;
+	}
+
+	/* Configure specified devices */
+	for (i = 0; i < 4; ++i) {
+
+		switch (i) {
+		case 0:		/* DSP */
+			if (!(device[i].io0 && device[i].irq && device[i].mem))
+				continue;
+			break;
+		case 1:		/* MPU */
+			if (!(device[i].io0 && device[i].irq))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring MPU to I/O 0x%x IRQ %d\n",
+			       device[i].io0, device[i].irq);
+			break;
+		case 2:		/* IDE */
+			if (!(device[i].io0 && device[i].io1 && device[i].irq))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n",
+			       device[i].io0, device[i].io1, device[i].irq);
+			break;
+		case 3:		/* Joystick */
+			if (!(device[i].io0))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring joystick to I/O 0x%x\n",
+			       device[i].io0);
+			break;
+		}
+
+		/* Configure the device */
+		if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem))
+			return -EIO;
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR				("Andrew Veliath <andrewtv@usa.net>");
+MODULE_DESCRIPTION			("Turtle Beach " LONGNAME " Linux Driver");
+MODULE_LICENSE("GPL");
+
+static int io __initdata =		-1;
+static int irq __initdata =		-1;
+static int mem __initdata =		-1;
+static int write_ndelay __initdata =	-1;
+
+#ifndef MSND_CLASSIC
+/* Pinnacle/Fiji non-PnP Config Port */
+static int cfg __initdata =		-1;
+
+/* Extra Peripheral Configuration */
+static int reset __initdata = 0;
+static int mpu_io __initdata = 0;
+static int mpu_irq __initdata = 0;
+static int ide_io0 __initdata = 0;
+static int ide_io1 __initdata = 0;
+static int ide_irq __initdata = 0;
+static int joystick_io __initdata = 0;
+
+/* If we have the digital daugherboard... */
+static bool digital __initdata = false;
+#endif
+
+static int fifosize __initdata =	DEFFIFOSIZE;
+static int calibrate_signal __initdata = 0;
+
+#else /* not a module */
+
+static int write_ndelay __initdata =	-1;
+
+#ifdef MSND_CLASSIC
+static int io __initdata =		CONFIG_MSNDCLAS_IO;
+static int irq __initdata =		CONFIG_MSNDCLAS_IRQ;
+static int mem __initdata =		CONFIG_MSNDCLAS_MEM;
+#else /* Pinnacle/Fiji */
+
+static int io __initdata =		CONFIG_MSNDPIN_IO;
+static int irq __initdata =		CONFIG_MSNDPIN_IRQ;
+static int mem __initdata =		CONFIG_MSNDPIN_MEM;
+
+/* Pinnacle/Fiji non-PnP Config Port */
+#ifdef CONFIG_MSNDPIN_NONPNP
+#  ifndef CONFIG_MSNDPIN_CFG
+#    define CONFIG_MSNDPIN_CFG		0x250
+#  endif
+#else
+#  ifdef CONFIG_MSNDPIN_CFG
+#    undef CONFIG_MSNDPIN_CFG
+#  endif
+#  define CONFIG_MSNDPIN_CFG		-1
+#endif
+static int cfg __initdata =		CONFIG_MSNDPIN_CFG;
+/* If not a module, we don't need to bother with reset=1 */
+static int reset;
+
+/* Extra Peripheral Configuration (Default: Disable) */
+#ifndef CONFIG_MSNDPIN_MPU_IO
+#  define CONFIG_MSNDPIN_MPU_IO		0
+#endif
+static int mpu_io __initdata =		CONFIG_MSNDPIN_MPU_IO;
+
+#ifndef CONFIG_MSNDPIN_MPU_IRQ
+#  define CONFIG_MSNDPIN_MPU_IRQ	0
+#endif
+static int mpu_irq __initdata =		CONFIG_MSNDPIN_MPU_IRQ;
+
+#ifndef CONFIG_MSNDPIN_IDE_IO0
+#  define CONFIG_MSNDPIN_IDE_IO0	0
+#endif
+static int ide_io0 __initdata =		CONFIG_MSNDPIN_IDE_IO0;
+
+#ifndef CONFIG_MSNDPIN_IDE_IO1
+#  define CONFIG_MSNDPIN_IDE_IO1	0
+#endif
+static int ide_io1 __initdata =		CONFIG_MSNDPIN_IDE_IO1;
+
+#ifndef CONFIG_MSNDPIN_IDE_IRQ
+#  define CONFIG_MSNDPIN_IDE_IRQ	0
+#endif
+static int ide_irq __initdata =		CONFIG_MSNDPIN_IDE_IRQ;
+
+#ifndef CONFIG_MSNDPIN_JOYSTICK_IO
+#  define CONFIG_MSNDPIN_JOYSTICK_IO	0
+#endif
+static int joystick_io __initdata =	CONFIG_MSNDPIN_JOYSTICK_IO;
+
+/* Have SPDIF (Digital) Daughterboard */
+#ifndef CONFIG_MSNDPIN_DIGITAL
+#  define CONFIG_MSNDPIN_DIGITAL	0
+#endif
+static bool digital __initdata =	CONFIG_MSNDPIN_DIGITAL;
+
+#endif /* MSND_CLASSIC */
+
+#ifndef CONFIG_MSND_FIFOSIZE
+#  define CONFIG_MSND_FIFOSIZE		DEFFIFOSIZE
+#endif
+static int fifosize __initdata =	CONFIG_MSND_FIFOSIZE;
+
+#ifndef CONFIG_MSND_CALSIGNAL
+#  define CONFIG_MSND_CALSIGNAL		0
+#endif
+static int
+calibrate_signal __initdata =		CONFIG_MSND_CALSIGNAL;
+#endif /* MODULE */
+
+module_param				(io, int, 0);
+module_param				(irq, int, 0);
+module_param				(mem, int, 0);
+module_param				(write_ndelay, int, 0);
+module_param				(fifosize, int, 0);
+module_param				(calibrate_signal, int, 0);
+#ifndef MSND_CLASSIC
+module_param				(digital, bool, 0);
+module_param				(cfg, int, 0);
+module_param				(reset, int, 0);
+module_param				(mpu_io, int, 0);
+module_param				(mpu_irq, int, 0);
+module_param				(ide_io0, int, 0);
+module_param				(ide_io1, int, 0);
+module_param				(ide_irq, int, 0);
+module_param				(joystick_io, int, 0);
+#endif
+
+static int __init msnd_init(void)
+{
+	int err;
+#ifndef MSND_CLASSIC
+	static msnd_pinnacle_cfg_t pinnacle_devs;
+#endif /* MSND_CLASSIC */
+
+	printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version "
+	       VERSION ", Copyright (C) 1998 Andrew Veliath\n");
+
+	if (io == -1 || irq == -1 || mem == -1)
+		printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
+
+#ifdef MSND_CLASSIC
+	if (io == -1 ||
+	    !(io == 0x290 ||
+	      io == 0x260 ||
+	      io == 0x250 ||
+	      io == 0x240 ||
+	      io == 0x230 ||
+	      io == 0x220 ||
+	      io == 0x210 ||
+	      io == 0x3e0)) {
+		printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n");
+		return -EINVAL;
+	}
+#else
+	if (io == -1 ||
+		io < 0x100 ||
+		io > 0x3e0 ||
+		(io % 0x10) != 0) {
+			printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n");
+			return -EINVAL;
+	}
+#endif /* MSND_CLASSIC */
+
+	if (irq == -1 ||
+	    !(irq == 5 ||
+	      irq == 7 ||
+	      irq == 9 ||
+	      irq == 10 ||
+	      irq == 11 ||
+	      irq == 12)) {
+		printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
+		return -EINVAL;
+	}
+
+	if (mem == -1 ||
+	    !(mem == 0xb0000 ||
+	      mem == 0xc8000 ||
+	      mem == 0xd0000 ||
+	      mem == 0xd8000 ||
+	      mem == 0xe0000 ||
+	      mem == 0xe8000)) {
+		printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
+		       "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n");
+		return -EINVAL;
+	}
+
+#ifdef MSND_CLASSIC
+	switch (irq) {
+	case 5: dev.irqid = HPIRQ_5; break;
+	case 7: dev.irqid = HPIRQ_7; break;
+	case 9: dev.irqid = HPIRQ_9; break;
+	case 10: dev.irqid = HPIRQ_10; break;
+	case 11: dev.irqid = HPIRQ_11; break;
+	case 12: dev.irqid = HPIRQ_12; break;
+	}
+
+	switch (mem) {
+	case 0xb0000: dev.memid = HPMEM_B000; break;
+	case 0xc8000: dev.memid = HPMEM_C800; break;
+	case 0xd0000: dev.memid = HPMEM_D000; break;
+	case 0xd8000: dev.memid = HPMEM_D800; break;
+	case 0xe0000: dev.memid = HPMEM_E000; break;
+	case 0xe8000: dev.memid = HPMEM_E800; break;
+	}
+#else
+	if (cfg == -1) {
+		printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+	} else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) {
+		printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n");
+		return -EINVAL;
+	} else {
+		printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg);
+
+		/* DSP */
+		pinnacle_devs[0].io0 = io;
+		pinnacle_devs[0].irq = irq;
+		pinnacle_devs[0].mem = mem;
+
+		/* The following are Pinnacle specific */
+
+		/* MPU */
+		pinnacle_devs[1].io0 = mpu_io;
+		pinnacle_devs[1].irq = mpu_irq;
+
+		/* IDE */
+		pinnacle_devs[2].io0 = ide_io0;
+		pinnacle_devs[2].io1 = ide_io1;
+		pinnacle_devs[2].irq = ide_irq;
+
+		/* Joystick */
+		pinnacle_devs[3].io0 = joystick_io;
+
+		if (!request_region(cfg, 2, "Pinnacle/Fiji Config")) {
+			printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg);
+			return -EIO;
+		}
+
+		if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) {
+			printk(KERN_ERR LOGNAME ": Device configuration error\n");
+			release_region(cfg, 2);
+			return -EIO;
+		}
+		release_region(cfg, 2);
+	}
+#endif /* MSND_CLASSIC */
+
+	if (fifosize < 16)
+		fifosize = 16;
+
+	if (fifosize > 1024)
+		fifosize = 1024;
+
+	set_default_audio_parameters();
+#ifdef MSND_CLASSIC
+	dev.type = msndClassic;
+#else
+	dev.type = msndPinnacle;
+#endif
+	dev.io = io;
+	dev.numio = DSP_NUMIO;
+	dev.irq = irq;
+	dev.base = ioremap(mem, 0x8000);
+	dev.fifosize = fifosize * 1024;
+	dev.calibrate_signal = calibrate_signal ? 1 : 0;
+	dev.recsrc = 0;
+	dev.dspq_data_buff = DSPQ_DATA_BUFF;
+	dev.dspq_buff_size = DSPQ_BUFF_SIZE;
+	if (write_ndelay == -1)
+		write_ndelay = CONFIG_MSND_WRITE_NDELAY;
+	if (write_ndelay)
+		clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
+	else
+		set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
+#ifndef MSND_CLASSIC
+	if (digital)
+		set_bit(F_HAVEDIGITAL, &dev.flags);
+#endif
+	init_waitqueue_head(&dev.writeblock);
+	init_waitqueue_head(&dev.readblock);
+	init_waitqueue_head(&dev.writeflush);
+	msnd_fifo_init(&dev.DAPF);
+	msnd_fifo_init(&dev.DARF);
+	spin_lock_init(&dev.lock);
+	printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize);
+	if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) {
+		printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n");
+		return err;
+	}
+
+	if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) {
+		printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n");
+		msnd_fifo_free(&dev.DAPF);
+		return err;
+	}
+
+	if ((err = probe_multisound()) < 0) {
+		printk(KERN_ERR LOGNAME ": Probe failed\n");
+		msnd_fifo_free(&dev.DAPF);
+		msnd_fifo_free(&dev.DARF);
+		return err;
+	}
+
+	if ((err = attach_multisound()) < 0) {
+		printk(KERN_ERR LOGNAME ": Attach failed\n");
+		msnd_fifo_free(&dev.DAPF);
+		msnd_fifo_free(&dev.DARF);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit msdn_cleanup(void)
+{
+	unload_multisound();
+	msnd_fifo_free(&dev.DAPF);
+	msnd_fifo_free(&dev.DARF);
+}
+
+module_init(msnd_init);
+module_exit(msdn_cleanup);
diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h
new file mode 100644
index 00000000..c18d66cb
--- /dev/null
+++ b/sound/oss/msnd_pinnacle.h
@@ -0,0 +1,246 @@
+/*********************************************************************
+ *
+ * msnd_pinnacle.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ ********************************************************************/
+#ifndef __MSND_PINNACLE_H
+#define __MSND_PINNACLE_H
+
+
+#define DSP_NUMIO				0x08
+
+#define IREG_LOGDEVICE				0x07
+#define IREG_ACTIVATE				0x30
+#define LD_ACTIVATE				0x01
+#define LD_DISACTIVATE				0x00
+#define IREG_EECONTROL				0x3F
+#define IREG_MEMBASEHI				0x40
+#define IREG_MEMBASELO				0x41
+#define IREG_MEMCONTROL				0x42
+#define IREG_MEMRANGEHI				0x43
+#define IREG_MEMRANGELO				0x44
+#define MEMTYPE_8BIT				0x00
+#define MEMTYPE_16BIT				0x02
+#define MEMTYPE_RANGE				0x00
+#define MEMTYPE_HIADDR				0x01
+#define IREG_IO0_BASEHI				0x60
+#define IREG_IO0_BASELO				0x61
+#define IREG_IO1_BASEHI				0x62
+#define IREG_IO1_BASELO				0x63
+#define IREG_IRQ_NUMBER				0x70
+#define IREG_IRQ_TYPE				0x71
+#define IRQTYPE_HIGH				0x02
+#define IRQTYPE_LOW				0x00
+#define IRQTYPE_LEVEL				0x01
+#define IRQTYPE_EDGE				0x00
+
+#define	HP_DSPR					0x04
+#define	HP_BLKS					0x04
+
+#define HPDSPRESET_OFF				2
+#define HPDSPRESET_ON				0
+
+#define HPBLKSEL_0				2
+#define HPBLKSEL_1				3
+
+#define	HIMT_DAT_OFF				0x03
+
+#define	HIDSP_PLAY_UNDER			0x00
+#define	HIDSP_INT_PLAY_UNDER			0x01
+#define	HIDSP_SSI_TX_UNDER  			0x02
+#define HIDSP_RECQ_OVERFLOW			0x08
+#define HIDSP_INT_RECORD_OVER			0x09
+#define HIDSP_SSI_RX_OVERFLOW			0x0a
+
+#define	HIDSP_MIDI_IN_OVER			0x10
+
+#define	HIDSP_MIDI_FRAME_ERR			0x11
+#define	HIDSP_MIDI_PARITY_ERR			0x12
+#define	HIDSP_MIDI_OVERRUN_ERR			0x13
+
+#define HIDSP_INPUT_CLIPPING			0x20
+#define	HIDSP_MIX_CLIPPING			0x30
+#define HIDSP_DAT_IN_OFF			0x21
+
+#define	HDEXAR_SET_ANA_IN			0
+#define	HDEXAR_CLEAR_PEAKS			1
+#define	HDEXAR_IN_SET_POTS			2
+#define	HDEXAR_AUX_SET_POTS			3
+#define	HDEXAR_CAL_A_TO_D			4
+#define	HDEXAR_RD_EXT_DSP_BITS			5
+
+#define	HDEXAR_SET_SYNTH_IN			4
+#define	HDEXAR_READ_DAT_IN			5
+#define	HDEXAR_MIC_SET_POTS			6
+#define	HDEXAR_SET_DAT_IN			7
+
+#define HDEXAR_SET_SYNTH_48			8
+#define HDEXAR_SET_SYNTH_44			9
+
+#define TIME_PRO_RESET_DONE			0x028A
+#define TIME_PRO_SYSEX				0x001E
+#define TIME_PRO_RESET				0x0032
+
+#define AGND					0x01
+#define SIGNAL					0x02
+
+#define EXT_DSP_BIT_DCAL			0x0001
+#define EXT_DSP_BIT_MIDI_CON			0x0002
+
+#define BUFFSIZE				0x8000
+#define HOSTQ_SIZE				0x40
+
+#define SRAM_CNTL_START				0x7F00
+#define SMA_STRUCT_START			0x7F40
+
+#define DAP_BUFF_SIZE				0x2400
+#define DAR_BUFF_SIZE				0x2000
+
+#define DAPQ_STRUCT_SIZE			0x10
+#define DARQ_STRUCT_SIZE			0x10
+#define DAPQ_BUFF_SIZE				(3 * 0x10)
+#define DARQ_BUFF_SIZE				(3 * 0x10)
+#define MODQ_BUFF_SIZE				0x400
+#define MIDQ_BUFF_SIZE				0x800
+#define DSPQ_BUFF_SIZE				0x5A0
+
+#define DAPQ_DATA_BUFF				0x6C00
+#define DARQ_DATA_BUFF				0x6C30
+#define MODQ_DATA_BUFF				0x6C60
+#define MIDQ_DATA_BUFF				0x7060
+#define DSPQ_DATA_BUFF				0x7860
+
+#define DAPQ_OFFSET				SRAM_CNTL_START
+#define DARQ_OFFSET				(SRAM_CNTL_START + 0x08)
+#define MODQ_OFFSET				(SRAM_CNTL_START + 0x10)
+#define MIDQ_OFFSET				(SRAM_CNTL_START + 0x18)
+#define DSPQ_OFFSET				(SRAM_CNTL_START + 0x20)
+
+#define MOP_WAVEHDR				0
+#define MOP_EXTOUT				1
+#define MOP_HWINIT				0xfe
+#define MOP_NONE				0xff
+#define MOP_MAX					1
+
+#define MIP_EXTIN				0
+#define MIP_WAVEHDR				1
+#define MIP_HWINIT				0xfe
+#define MIP_MAX					1
+
+/* Pinnacle/Fiji SMA Common Data */
+#define SMA_wCurrPlayBytes			0x0000
+#define SMA_wCurrRecordBytes			0x0002
+#define SMA_wCurrPlayVolLeft			0x0004
+#define SMA_wCurrPlayVolRight			0x0006
+#define SMA_wCurrInVolLeft			0x0008
+#define SMA_wCurrInVolRight			0x000a
+#define SMA_wCurrMHdrVolLeft			0x000c
+#define SMA_wCurrMHdrVolRight			0x000e
+#define SMA_dwCurrPlayPitch			0x0010
+#define SMA_dwCurrPlayRate			0x0014
+#define SMA_wCurrMIDIIOPatch			0x0018
+#define SMA_wCurrPlayFormat			0x001a
+#define SMA_wCurrPlaySampleSize			0x001c
+#define SMA_wCurrPlayChannels			0x001e
+#define SMA_wCurrPlaySampleRate			0x0020
+#define SMA_wCurrRecordFormat			0x0022
+#define SMA_wCurrRecordSampleSize		0x0024
+#define SMA_wCurrRecordChannels			0x0026
+#define SMA_wCurrRecordSampleRate		0x0028
+#define SMA_wCurrDSPStatusFlags			0x002a
+#define SMA_wCurrHostStatusFlags		0x002c
+#define SMA_wCurrInputTagBits			0x002e
+#define SMA_wCurrLeftPeak			0x0030
+#define SMA_wCurrRightPeak			0x0032
+#define SMA_bMicPotPosLeft			0x0034
+#define SMA_bMicPotPosRight			0x0035
+#define SMA_bMicPotMaxLeft			0x0036
+#define SMA_bMicPotMaxRight			0x0037
+#define SMA_bInPotPosLeft			0x0038
+#define SMA_bInPotPosRight			0x0039
+#define SMA_bAuxPotPosLeft			0x003a
+#define SMA_bAuxPotPosRight			0x003b
+#define SMA_bInPotMaxLeft			0x003c
+#define SMA_bInPotMaxRight			0x003d
+#define SMA_bAuxPotMaxLeft			0x003e
+#define SMA_bAuxPotMaxRight			0x003f
+#define SMA_bInPotMaxMethod			0x0040
+#define SMA_bAuxPotMaxMethod			0x0041
+#define SMA_wCurrMastVolLeft			0x0042
+#define SMA_wCurrMastVolRight			0x0044
+#define SMA_wCalFreqAtoD			0x0046
+#define SMA_wCurrAuxVolLeft			0x0048
+#define SMA_wCurrAuxVolRight			0x004a
+#define SMA_wCurrPlay1VolLeft			0x004c
+#define SMA_wCurrPlay1VolRight			0x004e
+#define SMA_wCurrPlay2VolLeft			0x0050
+#define SMA_wCurrPlay2VolRight			0x0052
+#define SMA_wCurrPlay3VolLeft			0x0054
+#define SMA_wCurrPlay3VolRight			0x0056
+#define SMA_wCurrPlay4VolLeft			0x0058
+#define SMA_wCurrPlay4VolRight			0x005a
+#define SMA_wCurrPlay1PeakLeft			0x005c
+#define SMA_wCurrPlay1PeakRight			0x005e
+#define SMA_wCurrPlay2PeakLeft			0x0060
+#define SMA_wCurrPlay2PeakRight			0x0062
+#define SMA_wCurrPlay3PeakLeft			0x0064
+#define SMA_wCurrPlay3PeakRight			0x0066
+#define SMA_wCurrPlay4PeakLeft			0x0068
+#define SMA_wCurrPlay4PeakRight			0x006a
+#define SMA_wCurrPlayPeakLeft			0x006c
+#define SMA_wCurrPlayPeakRight			0x006e
+#define SMA_wCurrDATSR				0x0070
+#define SMA_wCurrDATRXCHNL			0x0072
+#define SMA_wCurrDATTXCHNL			0x0074
+#define SMA_wCurrDATRXRate			0x0076
+#define SMA_dwDSPPlayCount			0x0078
+#define SMA__size				0x007c
+
+#ifdef HAVE_DSPCODEH
+#  include "pndsperm.c"
+#  include "pndspini.c"
+#  define PERMCODE		pndsperm
+#  define INITCODE		pndspini
+#  define PERMCODESIZE		sizeof(pndsperm)
+#  define INITCODESIZE		sizeof(pndspini)
+#else
+#  ifndef CONFIG_MSNDPIN_INIT_FILE
+#    define CONFIG_MSNDPIN_INIT_FILE				\
+				"/etc/sound/pndspini.bin"
+#  endif
+#  ifndef CONFIG_MSNDPIN_PERM_FILE
+#    define CONFIG_MSNDPIN_PERM_FILE				\
+				"/etc/sound/pndsperm.bin"
+#  endif
+#  define PERMCODEFILE		CONFIG_MSNDPIN_PERM_FILE
+#  define INITCODEFILE		CONFIG_MSNDPIN_INIT_FILE
+#  define PERMCODE		dspini
+#  define INITCODE		permini
+#  define PERMCODESIZE		sizeof_dspini
+#  define INITCODESIZE		sizeof_permini
+#endif
+#define LONGNAME		"MultiSound (Pinnacle/Fiji)"
+
+#endif /* __MSND_PINNACLE_H */
diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c
new file mode 100644
index 00000000..407cd677
--- /dev/null
+++ b/sound/oss/opl3.c
@@ -0,0 +1,1258 @@
+/*
+ * sound/oss/opl3.c
+ *
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ *
+ *
+ * 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.
+ *
+ *
+ * Changes
+ *	Thomas Sailer   	ioctl code reworked (vmalloc/vfree removed)
+ *	Alan Cox		modularisation, fixed sound_mem allocs.
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *	Arnaldo C. de Melo	get rid of check_region, use request_region for
+ *				OPL4, release it on exit, some cleanups.
+ *
+ * Status
+ *	Believed to work. Badly needs rewriting a bit to support multiple
+ *	OPL3 devices.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+/*
+ * Major improvements to the FM handling 30AUG92 by Rob Hooft,
+ * hooft@chem.ruu.nl
+ */
+
+#include "sound_config.h"
+
+#include "opl3_hw.h"
+
+#define MAX_VOICE	18
+#define OFFS_4OP	11
+
+struct voice_info
+{
+	unsigned char   keyon_byte;
+	long            bender;
+	long            bender_range;
+	unsigned long   orig_freq;
+	unsigned long   current_freq;
+	int             volume;
+	int             mode;
+	int             panning;	/* 0xffff means not set */
+};
+
+typedef struct opl_devinfo
+{
+	int             base;
+	int             left_io, right_io;
+	int             nr_voice;
+	int             lv_map[MAX_VOICE];
+
+	struct voice_info voc[MAX_VOICE];
+	struct voice_alloc_info *v_alloc;
+	struct channel_info *chn_info;
+
+	struct sbi_instrument i_map[SBFM_MAXINSTR];
+	struct sbi_instrument *act_i[MAX_VOICE];
+
+	struct synth_info fm_info;
+
+	int             busy;
+	int             model;
+	unsigned char   cmask;
+
+	int             is_opl4;
+} opl_devinfo;
+
+static struct opl_devinfo *devc = NULL;
+
+static int      detected_model;
+
+static int      store_instr(int instr_no, struct sbi_instrument *instr);
+static void     freq_to_fnum(int freq, int *block, int *fnum);
+static void     opl3_command(int io_addr, unsigned int addr, unsigned int val);
+static int      opl3_kill_note(int dev, int voice, int note, int velocity);
+
+static void enter_4op_mode(void)
+{
+	int i;
+	static int v4op[MAX_VOICE] = {
+		0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17
+	};
+
+	devc->cmask = 0x3f;	/* Connect all possible 4 OP voice operators */
+	opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f);
+
+	for (i = 0; i < 3; i++)
+		pv_map[i].voice_mode = 4;
+	for (i = 3; i < 6; i++)
+		pv_map[i].voice_mode = 0;
+
+	for (i = 9; i < 12; i++)
+		pv_map[i].voice_mode = 4;
+	for (i = 12; i < 15; i++)
+		pv_map[i].voice_mode = 0;
+
+	for (i = 0; i < 12; i++)
+		devc->lv_map[i] = v4op[i];
+	devc->v_alloc->max_voice = devc->nr_voice = 12;
+}
+
+static int opl3_ioctl(int dev, unsigned int cmd, void __user * arg)
+{
+	struct sbi_instrument ins;
+	
+	switch (cmd) {
+		case SNDCTL_FM_LOAD_INSTR:
+			printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+			if (copy_from_user(&ins, arg, sizeof(ins)))
+				return -EFAULT;
+			if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
+				printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
+				return -EINVAL;
+			}
+			return store_instr(ins.channel, &ins);
+
+		case SNDCTL_SYNTH_INFO:
+			devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
+			if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			return 0x7fffffff;
+
+		case SNDCTL_FM_4OP_ENABLE:
+			if (devc->model == 2)
+				enter_4op_mode();
+			return 0;
+
+		default:
+			return -EINVAL;
+	}
+}
+
+static int opl3_detect(int ioaddr)
+{
+	/*
+	 * This function returns 1 if the FM chip is present at the given I/O port
+	 * The detection algorithm plays with the timer built in the FM chip and
+	 * looks for a change in the status register.
+	 *
+	 * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+	 * boards.
+	 *
+	 * Note2! The chip is initialized if detected.
+	 */
+
+	unsigned char stat1, signature;
+	int i;
+
+	if (devc != NULL)
+	{
+		printk(KERN_ERR "opl3: Only one OPL3 supported.\n");
+		return 0;
+	}
+
+	devc = kzalloc(sizeof(*devc), GFP_KERNEL);
+
+	if (devc == NULL)
+	{
+		printk(KERN_ERR "opl3: Can't allocate memory for the device control "
+			"structure \n ");
+		return 0;
+	}
+
+	strcpy(devc->fm_info.name, "OPL2");
+
+	if (!request_region(ioaddr, 4, devc->fm_info.name)) {
+		printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr);
+		goto cleanup_devc;
+	}
+
+	devc->base = ioaddr;
+
+	/* Reset timers 1 and 2 */
+	opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+
+	/* Reset the IRQ of the FM chip */
+	opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+	signature = stat1 = inb(ioaddr);	/* Status register */
+
+	if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
+		signature != 0x0f)
+	{
+		MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature));
+		goto cleanup_region;
+	}
+
+	if (signature == 0x06)		/* OPL2 */
+	{
+		detected_model = 2;
+	}
+	else if (signature == 0x00 || signature == 0x0f)	/* OPL3 or OPL4 */
+	{
+		unsigned char tmp;
+
+		detected_model = 3;
+
+		/*
+		 * Detect availability of OPL4 (_experimental_). Works probably
+		 * only after a cold boot. In addition the OPL4 port
+		 * of the chip may not be connected to the PC bus at all.
+		 */
+
+		opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00);
+		opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
+
+		if ((tmp = inb(ioaddr)) == 0x02)	/* Have a OPL4 */
+		{
+			detected_model = 4;
+		}
+
+		if (request_region(ioaddr - 8, 2, "OPL4"))	/* OPL4 port was free */
+		{
+			int tmp;
+
+			outb((0x02), ioaddr - 8);	/* Select OPL4 ID register */
+			udelay(10);
+			tmp = inb(ioaddr - 7);		/* Read it */
+			udelay(10);
+
+			if (tmp == 0x20)	/* OPL4 should return 0x20 here */
+			{
+				detected_model = 4;
+				outb((0xF8), ioaddr - 8);	/* Select OPL4 FM mixer control */
+				udelay(10);
+				outb((0x1B), ioaddr - 7);	/* Write value */
+				udelay(10);
+			}
+			else
+			{ /* release OPL4 port */
+				release_region(ioaddr - 8, 2);
+				detected_model = 3;
+			}
+		}
+		opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0);
+	}
+	for (i = 0; i < 9; i++)
+		opl3_command(ioaddr, KEYON_BLOCK + i, 0);	/*
+								 * Note off
+								 */
+
+	opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
+	opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00);	/*
+								 * Melodic mode.
+								 */
+	return 1;
+cleanup_region:
+	release_region(ioaddr, 4);
+cleanup_devc:
+	kfree(devc);
+	devc = NULL;
+	return 0;
+}
+
+static int opl3_kill_note  (int devno, int voice, int note, int velocity)
+{
+	 struct physical_voice_info *map;
+
+	 if (voice < 0 || voice >= devc->nr_voice)
+		 return 0;
+
+	 devc->v_alloc->map[voice] = 0;
+
+	 map = &pv_map[devc->lv_map[voice]];
+	 DEB(printk("Kill note %d\n", voice));
+
+	 if (map->voice_mode == 0)
+		 return 0;
+
+	 opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20);
+	 devc->voc[voice].keyon_byte = 0;
+	 devc->voc[voice].bender = 0;
+	 devc->voc[voice].volume = 64;
+	 devc->voc[voice].panning = 0xffff;	/* Not set */
+	 devc->voc[voice].bender_range = 200;
+	 devc->voc[voice].orig_freq = 0;
+	 devc->voc[voice].current_freq = 0;
+	 devc->voc[voice].mode = 0;
+	 return 0;
+}
+
+#define HIHAT			0
+#define CYMBAL			1
+#define TOMTOM			2
+#define SNARE			3
+#define BDRUM			4
+#define UNDEFINED		TOMTOM
+#define DEFAULT			TOMTOM
+
+static int store_instr(int instr_no, struct sbi_instrument *instr)
+{
+	if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2))
+		printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
+	memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr));
+	return 0;
+}
+
+static int opl3_set_instr  (int dev, int voice, int instr_no)
+{
+	if (voice < 0 || voice >= devc->nr_voice)
+		return 0;
+	if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+		instr_no = 0;	/* Acoustic piano (usually) */
+
+	devc->act_i[voice] = &devc->i_map[instr_no];
+	return 0;
+}
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume
+ * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (RH)
+ */
+
+static char fm_volume_table[128] =
+{
+	-64, -48, -40, -35, -32, -29, -27, -26,
+	-24, -23, -21, -20, -19, -18, -18, -17,
+	-16, -15, -15, -14, -13, -13, -12, -12,
+	-11, -11, -10, -10, -10, -9, -9, -8,
+	-8, -8, -7, -7, -7, -6, -6, -6,
+	-5, -5, -5, -5, -4, -4, -4, -4,
+	-3, -3, -3, -3, -2, -2, -2, -2,
+	-2, -1, -1, -1, -1, 0, 0, 0,
+	0, 0, 0, 1, 1, 1, 1, 1,
+	1, 2, 2, 2, 2, 2, 2, 2,
+	3, 3, 3, 3, 3, 3, 3, 4,
+	4, 4, 4, 4, 4, 4, 4, 5,
+	5, 5, 5, 5, 5, 5, 5, 5,
+	6, 6, 6, 6, 6, 6, 6, 6,
+	6, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 8, 8, 8, 8, 8
+};
+
+static void calc_vol(unsigned char *regbyte, int volume, int main_vol)
+{
+	int level = (~*regbyte & 0x3f);
+
+	if (main_vol > 127)
+		main_vol = 127;
+	volume = (volume * main_vol) / 127;
+
+	if (level)
+		level += fm_volume_table[volume];
+
+	if (level > 0x3f)
+		level = 0x3f;
+	if (level < 0)
+		level = 0;
+
+	*regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+}
+
+static void set_voice_volume(int voice, int volume, int main_vol)
+{
+	unsigned char vol1, vol2, vol3, vol4;
+	struct sbi_instrument *instr;
+	struct physical_voice_info *map;
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	map = &pv_map[devc->lv_map[voice]];
+	instr = devc->act_i[voice];
+
+	if (!instr)
+		instr = &devc->i_map[0];
+
+	if (instr->channel < 0)
+		return;
+
+	if (devc->voc[voice].mode == 0)
+		return;
+
+	if (devc->voc[voice].mode == 2)
+	{
+		vol1 = instr->operators[2];
+		vol2 = instr->operators[3];
+		if ((instr->operators[10] & 0x01))
+		{
+			calc_vol(&vol1, volume, main_vol);
+			calc_vol(&vol2, volume, main_vol);
+		}
+		else
+		{
+			calc_vol(&vol2, volume, main_vol);
+		}
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+	}
+	else
+	{	/*
+		 * 4 OP voice
+		 */
+		int connection;
+
+		vol1 = instr->operators[2];
+		vol2 = instr->operators[3];
+		vol3 = instr->operators[OFFS_4OP + 2];
+		vol4 = instr->operators[OFFS_4OP + 3];
+
+		/*
+		 * The connection method for 4 OP devc->voc is defined by the rightmost
+		 * bits at the offsets 10 and 10+OFFS_4OP
+		 */
+
+		connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+		switch (connection)
+		{
+			case 0:
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			case 1:
+				calc_vol(&vol2, volume, main_vol);
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			case 2:
+				calc_vol(&vol1, volume, main_vol);
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			case 3:
+				calc_vol(&vol1, volume, main_vol);
+				calc_vol(&vol3, volume, main_vol);
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			default:
+				;
+		}
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4);
+	}
+}
+
+static int opl3_start_note (int dev, int voice, int note, int volume)
+{
+	unsigned char data, fpc;
+	int block, fnum, freq, voice_mode, pan;
+	struct sbi_instrument *instr;
+	struct physical_voice_info *map;
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return 0;
+
+	map = &pv_map[devc->lv_map[voice]];
+	pan = devc->voc[voice].panning;
+
+	if (map->voice_mode == 0)
+		return 0;
+
+	if (note == 255)	/*
+				 * Just change the volume
+				 */
+	{
+		set_voice_volume(voice, volume, devc->voc[voice].volume);
+		return 0;
+	}
+
+	/*
+	 * Kill previous note before playing
+	 */
+	
+	opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff);	/*
+									 * Carrier
+									 * volume to
+									 * min
+									 */
+	opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff);	/*
+									 * Modulator
+									 * volume to
+									 */
+
+	if (map->voice_mode == 4)
+	{
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
+	}
+
+	opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00);	/*
+									 * Note
+									 * off
+									 */
+
+	instr = devc->act_i[voice];
+	
+	if (!instr)
+		instr = &devc->i_map[0];
+
+	if (instr->channel < 0)
+	{
+		printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice);
+		return 0;
+	}
+
+	if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
+		return 0;	/*
+				 * Cannot play
+				 */
+
+	voice_mode = map->voice_mode;
+
+	if (voice_mode == 4)
+	{
+		int voice_shift;
+
+		voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3;
+		voice_shift += map->voice_num;
+
+		if (instr->key != OPL3_PATCH)	/*
+						 * Just 2 OP patch
+						 */
+		{
+			voice_mode = 2;
+			devc->cmask &= ~(1 << voice_shift);
+		}
+		else
+		{
+			devc->cmask |= (1 << voice_shift);
+		}
+
+		opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
+	}
+
+	/*
+	 * Set Sound Characteristics
+	 */
+	
+	opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
+	opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
+
+	/*
+	 * Set Attack/Decay
+	 */
+	
+	opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
+	opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
+
+	/*
+	 * Set Sustain/Release
+	 */
+	
+	opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
+	opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+
+	/*
+	 * Set Wave Select
+	 */
+
+	opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
+	opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
+
+	/*
+	 * Set Feedback/Connection
+	 */
+	
+	fpc = instr->operators[10];
+
+	if (pan != 0xffff)
+	{
+		fpc &= ~STEREO_BITS;
+		if (pan < -64)
+			fpc |= VOICE_TO_LEFT;
+		else
+			if (pan > 64)
+				fpc |= VOICE_TO_RIGHT;
+			else
+				fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT);
+	}
+
+	if (!(fpc & 0x30))
+		fpc |= 0x30;	/*
+				 * Ensure that at least one chn is enabled
+				 */
+	opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc);
+
+	/*
+	 * If the voice is a 4 OP one, initialize the operators 3 and 4 also
+	 */
+
+	if (voice_mode == 4)
+	{
+		/*
+		 * Set Sound Characteristics
+		 */
+	
+		opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
+		opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+
+		/*
+		 * Set Attack/Decay
+		 */
+		
+		opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
+		opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+
+		/*
+		 * Set Sustain/Release
+		 */
+		
+		opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
+		opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+
+		/*
+		 * Set Wave Select
+		 */
+		
+		opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
+		opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+
+		/*
+		 * Set Feedback/Connection
+		 */
+		
+		fpc = instr->operators[OFFS_4OP + 10];
+		if (!(fpc & 0x30))
+			 fpc |= 0x30;	/*
+					 * Ensure that at least one chn is enabled
+					 */
+		opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
+	}
+
+	devc->voc[voice].mode = voice_mode;
+	set_voice_volume(voice, volume, devc->voc[voice].volume);
+
+	freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000;
+
+	/*
+	 * Since the pitch bender may have been set before playing the note, we
+	 * have to calculate the bending now.
+	 */
+
+	freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
+	devc->voc[voice].current_freq = freq;
+
+	freq_to_fnum(freq, &block, &fnum);
+
+	/*
+	 * Play note
+	 */
+
+	data = fnum & 0xff;	/*
+				 * Least significant bits of fnumber
+				 */
+	opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+	data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+		 devc->voc[voice].keyon_byte = data;
+	opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+	if (voice_mode == 4)
+		opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
+
+	return 0;
+}
+
+static void freq_to_fnum    (int freq, int *block, int *fnum)
+{
+	int f, octave;
+
+	/*
+	 * Converts the note frequency to block and fnum values for the FM chip
+	 */
+	/*
+	 * First try to compute the block -value (octave) where the note belongs
+	 */
+
+	f = freq;
+
+	octave = 5;
+
+	if (f == 0)
+		octave = 0;
+	else if (f < 261)
+	{
+		while (f < 261)
+		{
+			octave--;
+			f <<= 1;
+		}
+	}
+	else if (f > 493)
+	{
+		while (f > 493)
+		{
+			 octave++;
+			 f >>= 1;
+		}
+	}
+
+	if (octave > 7)
+		octave = 7;
+
+	*fnum = freq * (1 << (20 - octave)) / 49716;
+	*block = octave;
+}
+
+static void opl3_command    (int io_addr, unsigned int addr, unsigned int val)
+{
+	 int i;
+
+	/*
+	 * The original 2-OP synth requires a quite long delay after writing to a
+	 * register. The OPL-3 survives with just two INBs
+	 */
+
+	outb(((unsigned char) (addr & 0xff)), io_addr);
+
+	if (devc->model != 2)
+		udelay(10);
+	else
+		for (i = 0; i < 2; i++)
+			inb(io_addr);
+
+	outb(((unsigned char) (val & 0xff)), io_addr + 1);
+
+	if (devc->model != 2)
+		udelay(30);
+	else
+		for (i = 0; i < 2; i++)
+			inb(io_addr);
+}
+
+static void opl3_reset(int devno)
+{
+	int i;
+
+	for (i = 0; i < 18; i++)
+		devc->lv_map[i] = i;
+
+	for (i = 0; i < devc->nr_voice; i++)
+	{
+		opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+			KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff);
+
+		opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+			KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff);
+
+		if (pv_map[devc->lv_map[i]].voice_mode == 4)
+		{
+			opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+				KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff);
+
+			opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+				KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff);
+		}
+
+		opl3_kill_note(devno, i, 0, 64);
+	}
+
+	if (devc->model == 2)
+	{
+		devc->v_alloc->max_voice = devc->nr_voice = 18;
+
+		for (i = 0; i < 18; i++)
+			pv_map[i].voice_mode = 2;
+
+	}
+}
+
+static int opl3_open(int dev, int mode)
+{
+	int i;
+
+	if (devc->busy)
+		return -EBUSY;
+	devc->busy = 1;
+
+	devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
+	devc->v_alloc->timestamp = 0;
+
+	for (i = 0; i < 18; i++)
+	{
+		devc->v_alloc->map[i] = 0;
+		devc->v_alloc->alloc_times[i] = 0;
+	}
+
+	devc->cmask = 0x00;	/*
+				 * Just 2 OP mode
+				 */
+	if (devc->model == 2)
+		opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
+	return 0;
+}
+
+static void opl3_close(int dev)
+{
+	devc->busy = 0;
+	devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
+
+	devc->fm_info.nr_drums = 0;
+	devc->fm_info.perc_mode = 0;
+
+	opl3_reset(dev);
+}
+
+static void opl3_hw_control(int dev, unsigned char *event)
+{
+}
+
+static int opl3_load_patch(int dev, int format, const char __user *addr,
+		int count, int pmgr_flag)
+{
+	struct sbi_instrument ins;
+
+	if (count <sizeof(ins))
+	{
+		printk(KERN_WARNING "FM Error: Patch record too short\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&ins, addr, sizeof(ins)))
+		return -EFAULT;
+
+	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+	{
+		printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
+		return -EINVAL;
+	}
+	ins.key = format;
+
+	return store_instr(ins.channel, &ins);
+}
+
+static void opl3_panning(int dev, int voice, int value)
+{
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	devc->voc[voice].panning = value;
+}
+
+static void opl3_volume_method(int dev, int mode)
+{
+}
+
+#define SET_VIBRATO(cell) { \
+	tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+	if (pressure > 110) \
+		tmp |= 0x40;		/* Vibrato on */ \
+	opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
+
+static void opl3_aftertouch(int dev, int voice, int pressure)
+{
+	int tmp;
+	struct sbi_instrument *instr;
+	struct physical_voice_info *map;
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	map = &pv_map[devc->lv_map[voice]];
+
+	DEB(printk("Aftertouch %d\n", voice));
+
+	if (map->voice_mode == 0)
+		return;
+
+	/*
+	 * Adjust the amount of vibrato depending the pressure
+	 */
+
+	instr = devc->act_i[voice];
+
+	if (!instr)
+		instr = &devc->i_map[0];
+
+	if (devc->voc[voice].mode == 4)
+	{
+		int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+		switch (connection)
+		{
+			case 0:
+				SET_VIBRATO(4);
+				break;
+
+			case 1:
+				SET_VIBRATO(2);
+				SET_VIBRATO(4);
+				break;
+
+			case 2:
+				SET_VIBRATO(1);
+				SET_VIBRATO(4);
+				break;
+
+			case 3:
+				SET_VIBRATO(1);
+				SET_VIBRATO(3);
+				SET_VIBRATO(4);
+				break;
+
+		}
+		/*
+		 * Not implemented yet
+		 */
+	}
+	else
+	{
+		SET_VIBRATO(1);
+
+		if ((instr->operators[10] & 0x01))	/*
+							 * Additive synthesis
+							 */
+			SET_VIBRATO(2);
+	}
+}
+
+#undef SET_VIBRATO
+
+static void bend_pitch(int dev, int voice, int value)
+{
+	unsigned char data;
+	int block, fnum, freq;
+	struct physical_voice_info *map;
+
+	map = &pv_map[devc->lv_map[voice]];
+
+	if (map->voice_mode == 0)
+		return;
+
+	devc->voc[voice].bender = value;
+	if (!value)
+		return;
+	if (!(devc->voc[voice].keyon_byte & 0x20))
+		return;	/*
+			 * Not keyed on
+			 */
+
+	freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
+	devc->voc[voice].current_freq = freq;
+
+	freq_to_fnum(freq, &block, &fnum);
+
+	data = fnum & 0xff;	/*
+				 * Least significant bits of fnumber
+				 */
+	opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+	data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+	devc->voc[voice].keyon_byte = data;
+	opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+}
+
+static void opl3_controller (int dev, int voice, int ctrl_num, int value)
+{
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	switch (ctrl_num)
+	{
+		case CTRL_PITCH_BENDER:
+			bend_pitch(dev, voice, value);
+			break;
+
+		case CTRL_PITCH_BENDER_RANGE:
+			devc->voc[voice].bender_range = value;
+			break;
+
+		case CTL_MAIN_VOLUME:
+			devc->voc[voice].volume = value / 128;
+			break;
+
+		case CTL_PAN:
+			devc->voc[voice].panning = (value * 2) - 128;
+			break;
+	}
+}
+
+static void opl3_bender(int dev, int voice, int value)
+{
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	bend_pitch(dev, voice, value - 8192);
+}
+
+static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+	int i, p, best, first, avail, best_time = 0x7fffffff;
+	struct sbi_instrument *instr;
+	int is4op;
+	int instr_no;
+
+	if (chn < 0 || chn > 15)
+		instr_no = 0;
+	else
+		instr_no = devc->chn_info[chn].pgm_num;
+
+	instr = &devc->i_map[instr_no];
+	if (instr->channel < 0 ||	/* Instrument not loaded */
+		devc->nr_voice != 12)	/* Not in 4 OP mode */
+		is4op = 0;
+	else if (devc->nr_voice == 12)	/* 4 OP mode */
+		is4op = (instr->key == OPL3_PATCH);
+	else
+		is4op = 0;
+
+	if (is4op)
+	{
+		first = p = 0;
+		avail = 6;
+	}
+	else
+	{
+		if (devc->nr_voice == 12)	/* 4 OP mode. Use the '2 OP only' operators first */
+			first = p = 6;
+		else
+			first = p = 0;
+		avail = devc->nr_voice;
+	}
+
+	/*
+	 *    Now try to find a free voice
+	 */
+	best = first;
+
+	for (i = 0; i < avail; i++)
+	{
+		if (alloc->map[p] == 0)
+		{
+			return p;
+		}
+		if (alloc->alloc_times[p] < best_time)		/* Find oldest playing note */
+		{
+			best_time = alloc->alloc_times[p];
+			best = p;
+		}
+		p = (p + 1) % avail;
+	}
+
+	/*
+	 *    Insert some kind of priority mechanism here.
+	 */
+
+	if (best < 0)
+		best = 0;
+	if (best > devc->nr_voice)
+		best -= devc->nr_voice;
+
+	return best;	/* All devc->voc in use. Select the first one. */
+}
+
+static void opl3_setup_voice(int dev, int voice, int chn)
+{
+	struct channel_info *info;
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	if (chn < 0 || chn > 15)
+		return;
+
+	info = &synth_devs[dev]->chn_info[chn];
+
+	opl3_set_instr(dev, voice, info->pgm_num);
+
+	devc->voc[voice].bender = 0;
+	devc->voc[voice].bender_range = info->bender_range;
+	devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME];
+	devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
+}
+
+static struct synth_operations opl3_operations =
+{
+	.owner		= THIS_MODULE,
+	.id		= "OPL",
+	.info		= NULL,
+	.midi_dev	= 0,
+	.synth_type	= SYNTH_TYPE_FM,
+	.synth_subtype	= FM_TYPE_ADLIB,
+	.open		= opl3_open,
+	.close		= opl3_close,
+	.ioctl		= opl3_ioctl,
+	.kill_note	= opl3_kill_note,
+	.start_note	= opl3_start_note,
+	.set_instr	= opl3_set_instr,
+	.reset		= opl3_reset,
+	.hw_control	= opl3_hw_control,
+	.load_patch	= opl3_load_patch,
+	.aftertouch	= opl3_aftertouch,
+	.controller	= opl3_controller,
+	.panning	= opl3_panning,
+	.volume_method	= opl3_volume_method,
+	.bender		= opl3_bender,
+	.alloc_voice	= opl3_alloc_voice,
+	.setup_voice	= opl3_setup_voice
+};
+
+static int opl3_init(int ioaddr, struct module *owner)
+{
+	int i;
+	int me;
+
+	if (devc == NULL)
+	{
+		printk(KERN_ERR "opl3: Device control structure not initialized.\n");
+		return -1;
+	}
+
+	if ((me = sound_alloc_synthdev()) == -1)
+	{
+		printk(KERN_WARNING "opl3: Too many synthesizers\n");
+		return -1;
+	}
+
+	devc->nr_voice = 9;
+
+	devc->fm_info.device = 0;
+	devc->fm_info.synth_type = SYNTH_TYPE_FM;
+	devc->fm_info.synth_subtype = FM_TYPE_ADLIB;
+	devc->fm_info.perc_mode = 0;
+	devc->fm_info.nr_voices = 9;
+	devc->fm_info.nr_drums = 0;
+	devc->fm_info.instr_bank_size = SBFM_MAXINSTR;
+	devc->fm_info.capabilities = 0;
+	devc->left_io = ioaddr;
+	devc->right_io = ioaddr + 2;
+
+	if (detected_model <= 2)
+		devc->model = 1;
+	else
+	{
+		devc->model = 2;
+		if (detected_model == 4)
+			devc->is_opl4 = 1;
+	}
+
+	opl3_operations.info = &devc->fm_info;
+
+	synth_devs[me] = &opl3_operations;
+
+	if (owner)
+		synth_devs[me]->owner = owner;
+	
+	sequencer_init();
+	devc->v_alloc = &opl3_operations.alloc;
+	devc->chn_info = &opl3_operations.chn_info[0];
+
+	if (devc->model == 2)
+	{
+		if (devc->is_opl4) 
+			strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM");
+		else 
+			strcpy(devc->fm_info.name, "Yamaha OPL3");
+
+		devc->v_alloc->max_voice = devc->nr_voice = 18;
+		devc->fm_info.nr_drums = 0;
+		devc->fm_info.synth_subtype = FM_TYPE_OPL3;
+		devc->fm_info.capabilities |= SYNTH_CAP_OPL3;
+
+		for (i = 0; i < 18; i++)
+		{
+			if (pv_map[i].ioaddr == USE_LEFT)
+				pv_map[i].ioaddr = devc->left_io;
+			else
+				pv_map[i].ioaddr = devc->right_io;
+		}
+		opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE);
+		opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00);
+	}
+	else
+	{
+		strcpy(devc->fm_info.name, "Yamaha OPL2");
+		devc->v_alloc->max_voice = devc->nr_voice = 9;
+		devc->fm_info.nr_drums = 0;
+
+		for (i = 0; i < 18; i++)
+			pv_map[i].ioaddr = devc->left_io;
+	};
+	conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1);
+
+	for (i = 0; i < SBFM_MAXINSTR; i++)
+		devc->i_map[i].channel = -1;
+
+	return me;
+}
+
+static int me;
+
+static int io = -1;
+
+module_param(io, int, 0);
+
+static int __init init_opl3 (void)
+{
+	printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n");
+
+	if (io != -1)	/* User loading pure OPL3 module */
+	{
+		if (!opl3_detect(io))
+		{
+			return -ENODEV;
+		}
+
+		me = opl3_init(io, THIS_MODULE);
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_opl3(void)
+{
+	if (devc && io != -1)
+	{
+		if (devc->base) {
+			release_region(devc->base,4);
+			if (devc->is_opl4)
+				release_region(devc->base - 8, 2);
+		}
+		kfree(devc);
+		devc = NULL;
+		sound_unload_synthdev(me);
+	}
+}
+
+module_init(init_opl3);
+module_exit(cleanup_opl3);
+
+#ifndef MODULE
+static int __init setup_opl3(char *str)
+{
+        /* io  */
+	int ints[2];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+
+	return 1;
+}
+
+__setup("opl3=", setup_opl3);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/opl3_hw.h b/sound/oss/opl3_hw.h
new file mode 100644
index 00000000..8b11c893
--- /dev/null
+++ b/sound/oss/opl3_hw.h
@@ -0,0 +1,246 @@
+/*
+ *	opl3_hw.h	- Definitions of the OPL-3 registers
+ *
+ *
+ * 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 OPL-3 mode is switched on by writing 0x01, to the offset 5
+ *	of the right side.
+ *
+ *	Another special register at the right side is at offset 4. It contains
+ *	a bit mask defining which voices are used as 4 OP voices.
+ *
+ *	The percussive mode is implemented in the left side only.
+ *
+ *	With the above exceptions the both sides can be operated independently.
+ *	
+ *	A 4 OP voice can be created by setting the corresponding
+ *	bit at offset 4 of the right side.
+ *
+ *	For example setting the rightmost bit (0x01) changes the
+ *	first voice on the right side to the 4 OP mode. The fourth
+ *	voice is made inaccessible.
+ *
+ *	If a voice is set to the 2 OP mode, it works like 2 OP modes
+ *	of the original YM3812 (AdLib). In addition the voice can 
+ *	be connected the left, right or both stereo channels. It can
+ *	even be left unconnected. This works with 4 OP voices also.
+ *
+ *	The stereo connection bits are located in the FEEDBACK_CONNECTION
+ *	register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ *	in the second half of the voice.
+ */
+
+/*
+ *	Register numbers for the global registers
+ */
+
+#define TEST_REGISTER				0x01
+#define   ENABLE_WAVE_SELECT		0x20
+
+#define TIMER1_REGISTER				0x02
+#define TIMER2_REGISTER				0x03
+#define TIMER_CONTROL_REGISTER			0x04	/* Left side */
+#define   IRQ_RESET			0x80
+#define   TIMER1_MASK			0x40
+#define   TIMER2_MASK			0x20
+#define   TIMER1_START			0x01
+#define   TIMER2_START			0x02
+
+#define CONNECTION_SELECT_REGISTER		0x04	/* Right side */
+#define   RIGHT_4OP_0			0x01
+#define   RIGHT_4OP_1			0x02
+#define   RIGHT_4OP_2			0x04
+#define   LEFT_4OP_0			0x08
+#define   LEFT_4OP_1			0x10
+#define   LEFT_4OP_2			0x20
+
+#define OPL3_MODE_REGISTER			0x05	/* Right side */
+#define   OPL3_ENABLE			0x01
+#define   OPL4_ENABLE			0x02
+
+#define KBD_SPLIT_REGISTER			0x08	/* Left side */
+#define   COMPOSITE_SINE_WAVE_MODE	0x80		/* Don't use with OPL-3? */
+#define   KEYBOARD_SPLIT		0x40
+
+#define PERCOSSION_REGISTER			0xbd	/* Left side only */
+#define   TREMOLO_DEPTH			0x80
+#define   VIBRATO_DEPTH			0x40
+#define	  PERCOSSION_ENABLE		0x20
+#define   BASSDRUM_ON			0x10
+#define   SNAREDRUM_ON			0x08
+#define   TOMTOM_ON			0x04
+#define   CYMBAL_ON			0x02
+#define   HIHAT_ON			0x01
+
+/*
+ *	Offsets to the register banks for operators. To get the
+ *	register number just add the operator offset to the bank offset
+ *
+ *	AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+#define AM_VIB					0x20
+#define   TREMOLO_ON			0x80
+#define   VIBRATO_ON			0x40
+#define   SUSTAIN_ON			0x20
+#define   KSR				0x10 	/* Key scaling rate */
+#define   MULTIPLE_MASK		0x0f	/* Frequency multiplier */
+
+ /*
+  *	KSL/Total level (0x40 to 0x55)
+  */
+#define KSL_LEVEL				0x40
+#define   KSL_MASK			0xc0	/* Envelope scaling bits */
+#define   TOTAL_LEVEL_MASK		0x3f	/* Strength (volume) of OP */
+
+/*
+ *	Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY				0x60
+#define   ATTACK_MASK			0xf0
+#define   DECAY_MASK			0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE				0x80
+#define   SUSTAIN_MASK			0xf0
+#define   RELEASE_MASK			0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT			0xe0
+
+/*
+ *	Offsets to the register banks for voices. Just add to the
+ *	voice number to get the register number.
+ *
+ *	F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW				0xa0
+
+/*
+ *	F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK					0xb0
+#define	  KEYON_BIT				0x20
+#define	  BLOCKNUM_MASK				0x1c
+#define   FNUM_HIGH_MASK			0x03
+
+/*
+ *	Feedback / Connection (0xc0 to 0xc8)
+ *
+ *	These registers have two new bits when the OPL-3 mode
+ *	is selected. These bits controls connecting the voice
+ *	to the stereo channels. For 4 OP voices this bit is
+ *	defined in the second half of the voice (add 3 to the
+ *	register offset).
+ *
+ *	For 4 OP voices the connection bit is used in the
+ *	both halves (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION				0xc0
+#define   FEEDBACK_MASK				0x0e	/* Valid just for 1st OP of a voice */
+#define   CONNECTION_BIT			0x01
+/*
+ *	In the 4 OP mode there is four possible configurations how the
+ *	operators can be connected together (in 2 OP modes there is just
+ *	AM or FM). The 4 OP connection mode is defined by the rightmost
+ *	bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
+ *
+ *	First half	Second half	Mode
+ *
+ *					 +---+
+ *					 v   |
+ *	0		0		>+-1-+--2--3--4-->
+ *
+ *
+ *					
+ *					 +---+
+ *					 |   |
+ *	0		1		>+-1-+--2-+
+ *						  |->
+ *					>--3----4-+
+ *					
+ *					 +---+
+ *					 |   |
+ *	1		0		>+-1-+-----+
+ *						   |->
+ *					>--2--3--4-+
+ *
+ *					 +---+
+ *					 |   |
+ *	1		1		>+-1-+--+
+ *						|
+ *					>--2--3-+->
+ *						|
+ *					>--4----+
+ */
+#define   STEREO_BITS				0x30	/* OPL-3 only */
+#define     VOICE_TO_LEFT		0x10
+#define     VOICE_TO_RIGHT		0x20
+
+/*
+ * 	Definition table for the physical voices
+ */
+
+struct physical_voice_info {
+		unsigned char voice_num;
+		unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
+		unsigned short ioaddr; /* I/O port (left or right side) */
+		unsigned char op[4]; /* Operator offsets */
+	};
+
+/*
+ *	There is 18 possible 2 OP voices
+ *	(9 in the left and 9 in the right).
+ *	The first OP is the modulator and 2nd is the carrier.
+ *
+ *	The first three voices in the both sides may be connected
+ *	with another voice to a 4 OP voice. For example voice 0
+ *	can be connected with voice 3. The operators of voice 3 are
+ *	used as operators 3 and 4 of the new 4 OP voice.
+ *	In this case the 2 OP voice number 0 is the 'first half' and
+ *	voice 3 is the second.
+ */
+
+#define USE_LEFT	0
+#define USE_RIGHT	1
+
+static struct physical_voice_info pv_map[18] =
+{
+/*       No Mode Side		OP1	OP2	OP3   OP4	*/
+/*	---------------------------------------------------	*/
+	{ 0,  2, USE_LEFT,	{0x00,	0x03,	0x08, 0x0b}},
+	{ 1,  2, USE_LEFT,	{0x01,	0x04,	0x09, 0x0c}},
+	{ 2,  2, USE_LEFT,	{0x02,	0x05,	0x0a, 0x0d}},
+
+	{ 3,  2, USE_LEFT,	{0x08,	0x0b,	0x00, 0x00}},
+	{ 4,  2, USE_LEFT,	{0x09,	0x0c,	0x00, 0x00}},
+	{ 5,  2, USE_LEFT,	{0x0a,	0x0d,	0x00, 0x00}},
+
+	{ 6,  2, USE_LEFT,	{0x10,	0x13,	0x00, 0x00}}, /* Used by percussive voices */
+	{ 7,  2, USE_LEFT,	{0x11,	0x14,	0x00, 0x00}}, /* if the percussive mode */
+	{ 8,  2, USE_LEFT,	{0x12,	0x15,	0x00, 0x00}}, /* is selected */
+
+	{ 0,  2, USE_RIGHT,	{0x00,	0x03,	0x08, 0x0b}},
+	{ 1,  2, USE_RIGHT,	{0x01,	0x04,	0x09, 0x0c}},
+	{ 2,  2, USE_RIGHT,	{0x02,	0x05,	0x0a, 0x0d}},
+
+	{ 3,  2, USE_RIGHT,	{0x08,	0x0b,	0x00, 0x00}},
+	{ 4,  2, USE_RIGHT,	{0x09,	0x0c,	0x00, 0x00}},
+	{ 5,  2, USE_RIGHT,	{0x0a,	0x0d,	0x00, 0x00}},
+
+	{ 6,  2, USE_RIGHT,	{0x10,	0x13,	0x00, 0x00}},
+	{ 7,  2, USE_RIGHT,	{0x11,	0x14,	0x00, 0x00}},
+	{ 8,  2, USE_RIGHT,	{0x12,	0x15,	0x00, 0x00}}
+};
+/*
+ *	DMA buffer calls
+ */
diff --git a/sound/oss/os.h b/sound/oss/os.h
new file mode 100644
index 00000000..75ad0cd0
--- /dev/null
+++ b/sound/oss/os.h
@@ -0,0 +1,45 @@
+#define ALLOW_SELECT
+#undef NO_INLINE_ASM
+#define SHORT_BANNERS
+#define MANUAL_PNP
+#undef  DO_TIMINGS
+
+#include <linux/module.h>
+
+#ifdef __KERNEL__
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/param.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <asm/page.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+#include <linux/pci.h>
+#endif
+
+#include <linux/soundcard.h>
+
+#define FALSE	0
+#define TRUE	1
+
+extern int sound_alloc_dma(int chn, char *deviceID);
+extern int sound_open_dma(int chn, char *deviceID);
+extern void sound_free_dma(int chn);
+extern void sound_close_dma(int chn);
+
+extern void reprogram_timer(void);
+
+#define USE_AUTOINIT_DMA
+
+extern void *sound_mem_blocks[1024];
+extern int sound_nblocks;
+
+#undef PSEUDO_DMA_AUTOINIT
+#define ALLOW_BUFFER_MAPPING
+
+extern const struct file_operations oss_sound_fops;
diff --git a/sound/oss/pas2.h b/sound/oss/pas2.h
new file mode 100644
index 00000000..fa12c55f
--- /dev/null
+++ b/sound/oss/pas2.h
@@ -0,0 +1,17 @@
+
+/*	From pas_card.c	*/
+int pas_set_intr(int mask);
+int pas_remove_intr(int mask);
+unsigned char pas_read(int ioaddr);
+void pas_write(unsigned char data, int ioaddr);
+
+/*	From pas_audio.c */
+void pas_pcm_interrupt(unsigned char status, int cause);
+void pas_pcm_init(struct address_info *hw_config);
+
+/*	From pas_mixer.c */
+int pas_init_mixer(void);
+
+/*	From pas_midi.c */
+void pas_midi_init(void);
+void pas_midi_interrupt(void);
diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c
new file mode 100644
index 00000000..dabf8a87
--- /dev/null
+++ b/sound/oss/pas2_card.c
@@ -0,0 +1,455 @@
+/*
+ * sound/oss/pas2_card.c
+ *
+ * Detection routine for the Pro Audio Spectrum cards.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+#include "sb.h"
+
+static unsigned char dma_bits[] = {
+	4, 1, 2, 3, 0, 5, 6, 7
+};
+
+static unsigned char irq_bits[] = {
+	0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11
+};
+
+static unsigned char sb_irq_bits[] = {
+	0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 
+	0x00, 0x08, 0x28, 0x30, 0x38, 0, 0
+};
+
+static unsigned char sb_dma_bits[] = {
+	0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0
+};
+
+/*
+ * The Address Translation code is used to convert I/O register addresses to
+ * be relative to the given base -register
+ */
+
+int      	pas_translate_code = 0;
+static int      pas_intr_mask;
+static int      pas_irq;
+static int      pas_sb_base;
+DEFINE_SPINLOCK(pas_lock);
+#ifndef CONFIG_PAS_JOYSTICK
+static bool	joystick;
+#else
+static bool 	joystick = 1;
+#endif
+#ifdef SYMPHONY_PAS
+static bool 	symphony = 1;
+#else
+static bool 	symphony;
+#endif
+#ifdef BROKEN_BUS_CLOCK
+static bool	broken_bus_clock = 1;
+#else
+static bool	broken_bus_clock;
+#endif
+
+static struct address_info cfg;
+static struct address_info cfg2;
+
+char            pas_model = 0;
+static char    *pas_model_names[] = {
+	"", 
+	"Pro AudioSpectrum+", 
+	"CDPC", 
+	"Pro AudioSpectrum 16", 
+	"Pro AudioSpectrum 16D"
+};
+
+/*
+ * pas_read() and pas_write() are equivalents of inb and outb 
+ * These routines perform the I/O address translation required
+ * to support other than the default base address
+ */
+
+extern void     mix_write(unsigned char data, int ioaddr);
+
+unsigned char pas_read(int ioaddr)
+{
+	return inb(ioaddr + pas_translate_code);
+}
+
+void pas_write(unsigned char data, int ioaddr)
+{
+	outb((data), ioaddr + pas_translate_code);
+}
+
+/******************* Begin of the Interrupt Handler ********************/
+
+static irqreturn_t pasintr(int irq, void *dev_id)
+{
+	int             status;
+
+	status = pas_read(0x0B89);
+	pas_write(status, 0x0B89);	/* Clear interrupt */
+
+	if (status & 0x08)
+	{
+		  pas_pcm_interrupt(status, 1);
+		  status &= ~0x08;
+	}
+	if (status & 0x10)
+	{
+		  pas_midi_interrupt();
+		  status &= ~0x10;
+	}
+	return IRQ_HANDLED;
+}
+
+int pas_set_intr(int mask)
+{
+	if (!mask)
+		return 0;
+
+	pas_intr_mask |= mask;
+
+	pas_write(pas_intr_mask, 0x0B8B);
+	return 0;
+}
+
+int pas_remove_intr(int mask)
+{
+	if (!mask)
+		return 0;
+
+	pas_intr_mask &= ~mask;
+	pas_write(pas_intr_mask, 0x0B8B);
+
+	return 0;
+}
+
+/******************* End of the Interrupt handler **********************/
+
+/******************* Begin of the Initialization Code ******************/
+
+static int __init config_pas_hw(struct address_info *hw_config)
+{
+	char            ok = 1;
+	unsigned        int_ptrs;	/* scsi/sound interrupt pointers */
+
+	pas_irq = hw_config->irq;
+
+	pas_write(0x00, 0x0B8B);
+	pas_write(0x36, 0x138B);
+	pas_write(0x36, 0x1388);
+	pas_write(0, 0x1388);
+	pas_write(0x74, 0x138B);
+	pas_write(0x74, 0x1389);
+	pas_write(0, 0x1389);
+
+	pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A);
+	pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
+	pas_write(0x01 | 0x02 | 0x04 | 0x10	/*
+						 * |
+						 * 0x80
+						 */ , 0xB88);
+
+	pas_write(0x80 | (joystick ? 0x40 : 0), 0xF388);
+
+	if (pas_irq < 0 || pas_irq > 15)
+	{
+		printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+		hw_config->irq=-1;
+		ok = 0;
+	}
+	else
+	{
+		int_ptrs = pas_read(0xF38A);
+		int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq];
+		pas_write(int_ptrs, 0xF38A);
+		if (!irq_bits[pas_irq])
+		{
+			printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+			hw_config->irq=-1;
+			ok = 0;
+		}
+		else
+		{
+			if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) {
+				printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq);
+				hw_config->irq=-1;
+				ok = 0;
+			}
+		}
+	}
+
+	if (hw_config->dma < 0 || hw_config->dma > 7)
+	{
+		printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+		hw_config->dma=-1;
+		ok = 0;
+	}
+	else
+	{
+		pas_write(dma_bits[hw_config->dma], 0xF389);
+		if (!dma_bits[hw_config->dma])
+		{
+			printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+			hw_config->dma=-1;
+			ok = 0;
+		}
+		else
+		{
+			if (sound_alloc_dma(hw_config->dma, "PAS16"))
+			{
+				printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n");
+				hw_config->dma=-1;
+				ok = 0;
+			}
+		}
+	}
+
+	/*
+	 * This fixes the timing problems of the PAS due to the Symphony chipset
+	 * as per Media Vision.  Only define this if your PAS doesn't work correctly.
+	 */
+
+	if(symphony)
+	{
+		outb((0x05), 0xa8);
+		outb((0x60), 0xa9);
+	}
+
+	if(broken_bus_clock)
+		pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
+	else
+		/*
+		 * pas_write(0x01, 0x8388);
+		 */
+		pas_write(0x01 | 0x10 | 0x20, 0x8388);
+
+	pas_write(0x18, 0x838A);	/* ??? */
+	pas_write(0x20 | 0x01, 0x0B8A);		/* Mute off, filter = 17.897 kHz */
+	pas_write(8, 0xBF8A);
+
+	mix_write(0x80 | 5, 0x078B);
+	mix_write(5, 0x078B);
+
+	{
+		struct address_info *sb_config;
+
+		sb_config = &cfg2;
+		if (sb_config->io_base)
+		{
+			unsigned char   irq_dma;
+
+			/*
+			 * Turn on Sound Blaster compatibility
+			 * bit 1 = SB emulation
+			 * bit 0 = MPU401 emulation (CDPC only :-( )
+			 */
+			
+			pas_write(0x02, 0xF788);
+
+			/*
+			 * "Emulation address"
+			 */
+			
+			pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
+			pas_sb_base = sb_config->io_base;
+
+			if (!sb_dma_bits[sb_config->dma])
+				printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
+
+			if (!sb_irq_bits[sb_config->irq])
+				printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
+
+			irq_dma = sb_dma_bits[sb_config->dma] |
+				sb_irq_bits[sb_config->irq];
+
+			pas_write(irq_dma, 0xFB8A);
+		}
+		else
+			pas_write(0x00, 0xF788);
+	}
+
+	if (!ok)
+		printk(KERN_WARNING "PAS16: Driver not enabled\n");
+
+	return ok;
+}
+
+static int __init detect_pas_hw(struct address_info *hw_config)
+{
+	unsigned char   board_id, foo;
+
+	/*
+	 * WARNING: Setting an option like W:1 or so that disables warm boot reset
+	 * of the card will screw up this detect code something fierce. Adding code
+	 * to handle this means possibly interfering with other cards on the bus if
+	 * you have something on base port 0x388. SO be forewarned.
+	 */
+
+	outb((0xBC), 0x9A01);	/* Activate first board */
+	outb((hw_config->io_base >> 2), 0x9A01);	/* Set base address */
+	pas_translate_code = hw_config->io_base - 0x388;
+	pas_write(1, 0xBF88);	/* Select one wait states */
+
+	board_id = pas_read(0x0B8B);
+
+	if (board_id == 0xff)
+		return 0;
+
+	/*
+	 * We probably have a PAS-series board, now check for a PAS16-series board
+	 * by trying to change the board revision bits. PAS16-series hardware won't
+	 * let you do this - the bits are read-only.
+	 */
+
+	foo = board_id ^ 0xe0;
+
+	pas_write(foo, 0x0B8B);
+	foo = pas_read(0x0B8B);
+	pas_write(board_id, 0x0B8B);
+
+	if (board_id != foo)
+		return 0;
+
+	pas_model = pas_read(0xFF88);
+
+	return pas_model;
+}
+
+static void __init attach_pas_card(struct address_info *hw_config)
+{
+	pas_irq = hw_config->irq;
+
+	if (detect_pas_hw(hw_config))
+	{
+
+		if ((pas_model = pas_read(0xFF88)))
+		{
+			char            temp[100];
+
+			sprintf(temp,
+			    "%s rev %d", pas_model_names[(int) pas_model],
+				    pas_read(0x2789));
+			conf_printf(temp, hw_config);
+		}
+		if (config_pas_hw(hw_config))
+		{
+			pas_pcm_init(hw_config);
+			pas_midi_init();
+			pas_init_mixer();
+		}
+	}
+}
+
+static inline int __init probe_pas(struct address_info *hw_config)
+{
+	return detect_pas_hw(hw_config);
+}
+
+static void __exit unload_pas(struct address_info *hw_config)
+{
+	extern int pas_audiodev;
+	extern int pas2_mididev;
+
+	if (hw_config->dma>0)
+		sound_free_dma(hw_config->dma);
+	if (hw_config->irq>0)
+		free_irq(hw_config->irq, hw_config);
+
+	if(pas_audiodev!=-1)
+		sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev);
+	if(pas2_mididev!=-1)
+	        sound_unload_mididev(pas2_mididev);
+	if(pas_audiodev!=-1)
+		sound_unload_audiodev(pas_audiodev);
+}
+
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma16	= -1;	/* Set this for modules that need it */
+
+static int __initdata sb_io	= 0;
+static int __initdata sb_irq	= -1;
+static int __initdata sb_dma	= -1;
+static int __initdata sb_dma16	= -1;
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(dma, int, 0);
+module_param(dma16, int, 0);
+
+module_param(sb_io, int, 0);
+module_param(sb_irq, int, 0);
+module_param(sb_dma, int, 0);
+module_param(sb_dma16, int, 0);
+
+module_param(joystick, bool, 0);
+module_param(symphony, bool, 0);
+module_param(broken_bus_clock, bool, 0);
+
+MODULE_LICENSE("GPL");
+
+static int __init init_pas2(void)
+{
+	printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma16;
+
+	cfg2.io_base = sb_io;
+	cfg2.irq = sb_irq;
+	cfg2.dma = sb_dma;
+	cfg2.dma2 = sb_dma16;
+
+	if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
+		printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
+		return -EINVAL;
+	}
+
+	if (!probe_pas(&cfg))
+		return -ENODEV;
+	attach_pas_card(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_pas2(void)
+{
+	unload_pas(&cfg);
+}
+
+module_init(init_pas2);
+module_exit(cleanup_pas2);
+
+#ifndef MODULE
+static int __init setup_pas2(char *str)
+{
+	/* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */
+	int ints[9];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma16	= ints[4];
+
+	sb_io	= ints[5];
+	sb_irq	= ints[6];
+	sb_dma	= ints[7];
+	sb_dma16 = ints[8];
+
+	return 1;
+}
+
+__setup("pas2=", setup_pas2);
+#endif
diff --git a/sound/oss/pas2_midi.c b/sound/oss/pas2_midi.c
new file mode 100644
index 00000000..1122d10a
--- /dev/null
+++ b/sound/oss/pas2_midi.c
@@ -0,0 +1,262 @@
+/*
+ * sound/oss/pas2_midi.c
+ *
+ * The low level driver for the PAS Midi Interface.
+ */
+/*
+ * 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.
+ *
+ * Bartlomiej Zolnierkiewicz	: Added __init to pas_init_mixer()
+ */
+
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+
+extern spinlock_t pas_lock;
+
+static int      midi_busy, input_opened;
+static int      my_dev;
+
+int pas2_mididev=-1;
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+
+static void     (*midi_input_intr) (int dev, unsigned char data);
+
+static int pas_midi_open(int dev, int mode,
+	      void            (*input) (int dev, unsigned char data),
+	      void            (*output) (int dev)
+)
+{
+	int             err;
+	unsigned long   flags;
+	unsigned char   ctrl;
+
+
+	if (midi_busy)
+		return -EBUSY;
+
+	/*
+	 * Reset input and output FIFO pointers
+	 */
+	pas_write(0x20 | 0x40,
+		  0x178b);
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	if ((err = pas_set_intr(0x10)) < 0)
+	{
+		spin_unlock_irqrestore(&pas_lock, flags);
+		return err;
+	}
+	/*
+	 * Enable input available and output FIFO empty interrupts
+	 */
+
+	ctrl = 0;
+	input_opened = 0;
+	midi_input_intr = input;
+
+	if (mode == OPEN_READ || mode == OPEN_READWRITE)
+	{
+		ctrl |= 0x04;	/* Enable input */
+		input_opened = 1;
+	}
+	if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+	{
+		ctrl |= 0x08 | 0x10;	/* Enable output */
+	}
+	pas_write(ctrl, 0x178b);
+
+	/*
+	 * Acknowledge any pending interrupts
+	 */
+
+	pas_write(0xff, 0x1B88);
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+
+	midi_busy = 1;
+	qlen = qhead = qtail = 0;
+	return 0;
+}
+
+static void pas_midi_close(int dev)
+{
+
+	/*
+	 * Reset FIFO pointers, disable intrs
+	 */
+	pas_write(0x20 | 0x40, 0x178b);
+
+	pas_remove_intr(0x10);
+	midi_busy = 0;
+}
+
+static int dump_to_midi(unsigned char midi_byte)
+{
+	int fifo_space, x;
+
+	fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
+
+	/*
+	 * The MIDI FIFO space register and it's documentation is nonunderstandable.
+	 * There seem to be no way to differentiate between buffer full and buffer
+	 * empty situations. For this reason we don't never write the buffer
+	 * completely full. In this way we can assume that 0 (or is it 15)
+	 * means that the buffer is empty.
+	 */
+
+	if (fifo_space < 2 && fifo_space != 0)	/* Full (almost) */
+		return 0;	/* Ask upper layers to retry after some time */
+
+	pas_write(midi_byte, 0x178A);
+
+	return 1;
+}
+
+static int pas_midi_out(int dev, unsigned char midi_byte)
+{
+
+	unsigned long flags;
+
+	/*
+	 * Drain the local queue first
+	 */
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	while (qlen && dump_to_midi(tmp_queue[qhead]))
+	{
+		qlen--;
+		qhead++;
+	}
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+
+	/*
+	 *	Output the byte if the local queue is empty.
+	 */
+
+	if (!qlen)
+		if (dump_to_midi(midi_byte))
+			return 1;
+
+	/*
+	 *	Put to the local queue
+	 */
+
+	if (qlen >= 256)
+		return 0;	/* Local queue full */
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	tmp_queue[qtail] = midi_byte;
+	qlen++;
+	qtail++;
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+
+	return 1;
+}
+
+static int pas_midi_start_read(int dev)
+{
+	return 0;
+}
+
+static int pas_midi_end_read(int dev)
+{
+	return 0;
+}
+
+static void pas_midi_kick(int dev)
+{
+}
+
+static int pas_buffer_status(int dev)
+{
+	return qlen;
+}
+
+#define MIDI_SYNTH_NAME	"Pro Audio Spectrum Midi"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations pas_midi_operations =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
+	.converter	= &std_midi_synth,
+	.in_info	= {0},
+	.open		= pas_midi_open,
+	.close		= pas_midi_close,
+	.outputc	= pas_midi_out,
+	.start_read	= pas_midi_start_read,
+	.end_read	= pas_midi_end_read,
+	.kick		= pas_midi_kick,
+	.buffer_status	= pas_buffer_status,
+};
+
+void __init pas_midi_init(void)
+{
+	int dev = sound_alloc_mididev();
+
+	if (dev == -1)
+	{
+		printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
+		return;
+	}
+	std_midi_synth.midi_dev = my_dev = dev;
+	midi_devs[dev] = &pas_midi_operations;
+	pas2_mididev = dev;
+	sequencer_init();
+}
+
+void pas_midi_interrupt(void)
+{
+	unsigned char   stat;
+	int             i, incount;
+
+	stat = pas_read(0x1B88);
+
+	if (stat & 0x04)	/* Input data available */
+	{
+		incount = pas_read(0x1B89) & 0x0f;	/* Input FIFO size */
+		if (!incount)
+			incount = 16;
+
+		for (i = 0; i < incount; i++)
+			if (input_opened)
+			{
+				midi_input_intr(my_dev, pas_read(0x178A));
+			} else
+				pas_read(0x178A);	/* Flush */
+	}
+	if (stat & (0x08 | 0x10))
+	{
+		spin_lock(&pas_lock);/* called in irq context */
+
+		while (qlen && dump_to_midi(tmp_queue[qhead]))
+		{
+			qlen--;
+			qhead++;
+		}
+
+		spin_unlock(&pas_lock);
+	}
+	if (stat & 0x40)
+	{
+		printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
+	}
+	pas_write(stat, 0x1B88);	/* Acknowledge interrupts */
+}
diff --git a/sound/oss/pas2_mixer.c b/sound/oss/pas2_mixer.c
new file mode 100644
index 00000000..a0bcb85c
--- /dev/null
+++ b/sound/oss/pas2_mixer.c
@@ -0,0 +1,336 @@
+
+/*
+ * sound/oss/pas2_mixer.c
+ *
+ * Mixer routines for the Pro Audio Spectrum cards.
+ */
+
+/*
+ * 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)
+ * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer()
+ */
+#include <linux/init.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+
+#ifndef DEB
+#define DEB(what)		/* (what) */
+#endif
+
+extern int      pas_translate_code;
+extern char     pas_model;
+extern int     *pas_osp;
+extern int      pas_audiodev;
+
+static int      rec_devices = (SOUND_MASK_MIC);		/* Default recording source */
+static int      mode_control;
+
+#define POSSIBLE_RECORDING_DEVICES	(SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_ALTPCM)
+
+#define SUPPORTED_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
+					 SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV)
+
+static int     *levels;
+
+static int      default_levels[32] =
+{
+	0x3232,			/* Master Volume */
+	0x3232,			/* Bass */
+	0x3232,			/* Treble */
+	0x5050,			/* FM */
+	0x4b4b,			/* PCM */
+	0x3232,			/* PC Speaker */
+	0x4b4b,			/* Ext Line */
+	0x4b4b,			/* Mic */
+	0x4b4b,			/* CD */
+	0x6464,			/* Recording monitor */
+	0x4b4b,			/* SB PCM */
+	0x6464			/* Recording level */
+};
+
+void
+mix_write(unsigned char data, int ioaddr)
+{
+	/*
+	 * The Revision D cards have a problem with their MVA508 interface. The
+	 * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
+	 * MSBs out of the output byte and to do a 16-bit out to the mixer port -
+	 * 1. We need to do this because it isn't timing problem but chip access
+	 * sequence problem.
+	 */
+
+	if (pas_model == 4)
+	  {
+		  outw(data | (data << 8), (ioaddr + pas_translate_code) - 1);
+		  outb((0x80), 0);
+	} else
+		pas_write(data, ioaddr);
+}
+
+static int
+mixer_output(int right_vol, int left_vol, int div, int bits,
+	     int mixer)		/* Input or output mixer */
+{
+	int             left = left_vol * div / 100;
+	int             right = right_vol * div / 100;
+
+
+	if (bits & 0x10)
+	  {
+		  left |= mixer;
+		  right |= mixer;
+	  }
+	if (bits == 0x03 || bits == 0x04)
+	  {
+		  mix_write(0x80 | bits, 0x078B);
+		  mix_write(left, 0x078B);
+		  right_vol = left_vol;
+	} else
+	  {
+		  mix_write(0x80 | 0x20 | bits, 0x078B);
+		  mix_write(left, 0x078B);
+		  mix_write(0x80 | 0x40 | bits, 0x078B);
+		  mix_write(right, 0x078B);
+	  }
+
+	return (left_vol | (right_vol << 8));
+}
+
+static void
+set_mode(int new_mode)
+{
+	mix_write(0x80 | 0x05, 0x078B);
+	mix_write(new_mode, 0x078B);
+
+	mode_control = new_mode;
+}
+
+static int
+pas_mixer_set(int whichDev, unsigned int level)
+{
+	int             left, right, devmask, changed, i, mixer = 0;
+
+	DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
+
+	left = level & 0x7f;
+	right = (level & 0x7f00) >> 8;
+
+	if (whichDev < SOUND_MIXER_NRDEVICES) {
+		if ((1 << whichDev) & rec_devices)
+			mixer = 0x20;
+		else
+			mixer = 0x00;
+	}
+
+	switch (whichDev)
+	  {
+	  case SOUND_MIXER_VOLUME:	/* Master volume (0-63) */
+		  levels[whichDev] = mixer_output(right, left, 63, 0x01, 0);
+		  break;
+
+		  /*
+		   * Note! Bass and Treble are mono devices. Will use just the left
+		   * channel.
+		   */
+	  case SOUND_MIXER_BASS:	/* Bass (0-12) */
+		  levels[whichDev] = mixer_output(right, left, 12, 0x03, 0);
+		  break;
+	  case SOUND_MIXER_TREBLE:	/* Treble (0-12) */
+		  levels[whichDev] = mixer_output(right, left, 12, 0x04, 0);
+		  break;
+
+	  case SOUND_MIXER_SYNTH:	/* Internal synthesizer (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer);
+		  break;
+	  case SOUND_MIXER_PCM:	/* PAS PCM (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer);
+		  break;
+	  case SOUND_MIXER_ALTPCM:	/* SB PCM (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer);
+		  break;
+	  case SOUND_MIXER_SPEAKER:	/* PC speaker (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer);
+		  break;
+	  case SOUND_MIXER_LINE:	/* External line (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer);
+		  break;
+	  case SOUND_MIXER_CD:	/* CD (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer);
+		  break;
+	  case SOUND_MIXER_MIC:	/* External microphone (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer);
+		  break;
+	  case SOUND_MIXER_IMIX:	/* Recording monitor (0-31) (Output mixer only) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01,
+						  0x00);
+		  break;
+	  case SOUND_MIXER_RECLEV:	/* Recording level (0-15) */
+		  levels[whichDev] = mixer_output(right, left, 15, 0x02, 0);
+		  break;
+
+
+	  case SOUND_MIXER_RECSRC:
+		  devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+		  changed = devmask ^ rec_devices;
+		  rec_devices = devmask;
+
+		  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+			  if (changed & (1 << i))
+			    {
+				    pas_mixer_set(i, levels[i]);
+			    }
+		  return rec_devices;
+		  break;
+
+	  default:
+		  return -EINVAL;
+	  }
+
+	return (levels[whichDev]);
+}
+
+/*****/
+
+static void
+pas_mixer_reset(void)
+{
+	int             foo;
+
+	DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n"));
+
+	for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+		pas_mixer_set(foo, levels[foo]);
+
+	set_mode(0x04 | 0x01);
+}
+
+static int pas_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	int level,v ;
+	int __user *p = (int __user *)arg;
+
+	DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+	if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */
+		if (get_user(level, p))
+			return -EFAULT;
+		if (level == -1)  /* Return current settings */
+			level = (mode_control & 0x04);
+		else {
+			mode_control &= ~0x04;
+			if (level)
+				mode_control |= 0x04;
+			set_mode(mode_control);
+		}
+		level = !!level;
+		return put_user(level, p);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */
+		if (get_user(level, p))
+			return -EFAULT;
+		if (level == -1) { /* Return current settings */
+			if (!(mode_control & 0x03))
+				level = 0;
+			else
+				level = ((mode_control & 0x03) + 1) * 20;
+		} else {
+			int i = 0;
+			
+			level &= 0x7f;
+			if (level)
+				i = (level / 20) - 1;
+			mode_control &= ~0x03;
+			mode_control |= i & 0x03;
+			set_mode(mode_control);
+			if (i)
+				i = (i + 1) * 20;
+			level = i;
+		}
+		return put_user(level, p);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */
+		if (get_user(level, p))
+			return -EFAULT;
+		if (level == -1)	/* Return current settings */
+			level = !(pas_read(0x0B8A) & 0x20);
+		else {
+			if (level)
+				pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A);
+			else
+				pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A);
+
+			level = !(pas_read(0x0B8A) & 0x20);
+		}
+		return put_user(level, p);
+	}
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (get_user(v, p))
+			return -EFAULT;
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+			v = pas_mixer_set(cmd & 0xff, v);
+		} else {
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				v = rec_devices;
+				break;
+				
+			case SOUND_MIXER_STEREODEVS:
+				v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE);
+				break;
+				
+			case SOUND_MIXER_DEVMASK:
+				v = SUPPORTED_MIXER_DEVICES;
+				break;
+				
+			case SOUND_MIXER_RECMASK:
+				v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES;
+				break;
+				
+			case SOUND_MIXER_CAPS:
+				v = 0;	/* No special capabilities */
+				break;
+				
+			default:
+				v = levels[cmd & 0xff];
+				break;
+			}
+		}
+		return put_user(v, p);
+	}
+	return -EINVAL;
+}
+
+static struct mixer_operations pas_mixer_operations =
+{
+	.owner	= THIS_MODULE,
+	.id	= "PAS16",
+	.name	= "Pro Audio Spectrum 16",
+	.ioctl	= pas_mixer_ioctl
+};
+
+int __init
+pas_init_mixer(void)
+{
+	int             d;
+
+	levels = load_mixer_volumes("PAS16_1", default_levels, 1);
+
+	pas_mixer_reset();
+
+	if ((d = sound_alloc_mixerdev()) != -1)
+	  {
+		  audio_devs[pas_audiodev]->mixer_dev = d;
+		  mixer_devs[d] = &pas_mixer_operations;
+	  }
+	return 1;
+}
diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c
new file mode 100644
index 00000000..6f13ab4a
--- /dev/null
+++ b/sound/oss/pas2_pcm.c
@@ -0,0 +1,437 @@
+/*
+ * pas2_pcm.c Audio routines for PAS16
+ *
+ *
+ * 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)
+ * Alan Cox	   : Swatted a double allocation of device bug. Made a few
+ *		     more things module options.
+ * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
+ */
+
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/timex.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+
+#ifndef DEB
+#define DEB(WHAT)
+#endif
+
+#define PAS_PCM_INTRBITS (0x08)
+/*
+ * Sample buffer timer interrupt enable
+ */
+
+#define PCM_NON	0
+#define PCM_DAC	1
+#define PCM_ADC	2
+
+static unsigned long pcm_speed; 	/* sampling rate */
+static unsigned char pcm_channels = 1;	/* channels (1 or 2) */
+static unsigned char pcm_bits = 8;	/* bits/sample (8 or 16) */
+static unsigned char pcm_filter;	/* filter FLAG */
+static unsigned char pcm_mode = PCM_NON;
+static unsigned long pcm_count;
+static unsigned short pcm_bitsok = 8;	/* mask of OK bits */
+static int      pcm_busy;
+int             pas_audiodev = -1;
+static int      open_mode;
+
+extern spinlock_t pas_lock;
+
+static int pcm_set_speed(int arg)
+{
+	int foo, tmp;
+	unsigned long flags;
+
+	if (arg == 0)
+		return pcm_speed;
+
+	if (arg > 44100)
+		arg = 44100;
+	if (arg < 5000)
+		arg = 5000;
+
+	if (pcm_channels & 2)
+	{
+		foo = ((PIT_TICK_RATE / 2) + (arg / 2)) / arg;
+		arg = ((PIT_TICK_RATE / 2) + (foo / 2)) / foo;
+	}
+	else
+	{
+		foo = (PIT_TICK_RATE + (arg / 2)) / arg;
+		arg = (PIT_TICK_RATE + (foo / 2)) / foo;
+	}
+
+	pcm_speed = arg;
+
+	tmp = pas_read(0x0B8A);
+
+	/*
+	 * Set anti-aliasing filters according to sample rate. You really *NEED*
+	 * to enable this feature for all normal recording unless you want to
+	 * experiment with aliasing effects.
+	 * These filters apply to the selected "recording" source.
+	 * I (pfw) don't know the encoding of these 5 bits. The values shown
+	 * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
+	 *
+	 * I cleared bit 5 of these values, since that bit controls the master
+	 * mute flag. (Olav Wölfelschneider)
+	 *
+	 */
+#if !defined NO_AUTO_FILTER_SET
+	tmp &= 0xe0;
+	if (pcm_speed >= 2 * 17897)
+		tmp |= 0x01;
+	else if (pcm_speed >= 2 * 15909)
+		tmp |= 0x02;
+	else if (pcm_speed >= 2 * 11931)
+		tmp |= 0x09;
+	else if (pcm_speed >= 2 * 8948)
+		tmp |= 0x11;
+	else if (pcm_speed >= 2 * 5965)
+		tmp |= 0x19;
+	else if (pcm_speed >= 2 * 2982)
+		tmp |= 0x04;
+	pcm_filter = tmp;
+#endif
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
+	pas_write(0x00 | 0x30 | 0x04, 0x138B);
+	pas_write(foo & 0xff, 0x1388);
+	pas_write((foo >> 8) & 0xff, 0x1388);
+	pas_write(tmp, 0x0B8A);
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+
+	return pcm_speed;
+}
+
+static int pcm_set_channels(int arg)
+{
+
+	if ((arg != 1) && (arg != 2))
+		return pcm_channels;
+
+	if (arg != pcm_channels)
+	{
+		pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
+
+		pcm_channels = arg;
+		pcm_set_speed(pcm_speed);	/* The speed must be reinitialized */
+	}
+	return pcm_channels;
+}
+
+static int pcm_set_bits(int arg)
+{
+	if (arg == 0)
+		return pcm_bits;
+
+	if ((arg & pcm_bitsok) != arg)
+		return pcm_bits;
+
+	if (arg != pcm_bits)
+	{
+		pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
+
+		pcm_bits = arg;
+	}
+	return pcm_bits;
+}
+
+static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	int val, ret;
+	int __user *p = arg;
+
+	DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+	switch (cmd) 
+	{
+	case SOUND_PCM_WRITE_RATE:
+		if (get_user(val, p)) 
+			return -EFAULT;
+		ret = pcm_set_speed(val);
+		break;
+
+	case SOUND_PCM_READ_RATE:
+		ret = pcm_speed;
+		break;
+		
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, p)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val + 1) - 1;
+		break;
+
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (get_user(val, p)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val);
+		break;
+
+	case SOUND_PCM_READ_CHANNELS:
+		ret = pcm_channels;
+		break;
+
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, p))
+			return -EFAULT;
+		ret = pcm_set_bits(val);
+		break;
+		
+	case SOUND_PCM_READ_BITS:
+		ret = pcm_bits;
+		break;
+  
+	default:
+		return -EINVAL;
+	}
+	return put_user(ret, p);
+}
+
+static void pas_audio_reset(int dev)
+{
+	DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
+
+	pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);	/* Disable PCM */
+}
+
+static int pas_audio_open(int dev, int mode)
+{
+	int             err;
+	unsigned long   flags;
+
+	DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
+
+	spin_lock_irqsave(&pas_lock, flags);
+	if (pcm_busy)
+	{
+		spin_unlock_irqrestore(&pas_lock, flags);
+		return -EBUSY;
+	}
+	pcm_busy = 1;
+	spin_unlock_irqrestore(&pas_lock, flags);
+
+	if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
+		return err;
+
+
+	pcm_count = 0;
+	open_mode = mode;
+
+	return 0;
+}
+
+static void pas_audio_close(int dev)
+{
+	unsigned long   flags;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	pas_audio_reset(dev);
+	pas_remove_intr(PAS_PCM_INTRBITS);
+	pcm_mode = PCM_NON;
+
+	pcm_busy = 0;
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+
+static void pas_audio_output_block(int dev, unsigned long buf, int count,
+		       int intrflag)
+{
+	unsigned long   flags, cnt;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
+
+	cnt = count;
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		cnt >>= 1;
+
+	if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    cnt == pcm_count)
+		return;
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	pas_write(pas_read(0xF8A) & ~0x40,
+		  0xF8A);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+
+	if (count != pcm_count)
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+
+		pcm_count = count;
+	}
+	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
+#ifdef NO_TRIGGER
+	pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
+#endif
+
+	pcm_mode = PCM_DAC;
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+
+static void pas_audio_start_input(int dev, unsigned long buf, int count,
+		      int intrflag)
+{
+	unsigned long   flags;
+	int             cnt;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
+
+	cnt = count;
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		cnt >>= 1;
+
+	if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    cnt == pcm_count)
+		return;
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+
+	if (count != pcm_count)
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+
+		pcm_count = count;
+	}
+	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
+#ifdef NO_TRIGGER
+	pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
+#endif
+
+	pcm_mode = PCM_ADC;
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+
+#ifndef NO_TRIGGER
+static void pas_audio_trigger(int dev, int state)
+{
+	unsigned long   flags;
+
+	spin_lock_irqsave(&pas_lock, flags);
+	state &= open_mode;
+
+	if (state & PCM_ENABLE_OUTPUT)
+		pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
+	else if (state & PCM_ENABLE_INPUT)
+		pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
+	else
+		pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+#endif
+
+static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	pas_audio_reset(dev);
+	return 0;
+}
+
+static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	pas_audio_reset(dev);
+	return 0;
+}
+
+static struct audio_driver pas_audio_driver =
+{
+	.owner			= THIS_MODULE,
+	.open			= pas_audio_open,
+	.close			= pas_audio_close,
+	.output_block		= pas_audio_output_block,
+	.start_input		= pas_audio_start_input,
+	.ioctl			= pas_audio_ioctl,
+	.prepare_for_input	= pas_audio_prepare_for_input,
+	.prepare_for_output	= pas_audio_prepare_for_output,
+	.halt_io		= pas_audio_reset,
+	.trigger		= pas_audio_trigger
+};
+
+void __init pas_pcm_init(struct address_info *hw_config)
+{
+	DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
+
+	pcm_bitsok = 8;
+	if (pas_read(0xEF8B) & 0x08)
+		pcm_bitsok |= 16;
+
+	pcm_set_speed(DSP_DEFAULT_SPEED);
+
+	if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+					"Pro Audio Spectrum",
+					&pas_audio_driver,
+					sizeof(struct audio_driver),
+					DMA_AUTOMODE,
+					AFMT_U8 | AFMT_S16_LE,
+					NULL,
+					hw_config->dma,
+					hw_config->dma)) < 0)
+		printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
+}
+
+void pas_pcm_interrupt(unsigned char status, int cause)
+{
+	if (cause == 1)
+	{
+		/*
+		 * Halt the PCM first. Otherwise we don't have time to start a new
+		 * block before the PCM chip proceeds to the next sample
+		 */
+
+		if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
+			pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
+
+		switch (pcm_mode)
+		{
+			case PCM_DAC:
+				DMAbuf_outputintr(pas_audiodev, 1);
+				break;
+
+			case PCM_ADC:
+				DMAbuf_inputintr(pas_audiodev);
+				break;
+
+			default:
+				printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
+		}
+	}
+}
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
new file mode 100644
index 00000000..0f32a561
--- /dev/null
+++ b/sound/oss/pss.c
@@ -0,0 +1,1268 @@
+/*
+ * sound/oss/pss.c
+ *
+ * The low level driver for the Personal Sound System (ECHO ESC614).
+ *
+ *
+ * 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)
+ * Alan Cox		modularisation, clean up.
+ *
+ * 98-02-21: Vladimir Michl <vladimir.michl@upol.cz>
+ *          Added mixer device for Beethoven ADSP-16 (master volume,
+ *	    bass, treble, synth), only for speakers.
+ *          Fixed bug in pss_write (exchange parameters)
+ *          Fixed config port of SB
+ *          Requested two regions for PSS (PSS mixer, PSS config)
+ *          Modified pss_download_boot
+ *          To probe_pss_mss added test for initialize AD1848
+ * 98-05-28: Vladimir Michl <vladimir.michl@upol.cz>
+ *          Fixed computation of mixer volumes
+ * 04-05-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
+ *          Added code that allows the user to enable his cdrom and/or 
+ *          joystick through the module parameters pss_cdrom_port and 
+ *          pss_enable_joystick.  pss_cdrom_port takes a port address as its
+ *          argument.  pss_enable_joystick takes either a 0 or a non-0 as its
+ *          argument.
+ * 04-06-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
+ *          Separated some code into new functions for easier reuse.  
+ *          Cleaned up and streamlined new code.  Added code to allow a user 
+ *          to only use this driver for enabling non-sound components 
+ *          through the new module parameter pss_no_sound (flag).  Added 
+ *          code that would allow a user to decide whether the driver should 
+ *          reset the configured hardware settings for the PSS board through 
+ *          the module parameter pss_keep_settings (flag).   This flag will 
+ *          allow a user to free up resources in use by this card if needbe, 
+ *          furthermore it allows him to use this driver to just enable the 
+ *          emulations and then be unloaded as it is no longer needed.  Both 
+ *          new settings are only available to this driver if compiled as a 
+ *          module.  The default settings of all new parameters are set to 
+ *          load the driver as it did in previous versions.
+ * 04-07-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
+ *          Added module parameter pss_firmware to allow the user to tell 
+ *          the driver where the firmware file is located.  The default 
+ *          setting is the previous hardcoded setting "/etc/sound/pss_synth".
+ * 00-03-03: Christoph Hellwig <chhellwig@infradead.org>
+ *	    Adapted to module_init/module_exit
+ * 11-10-2000: Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *	    Added __init to probe_pss(), attach_pss() and probe_pss_mpu()
+ * 02-Jan-2001: Chris Rankin
+ *          Specify that this module owns the coprocessor
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+#include "sound_firmware.h"
+
+#include "ad1848.h"
+#include "mpu401.h"
+
+/*
+ * PSS registers.
+ */
+#define REG(x)	(devc->base+x)
+#define	PSS_DATA	0
+#define	PSS_STATUS	2
+#define PSS_CONTROL	2
+#define	PSS_ID		4
+#define	PSS_IRQACK	4
+#define	PSS_PIO		0x1a
+
+/*
+ * Config registers
+ */
+#define CONF_PSS	0x10
+#define CONF_WSS	0x12
+#define CONF_SB		0x14
+#define CONF_CDROM	0x16
+#define CONF_MIDI	0x18
+
+/*
+ * Status bits.
+ */
+#define PSS_FLAG3     0x0800
+#define PSS_FLAG2     0x0400
+#define PSS_FLAG1     0x1000
+#define PSS_FLAG0     0x0800
+#define PSS_WRITE_EMPTY  0x8000
+#define PSS_READ_FULL    0x4000
+
+/*
+ * WSS registers
+ */
+#define WSS_INDEX 4
+#define WSS_DATA 5
+
+/*
+ * WSS status bits
+ */
+#define WSS_INITIALIZING 0x80
+#define WSS_AUTOCALIBRATION 0x20
+
+#define NO_WSS_MIXER	-1
+
+#include "coproc.h"
+
+#include "pss_boot.h"
+
+/* If compiled into kernel, it enable or disable pss mixer */
+#ifdef CONFIG_PSS_MIXER
+static bool pss_mixer = 1;
+#else
+static bool pss_mixer;
+#endif
+
+
+typedef struct pss_mixerdata {
+	unsigned int volume_l;
+	unsigned int volume_r;
+	unsigned int bass;
+	unsigned int treble;
+	unsigned int synth;
+} pss_mixerdata;
+
+typedef struct pss_confdata {
+	int             base;
+	int             irq;
+	int             dma;
+	int            *osp;
+	pss_mixerdata   mixer;
+	int             ad_mixer_dev;
+} pss_confdata;
+  
+static pss_confdata pss_data;
+static pss_confdata *devc = &pss_data;
+static DEFINE_SPINLOCK(lock);
+
+static int      pss_initialized;
+static int      nonstandard_microcode;
+static int	pss_cdrom_port = -1;	/* Parameter for the PSS cdrom port */
+static bool	pss_enable_joystick;    /* Parameter for enabling the joystick */
+static coproc_operations pss_coproc_operations;
+
+static void pss_write(pss_confdata *devc, int data)
+{
+	unsigned long i, limit;
+
+	limit = jiffies + HZ/10;	/* The timeout is 0.1 seconds */
+	/*
+	 * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+	 * called while interrupts are disabled. This means that the timer is
+	 * disabled also. However the timeout situation is a abnormal condition.
+	 * Normally the DSP should be ready to accept commands after just couple of
+	 * loops.
+	 */
+
+	for (i = 0; i < 5000000 && time_before(jiffies, limit); i++)
+ 	{
+ 		if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY)
+ 		{
+ 			outw(data, REG(PSS_DATA));
+ 			return;
+ 		}
+ 	}
+ 	printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data);
+}
+
+static int __init probe_pss(struct address_info *hw_config)
+{
+	unsigned short id;
+	int irq, dma;
+
+	devc->base = hw_config->io_base;
+	irq = devc->irq = hw_config->irq;
+	dma = devc->dma = hw_config->dma;
+	devc->osp = hw_config->osp;
+
+	if (devc->base != 0x220 && devc->base != 0x240)
+		if (devc->base != 0x230 && devc->base != 0x250)		/* Some cards use these */
+			return 0;
+
+	if (!request_region(devc->base, 0x10, "PSS mixer, SB emulation")) {
+		printk(KERN_ERR "PSS: I/O port conflict\n");
+		return 0;
+	}
+	id = inw(REG(PSS_ID));
+	if ((id >> 8) != 'E') {
+		printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n",  devc->base,  id); 
+		release_region(devc->base, 0x10);
+		return 0;
+	}
+	if (!request_region(devc->base + 0x10, 0x9, "PSS config")) {
+		printk(KERN_ERR "PSS: I/O port conflict\n");
+		release_region(devc->base, 0x10);
+		return 0;
+	}
+	return 1;
+}
+
+static int set_irq(pss_confdata * devc, int dev, int irq)
+{
+	static unsigned short irq_bits[16] =
+	{
+		0x0000, 0x0000, 0x0000, 0x0008,
+		0x0000, 0x0010, 0x0000, 0x0018,
+		0x0000, 0x0020, 0x0028, 0x0030,
+		0x0038, 0x0000, 0x0000, 0x0000
+	};
+
+	unsigned short  tmp, bits;
+
+	if (irq < 0 || irq > 15)
+		return 0;
+
+	tmp = inw(REG(dev)) & ~0x38;	/* Load confreg, mask IRQ bits out */
+
+	if ((bits = irq_bits[irq]) == 0 && irq != 0)
+	{
+		printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq);
+		return 0;
+	}
+	outw(tmp | bits, REG(dev));
+	return 1;
+}
+
+static void set_io_base(pss_confdata * devc, int dev, int base)
+{
+	unsigned short  tmp = inw(REG(dev)) & 0x003f;
+	unsigned short  bits = (base & 0x0ffc) << 4;
+
+	outw(bits | tmp, REG(dev));
+}
+
+static int set_dma(pss_confdata * devc, int dev, int dma)
+{
+	static unsigned short dma_bits[8] =
+	{
+		0x0001, 0x0002, 0x0000, 0x0003,
+		0x0000, 0x0005, 0x0006, 0x0007
+	};
+
+	unsigned short  tmp, bits;
+
+	if (dma < 0 || dma > 7)
+		return 0;
+
+	tmp = inw(REG(dev)) & ~0x07;	/* Load confreg, mask DMA bits out */
+
+	if ((bits = dma_bits[dma]) == 0 && dma != 4)
+	{
+		  printk(KERN_ERR "PSS: Invalid DMA %d\n", dma);
+		  return 0;
+	}
+	outw(tmp | bits, REG(dev));
+	return 1;
+}
+
+static int pss_reset_dsp(pss_confdata * devc)
+{
+	unsigned long   i, limit = jiffies + HZ/10;
+
+	outw(0x2000, REG(PSS_CONTROL));
+	for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
+		inw(REG(PSS_CONTROL));
+	outw(0x0000, REG(PSS_CONTROL));
+	return 1;
+}
+
+static int pss_put_dspword(pss_confdata * devc, unsigned short word)
+{
+	int i, val;
+
+	for (i = 0; i < 327680; i++)
+	{
+		val = inw(REG(PSS_STATUS));
+		if (val & PSS_WRITE_EMPTY)
+		{
+			outw(word, REG(PSS_DATA));
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int pss_get_dspword(pss_confdata * devc, unsigned short *word)
+{
+	int i, val;
+
+	for (i = 0; i < 327680; i++)
+	{
+		val = inw(REG(PSS_STATUS));
+		if (val & PSS_READ_FULL)
+		{
+			*word = inw(REG(PSS_DATA));
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags)
+{
+	int i, val, count;
+	unsigned long limit;
+
+	if (flags & CPF_FIRST)
+	{
+/*_____ Warn DSP software that a boot is coming */
+		outw(0x00fe, REG(PSS_DATA));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && time_before(jiffies, limit); i++)
+			if (inw(REG(PSS_DATA)) == 0x5500)
+				break;
+
+		outw(*block++, REG(PSS_DATA));
+		pss_reset_dsp(devc);
+	}
+	count = 1;
+	while ((flags&CPF_LAST) || count<size )
+	{
+		int j;
+
+		for (j = 0; j < 327670; j++)
+		{
+/*_____ Wait for BG to appear */
+			if (inw(REG(PSS_STATUS)) & PSS_FLAG3)
+				break;
+		}
+
+		if (j == 327670)
+		{
+			/* It's ok we timed out when the file was empty */
+			if (count >= size && flags & CPF_LAST)
+				break;
+			else
+			{
+				printk("\n");
+				printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size);
+				return 0;
+			}
+		}
+/*_____ Send the next byte */
+		if (count >= size) 
+		{
+			/* If not data in block send 0xffff */
+			outw (0xffff, REG (PSS_DATA));
+		}
+		else
+		{
+			/*_____ Send the next byte */
+			outw (*block++, REG (PSS_DATA));
+		};
+		count++;
+	}
+
+	if (flags & CPF_LAST)
+	{
+/*_____ Why */
+		outw(0, REG(PSS_DATA));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
+			val = inw(REG(PSS_STATUS));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
+		{
+			val = inw(REG(PSS_STATUS));
+			if (val & 0x4000)
+				break;
+		}
+
+		/* now read the version */
+		for (i = 0; i < 32000; i++)
+		{
+			val = inw(REG(PSS_STATUS));
+			if (val & PSS_READ_FULL)
+				break;
+		}
+		if (i == 32000)
+			return 0;
+
+		val = inw(REG(PSS_DATA));
+		/* printk( "<PSS: microcode version %d.%d loaded>",  val/16,  val % 16); */
+	}
+	return 1;
+}
+
+/* Mixer */
+static void set_master_volume(pss_confdata *devc, int left, int right)
+{
+	static unsigned char log_scale[101] =  {
+		0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee,
+		0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
+		0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
+		0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+		0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
+		0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
+		0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+		0xfe, 0xfe, 0xff, 0xff, 0xff
+	};
+	pss_write(devc, 0x0010);
+	pss_write(devc, log_scale[left] | 0x0000);
+	pss_write(devc, 0x0010);
+	pss_write(devc, log_scale[right] | 0x0100);
+}
+
+static void set_synth_volume(pss_confdata *devc, int volume)
+{
+	int vol = ((0x8000*volume)/100L);
+	pss_write(devc, 0x0080);
+	pss_write(devc, vol);
+	pss_write(devc, 0x0081);
+	pss_write(devc, vol);
+}
+
+static void set_bass(pss_confdata *devc, int level)
+{
+	int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0;
+	pss_write(devc, 0x0010);
+	pss_write(devc, vol | 0x0200);
+};
+
+static void set_treble(pss_confdata *devc, int level)
+{	
+	int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0;
+	pss_write(devc, 0x0010);
+	pss_write(devc, vol | 0x0300);
+};
+
+static void pss_mixer_reset(pss_confdata *devc)
+{
+	set_master_volume(devc, 33, 33);
+	set_bass(devc, 50);
+	set_treble(devc, 50);
+	set_synth_volume(devc, 30);
+	pss_write (devc, 0x0010);
+	pss_write (devc, 0x0800 | 0xce);	/* Stereo */
+	
+	if(pss_mixer)
+	{
+		devc->mixer.volume_l = devc->mixer.volume_r = 33;
+		devc->mixer.bass = 50;
+		devc->mixer.treble = 50;
+		devc->mixer.synth = 30;
+	}
+}
+
+static int set_volume_mono(unsigned __user *p, unsigned int *aleft)
+{
+	unsigned int left, volume;
+	if (get_user(volume, p))
+		return -EFAULT;
+	
+	left = volume & 0xff;
+	if (left > 100)
+		left = 100;
+	*aleft = left;
+	return 0;
+}
+
+static int set_volume_stereo(unsigned __user *p,
+			     unsigned int *aleft,
+			     unsigned int *aright)
+{
+	unsigned int left, right, volume;
+	if (get_user(volume, p))
+		return -EFAULT;
+
+	left = volume & 0xff;
+	if (left > 100)
+		left = 100;
+	right = (volume >> 8) & 0xff;
+	if (right > 100)
+		right = 100;
+	*aleft = left;
+	*aright = right;
+	return 0;
+}
+
+static int ret_vol_mono(int left)
+{
+	return ((left << 8) | left);
+}
+
+static int ret_vol_stereo(int left, int right)
+{
+	return ((right << 8) | left);
+}
+
+static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, void __user *arg)
+{
+	if (devc->ad_mixer_dev != NO_WSS_MIXER) 
+		return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg);
+	else 
+		return -EINVAL;
+}
+
+static int pss_mixer_ioctl (int dev, unsigned int cmd, void __user *arg)
+{
+	pss_confdata *devc = mixer_devs[dev]->devc;
+	int cmdf = cmd & 0xff;
+	
+	if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) &&
+		(cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) &&
+		(cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) &&
+		(cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) &&
+		(cmdf != SOUND_MIXER_RECSRC)) 
+	{
+		return call_ad_mixer(devc, cmd, arg);
+	}
+	
+	if (((cmd >> 8) & 0xff) != 'M')	
+		return -EINVAL;
+		
+	if (_SIOC_DIR (cmd) & _SIOC_WRITE)
+	{
+		switch (cmdf)	
+		{
+			case SOUND_MIXER_RECSRC:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				else
+				{
+					int v;
+					if (get_user(v, (int __user *)arg))
+						return -EFAULT;
+					if (v != 0)
+						return -EINVAL;
+					return 0;
+				}
+			case SOUND_MIXER_VOLUME:
+				if (set_volume_stereo(arg,
+					&devc->mixer.volume_l,
+					&devc->mixer.volume_r))
+					return -EFAULT;
+				set_master_volume(devc, devc->mixer.volume_l,
+					devc->mixer.volume_r);
+				return ret_vol_stereo(devc->mixer.volume_l,
+					devc->mixer.volume_r);
+		  
+			case SOUND_MIXER_BASS:
+				if (set_volume_mono(arg, &devc->mixer.bass))
+					return -EFAULT;
+				set_bass(devc, devc->mixer.bass);
+				return ret_vol_mono(devc->mixer.bass);
+		  
+			case SOUND_MIXER_TREBLE:
+				if (set_volume_mono(arg, &devc->mixer.treble))
+					return -EFAULT;
+				set_treble(devc, devc->mixer.treble);
+				return ret_vol_mono(devc->mixer.treble);
+		  
+			case SOUND_MIXER_SYNTH:
+				if (set_volume_mono(arg, &devc->mixer.synth))
+					return -EFAULT;
+				set_synth_volume(devc, devc->mixer.synth);
+				return ret_vol_mono(devc->mixer.synth);
+		  
+			default:
+				return -EINVAL;
+		}
+	}
+	else			
+	{
+		int val, and_mask = 0, or_mask = 0;
+		/*
+		 * Return parameters
+		 */
+		switch (cmdf)
+		{
+			case SOUND_MIXER_DEVMASK:
+				if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
+					break;
+				and_mask = ~0;
+				or_mask = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH;
+				break;
+		  
+			case SOUND_MIXER_STEREODEVS:
+				if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
+					break;
+				and_mask = ~0;
+				or_mask = SOUND_MASK_VOLUME;
+				break;
+		  
+			case SOUND_MIXER_RECMASK:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				break;
+
+			case SOUND_MIXER_CAPS:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				or_mask = SOUND_CAP_EXCL_INPUT;
+				break;
+
+			case SOUND_MIXER_RECSRC:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				break;
+
+			case SOUND_MIXER_VOLUME:
+				or_mask =  ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r);
+				break;
+			  
+			case SOUND_MIXER_BASS:
+				or_mask =  ret_vol_mono(devc->mixer.bass);
+				break;
+			  
+			case SOUND_MIXER_TREBLE:
+				or_mask = ret_vol_mono(devc->mixer.treble);
+				break;
+			  
+			case SOUND_MIXER_SYNTH:
+				or_mask = ret_vol_mono(devc->mixer.synth);
+				break;
+			default:
+				return -EINVAL;
+		}
+		if (get_user(val, (int __user *)arg))
+			return -EFAULT;
+		val &= and_mask;
+		val |= or_mask;
+		if (put_user(val, (int __user *)arg))
+			return -EFAULT;
+		return val;
+	}
+}
+
+static struct mixer_operations pss_mixer_operations =
+{
+	.owner	= THIS_MODULE,
+	.id	= "SOUNDPORT",
+	.name	= "PSS-AD1848",
+	.ioctl	= pss_mixer_ioctl
+};
+
+static void disable_all_emulations(void)
+{
+	outw(0x0000, REG(CONF_PSS));	/* 0x0400 enables joystick */
+	outw(0x0000, REG(CONF_WSS));
+	outw(0x0000, REG(CONF_SB));
+	outw(0x0000, REG(CONF_MIDI));
+	outw(0x0000, REG(CONF_CDROM));
+}
+
+static void configure_nonsound_components(void)
+{
+	/* Configure Joystick port */
+
+	if(pss_enable_joystick)
+	{
+		outw(0x0400, REG(CONF_PSS));	/* 0x0400 enables joystick */
+		printk(KERN_INFO "PSS: joystick enabled.\n");
+	}
+	else
+	{
+		printk(KERN_INFO "PSS: joystick port not enabled.\n");
+	}
+
+	/* Configure CDROM port */
+
+	if (pss_cdrom_port == -1) {	/* If cdrom port enablation wasn't requested */
+		printk(KERN_INFO "PSS: CDROM port not enabled.\n");
+	} else if (!request_region(pss_cdrom_port, 2, "PSS CDROM")) {
+		pss_cdrom_port = -1;
+		printk(KERN_ERR "PSS: CDROM I/O port conflict.\n");
+	} else {
+		set_io_base(devc, CONF_CDROM, pss_cdrom_port);
+		printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port);
+	}
+}
+
+static int __init attach_pss(struct address_info *hw_config)
+{
+	unsigned short  id;
+	char tmp[100];
+
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->dma = hw_config->dma;
+	devc->osp = hw_config->osp;
+	devc->ad_mixer_dev = NO_WSS_MIXER;
+
+	if (!probe_pss(hw_config))
+		return 0;
+
+	id = inw(REG(PSS_ID)) & 0x00ff;
+
+	/*
+	 * Disable all emulations. Will be enabled later (if required).
+	 */
+	 
+	disable_all_emulations();
+
+#ifdef YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
+	if (sound_alloc_dma(hw_config->dma, "PSS"))
+	{
+		printk("pss.c: Can't allocate DMA channel.\n");
+		release_region(hw_config->io_base, 0x10);
+		release_region(hw_config->io_base+0x10, 0x9);
+		return 0;
+	}
+	if (!set_irq(devc, CONF_PSS, devc->irq))
+	{
+		printk("PSS: IRQ allocation error.\n");
+		release_region(hw_config->io_base, 0x10);
+		release_region(hw_config->io_base+0x10, 0x9);
+		return 0;
+	}
+	if (!set_dma(devc, CONF_PSS, devc->dma))
+	{
+		printk(KERN_ERR "PSS: DMA allocation error\n");
+		release_region(hw_config->io_base, 0x10);
+		release_region(hw_config->io_base+0x10, 0x9);
+		return 0;
+	}
+#endif
+
+	configure_nonsound_components();
+	pss_initialized = 1;
+	sprintf(tmp, "ECHO-PSS  Rev. %d", id);
+	conf_printf(tmp, hw_config);
+	return 1;
+}
+
+static int __init probe_pss_mpu(struct address_info *hw_config)
+{
+	struct resource *ports;
+	int timeout;
+
+	if (!pss_initialized)
+		return 0;
+
+	ports = request_region(hw_config->io_base, 2, "mpu401");
+
+	if (!ports) {
+		printk(KERN_ERR "PSS: MPU I/O port conflict\n");
+		return 0;
+	}
+	set_io_base(devc, CONF_MIDI, hw_config->io_base);
+	if (!set_irq(devc, CONF_MIDI, hw_config->irq)) {
+		printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n");
+		goto fail;
+	}
+	if (!pss_synthLen) {
+		printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n");
+		goto fail;
+	}
+	if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) {
+		printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+		goto fail;
+	}
+
+	/*
+	 * Finally wait until the DSP algorithm has initialized itself and
+	 * deactivates receive interrupt.
+	 */
+
+	for (timeout = 900000; timeout > 0; timeout--)
+	{
+		if ((inb(hw_config->io_base + 1) & 0x80) == 0)	/* Input data avail */
+			inb(hw_config->io_base);	/* Discard it */
+		else
+			break;	/* No more input */
+	}
+
+	if (!probe_mpu401(hw_config, ports))
+		goto fail;
+
+	attach_mpu401(hw_config, THIS_MODULE);	/* Slot 1 */
+	if (hw_config->slots[1] != -1)	/* The MPU driver installed itself */
+		midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
+	return 1;
+fail:
+	release_region(hw_config->io_base, 2);
+	return 0;
+}
+
+static int pss_coproc_open(void *dev_info, int sub_device)
+{
+	switch (sub_device)
+	{
+		case COPR_MIDI:
+			if (pss_synthLen == 0)
+			{
+				printk(KERN_ERR "PSS: MIDI synth microcode not available.\n");
+				return -EIO;
+			}
+			if (nonstandard_microcode)
+				if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+			{
+				printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+				return -EIO;
+			}
+			nonstandard_microcode = 0;
+			break;
+
+		default:
+			break;
+	}
+	return 0;
+}
+
+static void pss_coproc_close(void *dev_info, int sub_device)
+{
+	return;
+}
+
+static void pss_coproc_reset(void *dev_info)
+{
+	if (pss_synthLen)
+		if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+		{
+			printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+		}
+	nonstandard_microcode = 0;
+}
+
+static int download_boot_block(void *dev_info, copr_buffer * buf)
+{
+	if (buf->len <= 0 || buf->len > sizeof(buf->data))
+		return -EINVAL;
+
+	if (!pss_download_boot(devc, buf->data, buf->len, buf->flags))
+	{
+		printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n");
+		return -EIO;
+	}
+	nonstandard_microcode = 1;	/* The MIDI microcode has been overwritten */
+	return 0;
+}
+
+static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
+{
+	copr_buffer *buf;
+	copr_msg *mbuf;
+	copr_debug_buf dbuf;
+	unsigned short tmp;
+	unsigned long flags;
+	unsigned short *data;
+	int i, err;
+	/* printk( "PSS coproc ioctl %x %x %d\n",  cmd,  arg,  local); */
+	
+	switch (cmd) 
+	{
+		case SNDCTL_COPR_RESET:
+			pss_coproc_reset(dev_info);
+			return 0;
+
+		case SNDCTL_COPR_LOAD:
+			buf = vmalloc(sizeof(copr_buffer));
+			if (buf == NULL)
+				return -ENOSPC;
+			if (copy_from_user(buf, arg, sizeof(copr_buffer))) {
+				vfree(buf);
+				return -EFAULT;
+			}
+			err = download_boot_block(dev_info, buf);
+			vfree(buf);
+			return err;
+		
+		case SNDCTL_COPR_SENDMSG:
+			mbuf = vmalloc(sizeof(copr_msg));
+			if (mbuf == NULL)
+				return -ENOSPC;
+			if (copy_from_user(mbuf, arg, sizeof(copr_msg))) {
+				vfree(mbuf);
+				return -EFAULT;
+			}
+			data = (unsigned short *)(mbuf->data);
+			spin_lock_irqsave(&lock, flags);
+			for (i = 0; i < mbuf->len; i++) {
+				if (!pss_put_dspword(devc, *data++)) {
+					spin_unlock_irqrestore(&lock,flags);
+					mbuf->len = i;	/* feed back number of WORDs sent */
+					err = copy_to_user(arg, mbuf, sizeof(copr_msg));
+					vfree(mbuf);
+					return err ? -EFAULT : -EIO;
+				}
+			}
+			spin_unlock_irqrestore(&lock,flags);
+			vfree(mbuf);
+			return 0;
+
+		case SNDCTL_COPR_RCVMSG:
+			err = 0;
+			mbuf = vmalloc(sizeof(copr_msg));
+			if (mbuf == NULL)
+				return -ENOSPC;
+			data = (unsigned short *)mbuf->data;
+			spin_lock_irqsave(&lock, flags);
+			for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) {
+				mbuf->len = i;	/* feed back number of WORDs read */
+				if (!pss_get_dspword(devc, data++)) {
+					if (i == 0)
+						err = -EIO;
+					break;
+				}
+			}
+			spin_unlock_irqrestore(&lock,flags);
+			if (copy_to_user(arg, mbuf, sizeof(copr_msg)))
+				err = -EFAULT;
+			vfree(mbuf);
+			return err;
+		
+		case SNDCTL_COPR_RDATA:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			spin_lock_irqsave(&lock, flags);
+			if (!pss_put_dspword(devc, 0x00d0)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			if (!pss_get_dspword(devc, &tmp)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			dbuf.parm1 = tmp;
+			spin_unlock_irqrestore(&lock,flags);
+			if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
+				return -EFAULT;
+			return 0;
+		
+		case SNDCTL_COPR_WDATA:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			spin_lock_irqsave(&lock, flags);
+			if (!pss_put_dspword(devc, 0x00d1)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			tmp = (unsigned int)dbuf.parm2 & 0xffff;
+			if (!pss_put_dspword(devc, tmp)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			spin_unlock_irqrestore(&lock,flags);
+			return 0;
+		
+		case SNDCTL_COPR_WCODE:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			spin_lock_irqsave(&lock, flags);
+			if (!pss_put_dspword(devc, 0x00d3)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			tmp = (unsigned int)dbuf.parm2 & 0x00ff;
+			if (!pss_put_dspword(devc, tmp)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff;
+			if (!pss_put_dspword(devc, tmp)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			spin_unlock_irqrestore(&lock,flags);
+			return 0;
+		
+		case SNDCTL_COPR_RCODE:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			spin_lock_irqsave(&lock, flags);
+			if (!pss_put_dspword(devc, 0x00d2)) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			dbuf.parm1 = tmp << 8;
+			if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */
+				spin_unlock_irqrestore(&lock,flags);
+				return -EIO;
+			}
+			dbuf.parm1 |= tmp & 0x00ff;
+			spin_unlock_irqrestore(&lock,flags);
+			if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
+				return -EFAULT;
+			return 0;
+
+		default:
+			return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+static coproc_operations pss_coproc_operations =
+{
+	"ADSP-2115",
+	THIS_MODULE,
+	pss_coproc_open,
+	pss_coproc_close,
+	pss_coproc_ioctl,
+	pss_coproc_reset,
+	&pss_data
+};
+
+static int __init probe_pss_mss(struct address_info *hw_config)
+{
+	volatile int timeout;
+	struct resource *ports;
+	int        my_mix = -999;	/* gcc shut up */
+
+	if (!pss_initialized)
+		return 0;
+
+	if (!request_region(hw_config->io_base, 4, "WSS config")) {
+		printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
+		return 0;
+	}
+	ports = request_region(hw_config->io_base + 4, 4, "ad1848");
+	if (!ports) {
+		printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
+		release_region(hw_config->io_base, 4);
+		return 0;
+	}
+	set_io_base(devc, CONF_WSS, hw_config->io_base);
+	if (!set_irq(devc, CONF_WSS, hw_config->irq)) {
+		printk("PSS: WSS IRQ allocation error.\n");
+		goto fail;
+	}
+	if (!set_dma(devc, CONF_WSS, hw_config->dma)) {
+		printk(KERN_ERR "PSS: WSS DMA allocation error\n");
+		goto fail;
+	}
+	/*
+	 * For some reason the card returns 0xff in the WSS status register
+	 * immediately after boot. Probably MIDI+SB emulation algorithm
+	 * downloaded to the ADSP2115 spends some time initializing the card.
+	 * Let's try to wait until it finishes this task.
+	 */
+	for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) &
+	  WSS_INITIALIZING); timeout++)
+		;
+
+	outb((0x0b), hw_config->io_base + WSS_INDEX);	/* Required by some cards */
+
+	for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) &&
+	  (timeout < 100000); timeout++)
+		;
+
+	if (!probe_ms_sound(hw_config, ports))
+		goto fail;
+
+	devc->ad_mixer_dev = NO_WSS_MIXER;
+	if (pss_mixer) 
+	{
+		if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION,
+			"PSS-SPEAKERS and AD1848 (through MSS audio codec)",
+			&pss_mixer_operations,
+			sizeof (struct mixer_operations),
+			devc)) < 0) 
+		{
+			printk(KERN_ERR "Could not install PSS mixer\n");
+			goto fail;
+		}
+	}
+	pss_mixer_reset(devc);
+	attach_ms_sound(hw_config, ports, THIS_MODULE);	/* Slot 0 */
+
+	if (hw_config->slots[0] != -1)
+	{
+		/* The MSS driver installed itself */
+		audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations;
+		if (pss_mixer && (num_mixers == (my_mix + 2)))
+		{
+			/* The MSS mixer installed */
+			devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev;
+		}
+	}
+	return 1;
+fail:
+	release_region(hw_config->io_base + 4, 4);
+	release_region(hw_config->io_base, 4);
+	return 0;
+}
+
+static inline void __exit unload_pss(struct address_info *hw_config)
+{
+	release_region(hw_config->io_base, 0x10);
+	release_region(hw_config->io_base+0x10, 0x9);
+}
+
+static inline void __exit unload_pss_mpu(struct address_info *hw_config)
+{
+	unload_mpu401(hw_config);
+}
+
+static inline void __exit unload_pss_mss(struct address_info *hw_config)
+{
+	unload_ms_sound(hw_config);
+}
+
+
+static struct address_info cfg;
+static struct address_info cfg2;
+static struct address_info cfg_mpu;
+
+static int pss_io __initdata	= -1;
+static int mss_io __initdata	= -1;
+static int mss_irq __initdata	= -1;
+static int mss_dma __initdata	= -1;
+static int mpu_io __initdata	= -1;
+static int mpu_irq __initdata	= -1;
+static bool pss_no_sound = 0;	/* Just configure non-sound components */
+static bool pss_keep_settings  = 1;	/* Keep hardware settings at module exit */
+static char *pss_firmware = "/etc/sound/pss_synth";
+
+module_param(pss_io, int, 0);
+MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)");
+module_param(mss_io, int, 0);
+MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)");
+module_param(mss_irq, int, 0);
+MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)");
+module_param(mss_dma, int, 0);
+MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)");
+module_param(mpu_io, int, 0);
+MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)");
+module_param(mpu_irq, int, 0);
+MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)");
+module_param(pss_cdrom_port, int, 0);
+MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)");
+module_param(pss_enable_joystick, bool, 0);
+MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)");
+module_param(pss_no_sound, bool, 0);
+MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)");
+module_param(pss_keep_settings, bool, 0);
+MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)");
+module_param(pss_firmware, charp, 0);
+MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)");
+module_param(pss_mixer, bool, 0);
+MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards.");
+MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl");
+MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).");
+MODULE_LICENSE("GPL");
+
+
+static int fw_load = 0;
+static int pssmpu = 0, pssmss = 0;
+
+/*
+ *    Load a PSS sound card module
+ */
+
+static int __init init_pss(void)
+{
+
+	if(pss_no_sound)		/* If configuring only nonsound components */
+	{
+		cfg.io_base = pss_io;
+		if(!probe_pss(&cfg))
+			return -ENODEV;
+		printk(KERN_INFO "ECHO-PSS  Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff);
+		printk(KERN_INFO "PSS: loading in no sound mode.\n");
+		disable_all_emulations();
+		configure_nonsound_components();
+		release_region(pss_io, 0x10);
+		release_region(pss_io + 0x10, 0x9);
+		return 0;
+	}
+
+	cfg.io_base = pss_io;
+
+	cfg2.io_base = mss_io;
+	cfg2.irq = mss_irq;
+	cfg2.dma = mss_dma;
+
+	cfg_mpu.io_base = mpu_io;
+	cfg_mpu.irq = mpu_irq;
+
+	if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) {
+		printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n");
+		return -EINVAL;
+	}
+
+	if (!pss_synth) {
+		fw_load = 1;
+		pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth);
+	}
+	if (!attach_pss(&cfg))
+		return -ENODEV;
+	/*
+	 *    Attach stuff
+	 */
+	if (probe_pss_mpu(&cfg_mpu))
+		pssmpu = 1;
+
+	if (probe_pss_mss(&cfg2))
+		pssmss = 1;
+
+	return 0;
+}
+
+static void __exit cleanup_pss(void)
+{
+	if(!pss_no_sound)
+	{
+		if(fw_load && pss_synth)
+			vfree(pss_synth);
+		if(pssmss)
+			unload_pss_mss(&cfg2);
+		if(pssmpu)
+			unload_pss_mpu(&cfg_mpu);
+		unload_pss(&cfg);
+	} else if (pss_cdrom_port != -1)
+		release_region(pss_cdrom_port, 2);
+
+	if(!pss_keep_settings)	/* Keep hardware settings if asked */
+	{
+		disable_all_emulations();
+		printk(KERN_INFO "Resetting PSS sound card configurations.\n");
+	}
+}
+
+module_init(init_pss);
+module_exit(cleanup_pss);
+
+#ifndef MODULE
+static int __init setup_pss(char *str)
+{
+	/* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */
+	int ints[7];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	pss_io	= ints[1];
+	mss_io	= ints[2];
+	mss_irq	= ints[3];
+	mss_dma	= ints[4];
+	mpu_io	= ints[5];
+	mpu_irq	= ints[6];
+
+	return 1;
+}
+
+__setup("pss=", setup_pss);
+#endif
diff --git a/sound/oss/sb.h b/sound/oss/sb.h
new file mode 100644
index 00000000..77e8891c
--- /dev/null
+++ b/sound/oss/sb.h
@@ -0,0 +1,185 @@
+#define DSP_RESET	(devc->base + 0x6)
+#define DSP_READ	(devc->base + 0xA)
+#define DSP_WRITE	(devc->base + 0xC)
+#define DSP_COMMAND	(devc->base + 0xC)
+#define DSP_STATUS	(devc->base + 0xC)
+#define DSP_DATA_AVAIL	(devc->base + 0xE)
+#define DSP_DATA_AVL16	(devc->base + 0xF)
+#define MIXER_ADDR	(devc->base + 0x4)
+#define MIXER_DATA	(devc->base + 0x5)
+#define OPL3_LEFT	(devc->base + 0x0)
+#define OPL3_RIGHT	(devc->base + 0x2)
+#define OPL3_BOTH	(devc->base + 0x8)
+/* DSP Commands */
+
+#define DSP_CMD_SPKON		0xD1
+#define DSP_CMD_SPKOFF		0xD3
+#define DSP_CMD_DMAON		0xD0
+#define DSP_CMD_DMAOFF		0xD4
+
+#define IMODE_NONE		0
+#define IMODE_OUTPUT		PCM_ENABLE_OUTPUT
+#define IMODE_INPUT		PCM_ENABLE_INPUT
+#define IMODE_INIT		3
+#define IMODE_MIDI		4
+
+#define NORMAL_MIDI	0
+#define UART_MIDI	1
+
+
+/*
+ * Device models
+ */
+#define MDL_NONE	0
+#define MDL_SB1		1	/* SB1.0 or 1.5 */
+#define MDL_SB2		2	/* SB2.0 */
+#define MDL_SB201	3	/* SB2.01 */
+#define MDL_SBPRO	4	/* SB Pro */
+#define MDL_SB16	5	/* SB16/32/AWE */
+#define MDL_SBPNP 	6	/* SB16/32/AWE PnP */
+#define MDL_JAZZ	10	/* Media Vision Jazz16 */
+#define MDL_SMW		11	/* Logitech SoundMan Wave (Jazz16) */
+#define MDL_ESS		12	/* ESS ES688 and ES1688 */
+#define MDL_AZTECH	13	/* Aztech Sound Galaxy family */
+#define MDL_ES1868MIDI	14	/* MIDI port of ESS1868 */
+#define MDL_AEDSP	15	/* Audio Excel DSP 16 */
+#define MDL_ESSPCI	16	/* ESS PCI card */
+#define MDL_YMPCI	17	/* Yamaha PCI sb in emulation */
+
+#define SUBMDL_ALS007	42	/* ALS-007 differs from SB16 only in mixer */
+				/* register assignment */
+#define SUBMDL_ALS100	43	/* ALS-100 allows sampling rates of up */
+				/* to 48kHz */
+				
+/*
+ * Config flags
+ */
+#define SB_NO_MIDI	0x00000001
+#define SB_NO_MIXER	0x00000002
+#define SB_NO_AUDIO	0x00000004
+#define SB_NO_RECORDING	0x00000008 /* No audio recording */
+#define SB_MIDI_ONLY	(SB_NO_AUDIO|SB_NO_MIXER)
+#define SB_PCI_IRQ	0x00000010 /* PCI shared IRQ */
+
+struct mixer_def {
+	unsigned int regno: 8;
+	unsigned int bitoffs:4;
+	unsigned int nbits:4;
+};
+
+typedef struct mixer_def mixer_tab[32][2];
+typedef struct mixer_def mixer_ent;
+
+struct sb_module_options
+{
+	int  esstype;	/* ESS chip type */
+	int  acer;	/* Do acer notebook init? */
+	int  sm_games;	/* Logitech soundman games? */
+};
+
+typedef struct sb_devc {
+	   int dev;
+
+	/* Hardware parameters */
+	   int *osp;
+	   int minor, major;
+	   int type;
+	   int model, submodel;
+	   int caps;
+#	define SBCAP_STEREO	0x00000001
+#	define SBCAP_16BITS	0x00000002
+
+	/* Hardware resources */
+	   int base;
+	   int irq;
+	   int dma8, dma16;
+	   
+	   int pcibase;		/* For ESS Maestro etc */
+
+	/* State variables */
+ 	   int opened;
+	/* new audio fields for full duplex support */
+	   int fullduplex;
+	   int duplex;
+	   int speed, bits, channels;
+	   volatile int irq_ok;
+	   volatile int intr_active, irq_mode;
+	/* duplicate audio fields for full duplex support */
+	   volatile int intr_active_16, irq_mode_16;
+
+	/* Mixer fields */
+	   int *levels;
+	   mixer_tab *iomap;
+	   size_t iomap_sz; /* number or records in the iomap table */
+	   int mixer_caps, recmask, outmask, supported_devices;
+	   int supported_rec_devices, supported_out_devices;
+	   int my_mixerdev;
+	   int sbmixnum;
+
+	/* Audio fields */
+	   unsigned long trg_buf;
+	   int      trigger_bits;
+	   int      trg_bytes;
+	   int      trg_intrflag;
+	   int      trg_restart;
+	/* duplicate audio fields for full duplex support */
+	   unsigned long trg_buf_16;
+	   int      trigger_bits_16;
+	   int      trg_bytes_16;
+	   int      trg_intrflag_16;
+	   int      trg_restart_16;
+
+	   unsigned char tconst;
+	
+	/* MIDI fields */
+	   int my_mididev;
+	   int input_opened;
+	   int midi_broken;
+	   void (*midi_input_intr) (int dev, unsigned char data);
+	   void *midi_irq_cookie;		/* IRQ cookie for the midi */
+
+	   spinlock_t lock;
+
+	   struct sb_module_options sbmo;	/* Module options */
+
+	} sb_devc;
+	
+/*
+ *	PCI card types
+ */
+
+#define	SB_PCI_ESSMAESTRO	1	/* ESS Maestro Legacy */
+#define	SB_PCI_YAMAHA		2	/* Yamaha Legacy */
+
+/* 
+ *	Functions
+ */
+ 
+int sb_dsp_command (sb_devc *devc, unsigned char val);
+int sb_dsp_get_byte(sb_devc * devc);
+int sb_dsp_reset (sb_devc *devc);
+void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
+unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
+int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo);
+int sb_dsp_init (struct address_info *hw_config, struct module *owner);
+void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
+int sb_mixer_init(sb_devc *devc, struct module *owner);
+void sb_mixer_unload(sb_devc *devc);
+void sb_mixer_set_stereo (sb_devc *devc, int mode);
+void smw_mixer_init(sb_devc *devc);
+void sb_dsp_midi_init (sb_devc *devc, struct module *owner);
+void sb_audio_init (sb_devc *devc, char *name, struct module *owner);
+void sb_midi_interrupt (sb_devc *devc);
+void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
+int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right);
+
+int sb_audio_open(int dev, int mode);
+void sb_audio_close(int dev);
+
+/*	From sb_common.c */
+void sb_dsp_disable_midi(int port);
+int probe_sbmpu (struct address_info *hw_config, struct module *owner);
+void unload_sbmpu (struct address_info *hw_config);
+
+void unload_sb16(struct address_info *hw_info);
+void unload_sb16midi(struct address_info *hw_info);
diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c
new file mode 100644
index 00000000..733b014e
--- /dev/null
+++ b/sound/oss/sb_audio.c
@@ -0,0 +1,1098 @@
+/*
+ * sound/oss/sb_audio.c
+ *
+ * Audio routines for Sound Blaster compatible cards.
+ *
+ *
+ * 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.
+ *
+ * Changes
+ *	Alan Cox	:	Formatting and clean ups
+ *
+ * Status
+ *	Mostly working. Weird uart bug causing irq storms
+ *
+ * Daniel J. Rodriksson: Changes to make sb16 work full duplex.
+ *                       Maybe other 16 bit cards in this code could behave
+ *                       the same.
+ * Chris Rankin:         Use spinlocks instead of CLI/STI
+ */
+
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+
+#include "sb_mixer.h"
+#include "sb.h"
+
+#include "sb_ess.h"
+
+int sb_audio_open(int dev, int mode)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+	{
+		  printk(KERN_ERR "Sound Blaster: incomplete initialization.\n");
+		  return -ENXIO;
+	}
+	if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
+	{
+		if (mode == OPEN_READ)
+			return -EPERM;
+	}
+	spin_lock_irqsave(&devc->lock, flags);
+	if (devc->opened)
+	{
+		  spin_unlock_irqrestore(&devc->lock, flags);
+		  return -EBUSY;
+	}
+	if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
+	{
+		if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit"))
+		{
+		  	spin_unlock_irqrestore(&devc->lock, flags);
+			return -EBUSY;
+		}
+	}
+	devc->opened = mode;
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->irq_mode = IMODE_NONE;
+	devc->irq_mode_16 = IMODE_NONE;
+	devc->fullduplex = devc->duplex &&
+		((mode & OPEN_READ) && (mode & OPEN_WRITE));
+	sb_dsp_reset(devc);
+
+	/* At first glance this check isn't enough, some ESS chips might not 
+	 * have a RECLEV. However if they don't common_mixer_set will refuse 
+	 * cause devc->iomap has no register mapping for RECLEV
+	 */
+	if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV);
+
+	/* The ALS007 seems to require that the DSP be removed from the output */
+	/* in order for recording to be activated properly.  This is done by   */
+	/* setting the appropriate bits of the output control register 4ch to  */
+	/* zero.  This code assumes that the output control registers are not  */
+	/* used anywhere else and therefore the DSP bits are *always* ON for   */
+	/* output and OFF for sampling.                                        */
+
+	if (devc->submodel == SUBMDL_ALS007) 
+	{
+		if (mode & OPEN_READ) 
+			sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
+				sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9);
+		else
+			sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
+				sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
+	}
+	return 0;
+}
+
+void sb_audio_close(int dev)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	/* fix things if mmap turned off fullduplex */
+	if(devc->duplex
+	   && !devc->fullduplex
+	   && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE))
+	{
+		struct dma_buffparms *dmap_temp;
+		dmap_temp = audio_devs[dev]->dmap_out;
+		audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in;
+		audio_devs[dev]->dmap_in = dmap_temp;
+	}
+	audio_devs[dev]->dmap_out->dma = devc->dma8;
+	audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ?
+		devc->dma16 : devc->dma8;
+
+	if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
+		sound_close_dma(devc->dma16);
+
+	/* For ALS007, turn DSP output back on if closing the device for read */
+	
+	if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ)) 
+	{
+		sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
+			sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
+	}
+	devc->opened = 0;
+}
+
+static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes,
+		    int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
+	{
+		devc->trg_buf = buf;
+		devc->trg_bytes = nr_bytes;
+		devc->trg_intrflag = intrflag;
+		devc->irq_mode = IMODE_OUTPUT;
+	}
+	else
+	{
+		devc->trg_buf_16 = buf;
+		devc->trg_bytes_16 = nr_bytes;
+		devc->trg_intrflag_16 = intrflag;
+		devc->irq_mode_16 = IMODE_OUTPUT;
+	}
+}
+
+static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
+	{
+		devc->trg_buf = buf;
+		devc->trg_bytes = count;
+		devc->trg_intrflag = intrflag;
+		devc->irq_mode = IMODE_INPUT;
+	}
+	else
+	{
+		devc->trg_buf_16 = buf;
+		devc->trg_bytes_16 = count;
+		devc->trg_intrflag_16 = intrflag;
+		devc->irq_mode_16 = IMODE_INPUT;
+	}
+}
+
+/*
+ * SB1.x compatible routines 
+ */
+
+static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_OUTPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x14))		/* 8 bit DAC using DMA */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+	}
+	else
+		printk(KERN_WARNING "Sound Blaster:  unable to start DAC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->intr_active = 1;
+}
+
+static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	/*
+	 * Start a DMA input to the buffer pointed by dmaqtail
+	 */
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_INPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x24))		/* 8 bit ADC using DMA */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+	}
+	else
+		printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->intr_active = 1;
+}
+
+static void sb1_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	bits &= devc->irq_mode;
+
+	if (!bits)
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	else
+	{
+		switch (devc->irq_mode)
+		{
+			case IMODE_INPUT:
+				sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+				break;
+
+			case IMODE_OUTPUT:
+				sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+				break;
+		}
+	}
+	devc->trigger_bits = bits;
+}
+
+static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKOFF);
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKON);
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sb1_audio_set_speed(int dev, int speed)
+{
+	int max_speed = 23000;
+	sb_devc *devc = audio_devs[dev]->devc;
+	int tmp;
+
+	if (devc->opened & OPEN_READ)
+		max_speed = 13000;
+
+	if (speed > 0)
+	{
+		if (speed < 4000)
+			speed = 4000;
+
+		if (speed > max_speed)
+			speed = max_speed;
+
+		devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+		tmp = 256 - devc->tconst;
+		speed = (1000000 + tmp / 2) / tmp;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+static short sb1_audio_set_channels(int dev, short channels)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	return devc->channels = 1;
+}
+
+static unsigned int sb1_audio_set_bits(int dev, unsigned int bits)
+{
+	sb_devc        *devc = audio_devs[dev]->devc;
+	return devc->bits = 8;
+}
+
+static void sb1_audio_halt_xfer(int dev)
+{
+	unsigned long flags;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	sb_dsp_reset(devc);
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+/*
+ * SB 2.0 and SB 2.01 compatible routines
+ */
+
+static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes,
+			int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned char cmd;
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_OUTPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x48))		/* DSP Block size */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+
+		if (devc->speed * devc->channels <= 23000)
+			cmd = 0x1c;	/* 8 bit PCM output */
+		else
+			cmd = 0x90;	/* 8 bit high speed PCM output (SB2.01/Pro) */
+
+		if (!sb_dsp_command(devc, cmd))
+			printk(KERN_ERR "Sound Blaster:  unable to start DAC.\n");
+	}
+	else
+		printk(KERN_ERR "Sound Blaster: unable to start DAC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->intr_active = 1;
+}
+
+static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned char cmd;
+
+	/*
+	 * Start a DMA input to the buffer pointed by dmaqtail
+	 */
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_INPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x48))		/* DSP Block size */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+
+		if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000))
+			cmd = 0x2c;	/* 8 bit PCM input */
+		else
+			cmd = 0x98;	/* 8 bit high speed PCM input (SB2.01/Pro) */
+
+		if (!sb_dsp_command(devc, cmd))
+			printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
+	}
+	else
+		printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->intr_active = 1;
+}
+
+static void sb20_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	bits &= devc->irq_mode;
+
+	if (!bits)
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	else
+	{
+		switch (devc->irq_mode)
+		{
+			case IMODE_INPUT:
+				sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+				break;
+
+			case IMODE_OUTPUT:
+				sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+			    break;
+		}
+	}
+	devc->trigger_bits = bits;
+}
+
+/*
+ * SB2.01 specific speed setup
+ */
+
+static int sb201_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	int tmp;
+	int s = speed * devc->channels;
+
+	if (speed > 0)
+	{
+		if (speed < 4000)
+			speed = 4000;
+		if (speed > 44100)
+			speed = 44100;
+		if (devc->opened & OPEN_READ && speed > 15000)
+			speed = 15000;
+		devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
+		tmp = 256 - devc->tconst;
+		speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+/*
+ * SB Pro specific routines
+ */
+
+static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount)
+{				/* For SB Pro and Jazz16 */
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+	unsigned char bits = 0;
+
+	if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+		audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma =
+			devc->bits == 16 ? devc->dma16 : devc->dma8;
+
+	if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
+		if (devc->bits == AFMT_S16_LE)
+			bits = 0x04;	/* 16 bit mode */
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKOFF);
+	if (devc->channels == 1)
+		sb_dsp_command(devc, 0xa0 | bits);	/* Mono input */
+	else
+		sb_dsp_command(devc, 0xa8 | bits);	/* Stereo input */
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount)
+{				/* For SB Pro and Jazz16 */
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned char bits = 0;
+
+	if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+		audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8;
+	if (devc->model == MDL_SBPRO)
+		sb_mixer_set_stereo(devc, devc->channels == 2);
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKON);
+
+	if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
+	{
+		if (devc->bits == AFMT_S16_LE)
+			bits = 0x04;	/* 16 bit mode */
+
+		if (devc->channels == 1)
+			sb_dsp_command(devc, 0xa0 | bits);	/* Mono output */
+		else
+			sb_dsp_command(devc, 0xa8 | bits);	/* Stereo output */
+		spin_unlock_irqrestore(&devc->lock, flags);
+	}
+	else
+	{
+		spin_unlock_irqrestore(&devc->lock, flags);
+		tmp = sb_getmixer(devc, 0x0e);
+		if (devc->channels == 1)
+			tmp &= ~0x02;
+		else
+			tmp |= 0x02;
+		sb_setmixer(devc, 0x0e, tmp);
+	}
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sbpro_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (speed > 0)
+	{
+		if (speed < 4000)
+			speed = 4000;
+		if (speed > 44100)
+			speed = 44100;
+		if (devc->channels > 1 && speed > 22050)
+			speed = 22050;
+		sb201_audio_set_speed(dev, speed);
+	}
+	return devc->speed;
+}
+
+static short sbpro_audio_set_channels(int dev, short channels)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (channels == 1 || channels == 2)
+	{
+		if (channels != devc->channels)
+		{
+			devc->channels = channels;
+			if (devc->model == MDL_SBPRO && devc->channels == 2)
+				sbpro_audio_set_speed(dev, devc->speed);
+		}
+	}
+	return devc->channels;
+}
+
+static int jazz16_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (speed > 0)
+	{
+		int tmp;
+		int s = speed * devc->channels;
+
+		if (speed < 5000)
+			speed = 5000;
+		if (speed > 44100)
+			speed = 44100;
+
+		devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
+
+		tmp = 256 - devc->tconst;
+		speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+/*
+ * SB16 specific routines
+ */
+
+static int sb16_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	int	max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100;
+
+	if (speed > 0)
+	{
+		if (speed < 5000)
+			speed = 5000;
+
+		if (speed > max_speed)
+			speed = max_speed;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+static unsigned int sb16_audio_set_bits(int dev, unsigned int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (bits != 0)
+	{
+		if (bits == AFMT_U8 || bits == AFMT_S16_LE)
+			devc->bits = bits;
+		else
+			devc->bits = AFMT_U8;
+	}
+
+	return devc->bits;
+}
+
+static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex)
+	{
+		audio_devs[dev]->dmap_out->dma =
+			audio_devs[dev]->dmap_in->dma =
+				devc->bits == AFMT_S16_LE ?
+					devc->dma16 : devc->dma8;
+	}
+	else if (devc->bits == AFMT_S16_LE)
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma8;
+		audio_devs[dev]->dmap_in->dma = devc->dma16;
+	}
+	else
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma16;
+		audio_devs[dev]->dmap_in->dma = devc->dma8;
+	}
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex)
+	{
+		audio_devs[dev]->dmap_out->dma =
+			audio_devs[dev]->dmap_in->dma =
+				devc->bits == AFMT_S16_LE ?
+					devc->dma16 : devc->dma8;
+	}
+	else if (devc->bits == AFMT_S16_LE)
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma8;
+		audio_devs[dev]->dmap_in->dma = devc->dma16;
+	}
+	else
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma16;
+		audio_devs[dev]->dmap_in->dma = devc->dma8;
+	}
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static void sb16_audio_output_block(int dev, unsigned long buf, int count,
+			int intrflag)
+{
+	unsigned long   flags, cnt;
+	sb_devc        *devc = audio_devs[dev]->devc;
+	unsigned long   bits;
+
+	if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
+	{
+		devc->irq_mode = IMODE_OUTPUT;
+		devc->intr_active = 1;
+	}
+	else
+	{
+		devc->irq_mode_16 = IMODE_OUTPUT;
+		devc->intr_active_16 = 1;
+	}
+
+	/* save value */
+	spin_lock_irqsave(&devc->lock, flags);
+	bits = devc->bits;
+	if (devc->fullduplex)
+		devc->bits = (devc->bits == AFMT_S16_LE) ?
+			AFMT_U8 : AFMT_S16_LE;
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	cnt = count;
+	if (devc->bits == AFMT_S16_LE)
+		cnt >>= 1;
+	cnt--;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	sb_dsp_command(devc, 0x41);
+	sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
+	sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
+
+	sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6));
+	sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
+			      (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
+	sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
+	sb_dsp_command(devc, (unsigned char) (cnt >> 8));
+
+	/* restore real value after all programming */
+	devc->bits = bits;
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+
+/*
+ *	This fails on the Cyrix MediaGX. If you don't have the DMA enabled
+ *	before the first sample arrives it locks up. However even if you
+ *	do enable the DMA in time you just get DMA timeouts and missing
+ *	interrupts and stuff, so for now I've not bothered fixing this either.
+ */
+ 
+static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
+{
+	unsigned long   flags, cnt;
+	sb_devc        *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
+	{
+		devc->irq_mode = IMODE_INPUT;
+		devc->intr_active = 1;
+	}
+	else
+	{
+		devc->irq_mode_16 = IMODE_INPUT;
+		devc->intr_active_16 = 1;
+	}
+
+	cnt = count;
+	if (devc->bits == AFMT_S16_LE)
+		cnt >>= 1;
+	cnt--;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	sb_dsp_command(devc, 0x42);
+	sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
+	sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
+
+	sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce));
+	sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
+			      (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
+	sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
+	sb_dsp_command(devc, (unsigned char) (cnt >> 8));
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+static void sb16_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	int bits_16 = bits & devc->irq_mode_16;
+	bits &= devc->irq_mode;
+
+	if (!bits && !bits_16)
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	else
+	{
+		if (bits)
+		{
+			switch (devc->irq_mode)
+			{
+				case IMODE_INPUT:
+					sb16_audio_start_input(dev,
+							devc->trg_buf,
+							devc->trg_bytes,
+							devc->trg_intrflag);
+					break;
+
+				case IMODE_OUTPUT:
+					sb16_audio_output_block(dev,
+							devc->trg_buf,
+							devc->trg_bytes,
+							devc->trg_intrflag);
+					break;
+			}
+		}
+		if (bits_16)
+		{
+			switch (devc->irq_mode_16)
+			{
+				case IMODE_INPUT:
+					sb16_audio_start_input(dev,
+							devc->trg_buf_16,
+							devc->trg_bytes_16,
+							devc->trg_intrflag_16);
+					break;
+
+				case IMODE_OUTPUT:
+					sb16_audio_output_block(dev,
+							devc->trg_buf_16,
+							devc->trg_bytes_16,
+							devc->trg_intrflag_16);
+					break;
+			}
+		}
+	}
+
+	devc->trigger_bits = bits | bits_16;
+}
+
+static unsigned char lbuf8[2048];
+static signed short *lbuf16 = (signed short *)lbuf8;
+#define LBUFCOPYSIZE 1024
+static void
+sb16_copy_from_user(int dev,
+		char *localbuf, int localoffs,
+		const char __user *userbuf, int useroffs,
+		int max_in, int max_out,
+		int *used, int *returned,
+		int len)
+{
+	sb_devc       *devc = audio_devs[dev]->devc;
+	int           i, c, p, locallen;
+	unsigned char *buf8;
+	signed short  *buf16;
+
+	/* if not duplex no conversion */
+	if (!devc->fullduplex)
+	{
+		if (copy_from_user(localbuf + localoffs,
+				   userbuf + useroffs, len))
+			return;
+		*used = len;
+		*returned = len;
+	}
+	else if (devc->bits == AFMT_S16_LE)
+	{
+		/* 16 -> 8 */
+		/* max_in >> 1, max number of samples in ( 16 bits ) */
+		/* max_out, max number of samples out ( 8 bits ) */
+		/* len, number of samples that will be taken ( 16 bits )*/
+		/* c, count of samples remaining in buffer ( 16 bits )*/
+		/* p, count of samples already processed ( 16 bits )*/
+		len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1);
+		c = len;
+		p = 0;
+		buf8 = (unsigned char *)(localbuf + localoffs);
+		while (c)
+		{
+			locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
+			/* << 1 in order to get 16 bit samples */
+			if (copy_from_user(lbuf16,
+					   userbuf + useroffs + (p << 1),
+					   locallen << 1))
+				return;
+			for (i = 0; i < locallen; i++)
+			{
+				buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80;
+			}
+			c -= locallen; p += locallen;
+		}
+		/* used = ( samples * 16 bits size ) */
+		*used =  max_in  > ( max_out << 1) ? (max_out << 1) : max_in;
+		/* returned = ( samples * 8 bits size ) */
+		*returned = len;
+	}
+	else
+	{
+		/* 8 -> 16 */
+		/* max_in, max number of samples in ( 8 bits ) */
+		/* max_out >> 1, max number of samples out ( 16 bits ) */
+		/* len, number of samples that will be taken ( 8 bits )*/
+		/* c, count of samples remaining in buffer ( 8 bits )*/
+		/* p, count of samples already processed ( 8 bits )*/
+		len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in;
+		c = len;
+		p = 0;
+		buf16 = (signed short *)(localbuf + localoffs);
+		while (c)
+		{
+			locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
+			if (copy_from_user(lbuf8,
+					   userbuf+useroffs + p,
+					   locallen))
+				return;
+			for (i = 0; i < locallen; i++)
+			{
+				buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8;
+			}
+	      		c -= locallen; p += locallen;
+		}
+		/* used = ( samples * 8 bits size ) */
+		*used = len;
+		/* returned = ( samples * 16 bits size ) */
+		*returned = len << 1;
+	}
+}
+
+static void
+sb16_audio_mmap(int dev)
+{
+	sb_devc       *devc = audio_devs[dev]->devc;
+	devc->fullduplex = 0;
+}
+
+static struct audio_driver sb1_audio_driver =	/* SB1.x */
+{
+	.owner			= THIS_MODULE,
+	.open			= sb_audio_open,
+	.close			= sb_audio_close,
+	.output_block		= sb_set_output_parms,
+	.start_input		= sb_set_input_parms,
+	.prepare_for_input	= sb1_audio_prepare_for_input,
+	.prepare_for_output	= sb1_audio_prepare_for_output,
+	.halt_io		= sb1_audio_halt_xfer,
+	.trigger		= sb1_audio_trigger,
+	.set_speed		= sb1_audio_set_speed,
+	.set_bits		= sb1_audio_set_bits,
+	.set_channels		= sb1_audio_set_channels
+};
+
+static struct audio_driver sb20_audio_driver =	/* SB2.0 */
+{
+	.owner			= THIS_MODULE,
+	.open			= sb_audio_open,
+	.close			= sb_audio_close,
+	.output_block		= sb_set_output_parms,
+	.start_input		= sb_set_input_parms,
+	.prepare_for_input	= sb1_audio_prepare_for_input,
+	.prepare_for_output	= sb1_audio_prepare_for_output,
+	.halt_io		= sb1_audio_halt_xfer,
+	.trigger		= sb20_audio_trigger,
+	.set_speed		= sb1_audio_set_speed,
+	.set_bits		= sb1_audio_set_bits,
+	.set_channels		= sb1_audio_set_channels
+};
+
+static struct audio_driver sb201_audio_driver =		/* SB2.01 */
+{
+	.owner			= THIS_MODULE,
+	.open			= sb_audio_open,
+	.close			= sb_audio_close,
+	.output_block		= sb_set_output_parms,
+	.start_input		= sb_set_input_parms,
+	.prepare_for_input	= sb1_audio_prepare_for_input,
+	.prepare_for_output	= sb1_audio_prepare_for_output,
+	.halt_io		= sb1_audio_halt_xfer,
+	.trigger		= sb20_audio_trigger,
+	.set_speed		= sb201_audio_set_speed,
+	.set_bits		= sb1_audio_set_bits,
+	.set_channels		= sb1_audio_set_channels
+};
+
+static struct audio_driver sbpro_audio_driver =		/* SB Pro */
+{
+	.owner			= THIS_MODULE,
+	.open			= sb_audio_open,
+	.close			= sb_audio_close,
+	.output_block		= sb_set_output_parms,
+	.start_input		= sb_set_input_parms,
+	.prepare_for_input	= sbpro_audio_prepare_for_input,
+	.prepare_for_output	= sbpro_audio_prepare_for_output,
+	.halt_io		= sb1_audio_halt_xfer,
+	.trigger		= sb20_audio_trigger,
+	.set_speed		= sbpro_audio_set_speed,
+	.set_bits		= sb1_audio_set_bits,
+	.set_channels		= sbpro_audio_set_channels
+};
+
+static struct audio_driver jazz16_audio_driver =	/* Jazz16 and SM Wave */
+{
+	.owner			= THIS_MODULE,
+	.open			= sb_audio_open,
+	.close			= sb_audio_close,
+	.output_block		= sb_set_output_parms,
+	.start_input		= sb_set_input_parms,
+	.prepare_for_input	= sbpro_audio_prepare_for_input,
+	.prepare_for_output	= sbpro_audio_prepare_for_output,
+	.halt_io		= sb1_audio_halt_xfer,
+	.trigger		= sb20_audio_trigger,
+	.set_speed		= jazz16_audio_set_speed,
+	.set_bits		= sb16_audio_set_bits,
+	.set_channels		= sbpro_audio_set_channels
+};
+
+static struct audio_driver sb16_audio_driver =	/* SB16 */
+{
+	.owner			= THIS_MODULE,
+	.open			= sb_audio_open,
+	.close			= sb_audio_close,
+	.output_block		= sb_set_output_parms,
+	.start_input		= sb_set_input_parms,
+	.prepare_for_input	= sb16_audio_prepare_for_input,
+	.prepare_for_output	= sb16_audio_prepare_for_output,
+	.halt_io		= sb1_audio_halt_xfer,
+	.copy_user		= sb16_copy_from_user,
+	.trigger		= sb16_audio_trigger,
+	.set_speed		= sb16_audio_set_speed,
+	.set_bits		= sb16_audio_set_bits,
+	.set_channels		= sbpro_audio_set_channels,
+	.mmap			= sb16_audio_mmap
+};
+
+void sb_audio_init(sb_devc * devc, char *name, struct module *owner)
+{
+	int audio_flags = 0;
+	int format_mask = AFMT_U8;
+
+	struct audio_driver *driver = &sb1_audio_driver;
+
+	switch (devc->model)
+	{
+		case MDL_SB1:	/* SB1.0 or SB 1.5 */
+			DDB(printk("Will use standard SB1.x driver\n"));
+			audio_flags = DMA_HARDSTOP;
+			break;
+
+		case MDL_SB2:
+			DDB(printk("Will use SB2.0 driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			driver = &sb20_audio_driver;
+			break;
+
+		case MDL_SB201:
+			DDB(printk("Will use SB2.01 (high speed) driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			driver = &sb201_audio_driver;
+			break;
+
+		case MDL_JAZZ:
+		case MDL_SMW:
+			DDB(printk("Will use Jazz16 driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			format_mask |= AFMT_S16_LE;
+			driver = &jazz16_audio_driver;
+			break;
+
+		case MDL_ESS:
+			DDB(printk("Will use ESS ES688/1688 driver\n"));
+			driver = ess_audio_init (devc, &audio_flags, &format_mask);
+			break;
+
+		case MDL_SB16:
+			DDB(printk("Will use SB16 driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			format_mask |= AFMT_S16_LE;
+			if (devc->dma8 != devc->dma16 && devc->dma16 != -1)
+			{
+				audio_flags |= DMA_DUPLEX;
+				devc->duplex = 1;
+			}
+			driver = &sb16_audio_driver;
+			break;
+
+		default:
+			DDB(printk("Will use SB Pro driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			driver = &sbpro_audio_driver;
+	}
+
+	if (owner)
+			driver->owner = owner;
+	
+	if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+				name,driver, sizeof(struct audio_driver),
+				audio_flags, format_mask, devc,
+				devc->dma8,
+				devc->duplex ? devc->dma16 : devc->dma8)) < 0)
+	{
+		  printk(KERN_ERR "Sound Blaster:  unable to install audio.\n");
+		  return;
+	}
+	audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev;
+	audio_devs[devc->dev]->min_fragment = 5;
+}
diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c
new file mode 100644
index 00000000..fb5d7250
--- /dev/null
+++ b/sound/oss/sb_card.c
@@ -0,0 +1,354 @@
+/*
+ * sound/oss/sb_card.c
+ *
+ * Detection routine for the ISA Sound Blaster and compatible sound
+ * cards.
+ *
+ * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this
+ * software for more info.
+ *
+ * This is a complete rewrite of the detection routines. This was
+ * prompted by the PnP API change during v2.5 and the ugly state the
+ * code was in.
+ *
+ * Copyright (C) by Paul Laufer 2002. Based on code originally by
+ * Hannu Savolainen which was modified by many others over the
+ * years. Authors specifically mentioned in the previous version were:
+ * Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de
+ * Melo, Daniel Church, and myself.
+ *
+ * 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com>
+ * 02-07-2003 Bug made it into first release. Take two.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include "sound_config.h"
+#include "sb_mixer.h"
+#include "sb.h"
+#ifdef CONFIG_PNP
+#include <linux/pnp.h>
+#endif /* CONFIG_PNP */
+#include "sb_card.h"
+
+MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver");
+MODULE_LICENSE("GPL");
+
+extern void *smw_free;
+
+static int __initdata mpu_io	= 0;
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma16	= -1;
+static int __initdata type	= 0; /* Can set this to a specific card type */
+static int __initdata esstype   = 0; /* ESS chip type */
+static int __initdata acer 	= 0; /* Do acer notebook init? */
+static int __initdata sm_games 	= 0; /* Logitech soundman games? */
+
+static struct sb_card_config *legacy = NULL;
+
+#ifdef CONFIG_PNP
+static int pnp_registered;
+static int __initdata pnp       = 1;
+/*
+static int __initdata uart401	= 0;
+*/
+#else
+static int __initdata pnp       = 0;
+#endif
+
+module_param(io, int, 000);
+MODULE_PARM_DESC(io,       "Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
+module_param(irq, int, 000);
+MODULE_PARM_DESC(irq,	   "IRQ (5,7,9,10)");
+module_param(dma, int, 000);
+MODULE_PARM_DESC(dma,	   "8-bit DMA channel (0,1,3)");
+module_param(dma16, int, 000);
+MODULE_PARM_DESC(dma16,	   "16-bit DMA channel (5,6,7)");
+module_param(mpu_io, int, 000);
+MODULE_PARM_DESC(mpu_io,   "MPU base address");
+module_param(type, int, 000);
+MODULE_PARM_DESC(type,	   "You can set this to specific card type (doesn't " \
+		 "work with pnp)");
+module_param(sm_games, int, 000);
+MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \
+		 "(doesn't work with pnp)");
+module_param(esstype, int, 000);
+MODULE_PARM_DESC(esstype,  "ESS chip type (doesn't work with pnp)");
+module_param(acer, int, 000);
+MODULE_PARM_DESC(acer,	   "Set this to detect cards in some ACER notebooks "\
+		 "(doesn't work with pnp)");
+
+#ifdef CONFIG_PNP
+module_param(pnp, int, 000);
+MODULE_PARM_DESC(pnp,     "Went set to 0 will disable detection using PnP. "\
+		  "Default is 1.\n");
+/* Not done yet.... */
+/*
+module_param(uart401, int, 000);
+MODULE_PARM_DESC(uart401,  "When set to 1, will attempt to detect and enable"\
+		 "the mpu on some clones");
+*/
+#endif /* CONFIG_PNP */
+
+/* OSS subsystem card registration shared by PnP and legacy routines */
+static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo)
+{
+	if (!request_region(scc->conf.io_base, 16, "soundblaster")) {
+		printk(KERN_ERR "sb: ports busy.\n");
+		kfree(scc);
+		return -EBUSY;
+	}
+
+	if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) {
+		release_region(scc->conf.io_base, 16);
+		printk(KERN_ERR "sb: Failed DSP Detect.\n");
+		kfree(scc);
+		return -ENODEV;
+	}
+	if(!sb_dsp_init(&scc->conf, THIS_MODULE)) {
+		printk(KERN_ERR "sb: Failed DSP init.\n");
+		kfree(scc);
+		return -ENODEV;
+	}
+	if(scc->mpucnf.io_base > 0) {
+		scc->mpu = 1;
+		printk(KERN_INFO "sb: Turning on MPU\n");
+		if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE))
+			scc->mpu = 0;
+	}
+
+	return 1;
+}
+
+static void sb_unload(struct sb_card_config *scc)
+{
+	sb_dsp_unload(&scc->conf, 0);
+	if(scc->mpu)
+		unload_sbmpu(&scc->mpucnf);
+	kfree(scc);
+}
+
+/* Register legacy card with OSS subsystem */
+static int __init sb_init_legacy(void)
+{
+	struct sb_module_options sbmo = {0};
+
+	if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "sb: Error: Could not allocate memory\n");
+		return -ENOMEM;
+	}
+
+	legacy->conf.io_base      = io;
+	legacy->conf.irq          = irq;
+	legacy->conf.dma          = dma;
+	legacy->conf.dma2         = dma16;
+	legacy->conf.card_subtype = type;
+
+	legacy->mpucnf.io_base = mpu_io;
+	legacy->mpucnf.irq     = -1;
+	legacy->mpucnf.dma     = -1;
+	legacy->mpucnf.dma2    = -1;
+
+	sbmo.esstype  = esstype;
+	sbmo.sm_games = sm_games;
+	sbmo.acer     = acer;
+
+	return sb_register_oss(legacy, &sbmo);
+}
+
+#ifdef CONFIG_PNP
+
+/* Populate the OSS subsystem structures with information from PnP */
+static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc)
+{
+	scc->conf.io_base   = -1;
+	scc->conf.irq       = -1;
+	scc->conf.dma       = -1;
+	scc->conf.dma2      = -1;
+	scc->mpucnf.io_base = -1;
+	scc->mpucnf.irq     = -1;
+	scc->mpucnf.dma     = -1;
+	scc->mpucnf.dma2    = -1;
+
+	/* All clones layout their PnP tables differently and some use
+	   different logical devices for the MPU */
+	if(!strncmp("CTL",scc->card_id,3)) {
+		scc->conf.io_base   = pnp_port_start(dev,0);
+		scc->conf.irq       = pnp_irq(dev,0);
+		scc->conf.dma       = pnp_dma(dev,0);
+		scc->conf.dma2      = pnp_dma(dev,1);
+		scc->mpucnf.io_base = pnp_port_start(dev,1);
+		return;
+	}
+	if(!strncmp("tBA",scc->card_id,3)) {
+		scc->conf.io_base   = pnp_port_start(dev,0);
+		scc->conf.irq       = pnp_irq(dev,0);
+		scc->conf.dma       = pnp_dma(dev,0);
+		scc->conf.dma2      = pnp_dma(dev,1);
+		return;
+	}
+	if(!strncmp("ESS",scc->card_id,3)) {
+		scc->conf.io_base   = pnp_port_start(dev,0);
+		scc->conf.irq       = pnp_irq(dev,0);
+		scc->conf.dma       = pnp_dma(dev,0);
+		scc->conf.dma2      = pnp_dma(dev,1);
+	       	scc->mpucnf.io_base = pnp_port_start(dev,2);
+		return;
+	}
+	if(!strncmp("CMI",scc->card_id,3)) {
+		scc->conf.io_base = pnp_port_start(dev,0);
+		scc->conf.irq     = pnp_irq(dev,0);
+		scc->conf.dma     = pnp_dma(dev,0);
+		scc->conf.dma2    = pnp_dma(dev,1);
+		return;
+	}
+	if(!strncmp("RWB",scc->card_id,3)) {
+		scc->conf.io_base = pnp_port_start(dev,0);
+		scc->conf.irq     = pnp_irq(dev,0);
+		scc->conf.dma     = pnp_dma(dev,0);
+		return;
+	}
+	if(!strncmp("ALS",scc->card_id,3)) {
+		if(!strncmp("ALS0007",scc->card_id,7)) {
+			scc->conf.io_base = pnp_port_start(dev,0);
+			scc->conf.irq     = pnp_irq(dev,0);
+			scc->conf.dma     = pnp_dma(dev,0);
+		} else {
+			scc->conf.io_base = pnp_port_start(dev,0);
+			scc->conf.irq     = pnp_irq(dev,0);
+			scc->conf.dma     = pnp_dma(dev,1);
+			scc->conf.dma2    = pnp_dma(dev,0);
+		}
+		return;
+	}
+	if(!strncmp("RTL",scc->card_id,3)) {
+		scc->conf.io_base = pnp_port_start(dev,0);
+		scc->conf.irq     = pnp_irq(dev,0);
+		scc->conf.dma     = pnp_dma(dev,1);
+		scc->conf.dma2    = pnp_dma(dev,0);
+	}
+}
+
+static unsigned int sb_pnp_devices;
+
+/* Probe callback function for the PnP API */
+static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id)
+{
+	struct sb_card_config *scc;
+	struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */
+	struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL);
+	
+	if(!dev){
+		return -EBUSY;
+	}
+
+	if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "sb: Error: Could not allocate memory\n");
+		return -ENOMEM;
+	}
+
+	printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \
+	       "%s, Device PnP id = %s\n", card->card->name, card_id->id,
+	       dev->id->id);
+
+	scc->card_id = card_id->id;
+	scc->dev_id = dev->id->id;
+	sb_dev2cfg(dev, scc);
+
+	printk(KERN_INFO "sb: PnP:      Detected at: io=0x%x, irq=%d, " \
+	       "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq,
+	       scc->conf.dma, scc->conf.dma2);
+
+	pnp_set_card_drvdata(card, scc);
+	sb_pnp_devices++;
+
+	return sb_register_oss(scc, &sbmo);
+}
+
+static void sb_pnp_remove(struct pnp_card_link *card)
+{
+	struct sb_card_config *scc = pnp_get_card_drvdata(card);
+
+	if(!scc)
+		return;
+
+	printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id);
+
+	sb_unload(scc);
+}
+
+static struct pnp_card_driver sb_pnp_driver = {
+	.name          = "OSS SndBlstr", /* 16 character limit */
+	.id_table      = sb_pnp_card_table,
+	.probe         = sb_pnp_probe,
+	.remove        = sb_pnp_remove,
+};
+MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table);
+#endif /* CONFIG_PNP */
+
+static void sb_unregister_all(void)
+{
+#ifdef CONFIG_PNP
+	if (pnp_registered)
+		pnp_unregister_card_driver(&sb_pnp_driver);
+#endif
+}
+
+static int __init sb_init(void)
+{
+	int lres = 0;
+	int pres = 0;
+
+	printk(KERN_INFO "sb: Init: Starting Probe...\n");
+
+	if(io != -1 && irq != -1 && dma != -1) {
+		printk(KERN_INFO "sb: Probing legacy card with io=%x, "\
+		       "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16);
+		lres = sb_init_legacy();
+	} else if((io != -1 || irq != -1 || dma != -1) ||
+		  (!pnp && (io == -1 && irq == -1 && dma == -1)))
+		printk(KERN_ERR "sb: Error: At least io, irq, and dma "\
+		       "must be set for legacy cards.\n");
+
+#ifdef CONFIG_PNP
+	if(pnp) {
+		int err = pnp_register_card_driver(&sb_pnp_driver);
+		if (!err)
+			pnp_registered = 1;
+		pres = sb_pnp_devices;
+	}
+#endif
+	printk(KERN_INFO "sb: Init: Done\n");
+
+	/* If either PnP or Legacy registered a card then return
+	 * success */
+	if (pres == 0 && lres <= 0) {
+		sb_unregister_all();
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit sb_exit(void)
+{
+	printk(KERN_INFO "sb: Unloading...\n");
+
+	/* Unload legacy card */
+	if (legacy) {
+		printk (KERN_INFO "sb: Unloading legacy card\n");
+		sb_unload(legacy);
+	}
+
+	sb_unregister_all();
+
+	vfree(smw_free);
+	smw_free = NULL;
+}
+
+module_init(sb_init);
+module_exit(sb_exit);
diff --git a/sound/oss/sb_card.h b/sound/oss/sb_card.h
new file mode 100644
index 00000000..5535cff8
--- /dev/null
+++ b/sound/oss/sb_card.h
@@ -0,0 +1,149 @@
+/*
+ * sound/oss/sb_card.h
+ *
+ * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this
+ * software for more info.
+ *
+ * 02-05-2002 Original Release, Paul Laufer <paul@laufernet.com>
+ */
+
+struct sb_card_config {
+	struct address_info conf;
+	struct address_info mpucnf;
+	const  char         *card_id;
+	const  char         *dev_id;
+	int                 mpu;
+};
+
+#ifdef CONFIG_PNP
+
+/*
+ * SoundBlaster PnP tables and structures.
+ */
+
+/* Card PnP ID Table */
+static struct pnp_card_device_id sb_pnp_card_table[] = {
+	/* Sound Blaster 16 */
+	{.id = "CTL0024", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL0025", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL0026", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL0027", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL0028", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL0029", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL002a", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL002b", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL002c", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL00ed", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
+	/* Sound Blaster 16 */
+	{.id = "CTL0086", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
+	/* Sound Blaster Vibra16S */
+	{.id = "CTL0051", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
+	/* Sound Blaster Vibra16C */
+	{.id = "CTL0070", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
+	/* Sound Blaster Vibra16CL */
+	{.id = "CTL0080", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
+	/* Sound Blaster Vibra16CL */
+	{.id = "CTL00F0", .driver_data = 0, .devs = { {.id="CTL0043"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0039", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0042", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0043", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0044", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0045", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0046", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0047", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0048", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL0054", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
+	/* Sound Blaster AWE 32 */
+	{.id = "CTL009C", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
+	/* Createive SB32 PnP */
+	{.id = "CTL009F", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
+	/* Sound Blaster AWE 64 */
+	{.id = "CTL009D", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
+	/* Sound Blaster AWE 64 Gold */
+	{.id = "CTL009E", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
+	/* Sound Blaster AWE 64 Gold */
+	{.id = "CTL00B2", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
+	/* Sound Blaster AWE 64 */
+	{.id = "CTL00C1", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
+	/* Sound Blaster AWE 64 */
+	{.id = "CTL00C3", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
+	/* Sound Blaster AWE 64 */
+	{.id = "CTL00C5", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
+	/* Sound Blaster AWE 64 */
+	{.id = "CTL00C7", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
+	/* Sound Blaster AWE 64 */
+	{.id = "CTL00E4", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
+	/* Sound Blaster AWE 64 */
+	{.id = "CTL00E9", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
+	/* ESS 1868 */
+	{.id = "ESS0968", .driver_data = 0, .devs = { {.id="ESS0968"}, } },
+	/* ESS 1868 */
+	{.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS1868"}, } },
+	/* ESS 1868 */
+	{.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS8611"}, } },
+	/* ESS 1869 PnP AudioDrive */
+	{.id = "ESS0003", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
+	/* ESS 1869 */
+	{.id = "ESS1869", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
+	/* ESS 1878 */
+	{.id = "ESS1878", .driver_data = 0, .devs = { {.id="ESS1878"}, } },
+	/* ESS 1879 */
+	{.id = "ESS1879", .driver_data = 0, .devs = { {.id="ESS1879"}, } },
+	/* CMI 8330 SoundPRO */
+	{.id = "CMI0001", .driver_data = 0, .devs = { {.id="@X@0001"},
+						     {.id="@H@0001"},
+						     {.id="@@@0001"}, } },
+	/* Diamond DT0197H */
+	{.id = "RWR1688", .driver_data = 0, .devs = { {.id="@@@0001"},
+						     {.id="@X@0001"},
+						     {.id="@H@0001"}, } },
+	/* ALS007 */
+	{.id = "ALS0007", .driver_data = 0, .devs = { {.id="@@@0001"},
+						     {.id="@X@0001"},
+						     {.id="@H@0001"}, } },
+	/* ALS100 */
+	{.id = "ALS0001", .driver_data = 0, .devs = { {.id="@@@0001"},
+						     {.id="@X@0001"},
+						     {.id="@H@0001"}, } },
+	/* ALS110 */
+	{.id = "ALS0110", .driver_data = 0, .devs = { {.id="@@@1001"},
+						     {.id="@X@1001"},
+						     {.id="@H@0001"}, } },
+	/* ALS120 */
+	{.id = "ALS0120", .driver_data = 0, .devs = { {.id="@@@2001"},
+						     {.id="@X@2001"},
+						     {.id="@H@0001"}, } },
+	/* ALS200 */
+	{.id = "ALS0200", .driver_data = 0, .devs = { {.id="@@@0020"},
+						     {.id="@X@0030"},
+						     {.id="@H@0001"}, } },
+	/* ALS200 */
+	{.id = "RTL3000", .driver_data = 0, .devs = { {.id="@@@2001"},
+						     {.id="@X@2001"},
+						     {.id="@H@0001"}, } },
+	/* Sound Blaster 16 (Virtual PC 2004) */
+	{.id = "tBA03b0", .driver_data = 0, .devs = { {.id="PNPb003"}, } },
+	/* -end- */
+	{.id = "", }
+};
+
+#endif
diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c
new file mode 100644
index 00000000..7d42c541
--- /dev/null
+++ b/sound/oss/sb_common.c
@@ -0,0 +1,1292 @@
+/*
+ * sound/oss/sb_common.c
+ *
+ * Common routines for Sound Blaster compatible cards.
+ *
+ *
+ * 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.
+ *
+ *
+ * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts
+ *                       for full duplex support ( only sb16 by now )
+ * Rolf Fokkens:	 Added (BETA?) support for ES1887 chips.
+ * (fokkensr@vertis.nl)	 Which means: You can adjust the recording levels.
+ *
+ * 2000/01/18 - separated sb_card and sb_common -
+ * Jeff Garzik <jgarzik@pobox.com>
+ *
+ * 2000/09/18 - got rid of attach_uart401
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * 2001/01/26 - replaced CLI/STI with spinlocks
+ * Chris Rankin <rankinc@zipworld.com.au>
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#include "sound_config.h"
+#include "sound_firmware.h"
+
+#include "mpu401.h"
+
+#include "sb_mixer.h"
+#include "sb.h"
+#include "sb_ess.h"
+
+/*
+ * global module flag
+ */
+
+int sb_be_quiet;
+
+static sb_devc *detected_devc;	/* For communication from probe to init */
+static sb_devc *last_devc;	/* For MPU401 initialization */
+
+static unsigned char jazz_irq_bits[] = {
+	0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6
+};
+
+static unsigned char jazz_dma_bits[] = {
+	0, 1, 0, 2, 0, 3, 0, 4
+};
+
+void *smw_free;
+
+/*
+ * Jazz16 chipset specific control variables
+ */
+
+static int jazz16_base;			/* Not detected */
+static unsigned char jazz16_bits;	/* I/O relocation bits */
+static DEFINE_SPINLOCK(jazz16_lock);
+
+/*
+ * Logitech Soundman Wave specific initialization code
+ */
+
+#ifdef SMW_MIDI0001_INCLUDED
+#include "smw-midi0001.h"
+#else
+static unsigned char *smw_ucode;
+static int      smw_ucodeLen;
+
+#endif
+
+static sb_devc *last_sb;		/* Last sb loaded */
+
+int sb_dsp_command(sb_devc * devc, unsigned char val)
+{
+	int i;
+	unsigned long limit;
+
+	limit = jiffies + HZ / 10;	/* Timeout */
+	
+	/*
+	 * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
+	 * called while interrupts are disabled. This means that the timer is
+	 * disabled also. However the timeout situation is a abnormal condition.
+	 * Normally the DSP should be ready to accept commands after just couple of
+	 * loops.
+	 */
+
+	for (i = 0; i < 500000 && (limit-jiffies)>0; i++)
+	{
+		if ((inb(DSP_STATUS) & 0x80) == 0)
+		{
+			outb((val), DSP_COMMAND);
+			return 1;
+		}
+	}
+	printk(KERN_WARNING "Sound Blaster:  DSP command(%x) timeout.\n", val);
+	return 0;
+}
+
+int sb_dsp_get_byte(sb_devc * devc)
+{
+	int i;
+
+	for (i = 1000; i; i--)
+	{
+		if (inb(DSP_DATA_AVAIL) & 0x80)
+			return inb(DSP_READ);
+	}
+	return 0xffff;
+}
+
+static void sb_intr (sb_devc *devc)
+{
+	int status;
+	unsigned char   src = 0xff;
+
+	if (devc->model == MDL_SB16)
+	{
+		src = sb_getmixer(devc, IRQ_STAT);	/* Interrupt source register */
+
+		if (src & 4)						/* MPU401 interrupt */
+			if(devc->midi_irq_cookie)
+				uart401intr(devc->irq, devc->midi_irq_cookie);
+
+		if (!(src & 3))
+			return;	/* Not a DSP interrupt */
+	}
+	if (devc->intr_active && (!devc->fullduplex || (src & 0x01)))
+	{
+		switch (devc->irq_mode)
+		{
+			case IMODE_OUTPUT:
+				DMAbuf_outputintr(devc->dev, 1);
+				break;
+
+			case IMODE_INPUT:
+				DMAbuf_inputintr(devc->dev);
+				break;
+
+			case IMODE_INIT:
+				break;
+
+			case IMODE_MIDI:
+				sb_midi_interrupt(devc);
+				break;
+
+			default:
+				/* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
+				;
+		}
+	}
+	else if (devc->intr_active_16 && (src & 0x02))
+	{
+		switch (devc->irq_mode_16)
+		{
+			case IMODE_OUTPUT:
+				DMAbuf_outputintr(devc->dev, 1);
+				break;
+
+			case IMODE_INPUT:
+				DMAbuf_inputintr(devc->dev);
+				break;
+
+			case IMODE_INIT:
+				break;
+
+			default:
+				/* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
+				;
+		}
+	}
+	/*
+	 * Acknowledge interrupts 
+	 */
+
+	if (src & 0x01)
+		status = inb(DSP_DATA_AVAIL);
+
+	if (devc->model == MDL_SB16 && src & 0x02)
+		status = inb(DSP_DATA_AVL16);
+}
+
+static void pci_intr(sb_devc *devc)
+{
+	int src = inb(devc->pcibase+0x1A);
+	src&=3;
+	if(src)
+		sb_intr(devc);
+}
+
+static irqreturn_t sbintr(int irq, void *dev_id)
+{
+	sb_devc *devc = dev_id;
+
+	devc->irq_ok = 1;
+
+	switch (devc->model) {
+	case MDL_ESSPCI:
+		pci_intr (devc);
+		break;
+		
+	case MDL_ESS:
+		ess_intr (devc);
+		break;
+	default:
+		sb_intr (devc);
+		break;
+	}
+	return IRQ_HANDLED;
+}
+
+int sb_dsp_reset(sb_devc * devc)
+{
+	int loopc;
+
+	DEB(printk("Entered sb_dsp_reset()\n"));
+
+	if (devc->model == MDL_ESS) return ess_dsp_reset (devc);
+
+	/* This is only for non-ESS chips */
+
+	outb(1, DSP_RESET);
+
+	udelay(10);
+	outb(0, DSP_RESET);
+	udelay(30);
+
+	for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
+
+	if (inb(DSP_READ) != 0xAA)
+	{
+		DDB(printk("sb: No response to RESET\n"));
+		return 0;	/* Sorry */
+	}
+
+	DEB(printk("sb_dsp_reset() OK\n"));
+
+	return 1;
+}
+
+static void dsp_get_vers(sb_devc * devc)
+{
+	int i;
+
+	unsigned long   flags;
+
+	DDB(printk("Entered dsp_get_vers()\n"));
+	spin_lock_irqsave(&devc->lock, flags);
+	devc->major = devc->minor = 0;
+	sb_dsp_command(devc, 0xe1);	/* Get version */
+
+	for (i = 100000; i; i--)
+	{
+		if (inb(DSP_DATA_AVAIL) & 0x80)
+		{
+			if (devc->major == 0)
+				devc->major = inb(DSP_READ);
+			else
+			{
+				devc->minor = inb(DSP_READ);
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&devc->lock, flags);
+	DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor));
+}
+
+static int sb16_set_dma_hw(sb_devc * devc)
+{
+	int bits;
+
+	if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
+	{
+		printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
+		return 0;
+	}
+	bits = (1 << devc->dma8);
+
+	if (devc->dma16 >= 5 && devc->dma16 <= 7)
+		bits |= (1 << devc->dma16);
+
+	sb_setmixer(devc, DMA_NR, bits);
+	return 1;
+}
+
+static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config)
+{
+	/*
+	 * This routine initializes new MIDI port setup register of SB Vibra (CT2502).
+	 */
+	unsigned char   bits = sb_getmixer(devc, 0x84) & ~0x06;
+
+	switch (hw_config->io_base)
+	{
+		case 0x300:
+			sb_setmixer(devc, 0x84, bits | 0x04);
+			break;
+
+		case 0x330:
+			sb_setmixer(devc, 0x84, bits | 0x00);
+			break;
+
+		default:
+			sb_setmixer(devc, 0x84, bits | 0x02);		/* Disable MPU */
+			printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
+	}
+}
+
+static int sb16_set_irq_hw(sb_devc * devc, int level)
+{
+	int ival;
+
+	switch (level)
+	{
+		case 5:
+			ival = 2;
+			break;
+		case 7:
+			ival = 4;
+			break;
+		case 9:
+			ival = 1;
+			break;
+		case 10:
+			ival = 8;
+			break;
+		default:
+			printk(KERN_ERR "SB16: Invalid IRQ%d\n", level);
+			return 0;
+	}
+	sb_setmixer(devc, IRQ_NR, ival);
+	return 1;
+}
+
+static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char bits = 0;
+	unsigned long flags;
+
+	if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
+		return;
+
+	switch (hw_config->io_base)
+	{
+		case 0x220:
+			bits = 1;
+			break;
+		case 0x240:
+			bits = 2;
+			break;
+		case 0x260:
+			bits = 3;
+			break;
+		default:
+			return;
+	}
+	bits = jazz16_bits = bits << 5;
+	jazz16_base = hw_config->io_base;
+
+	/*
+	 *	Magic wake up sequence by writing to 0x201 (aka Joystick port)
+	 */
+	spin_lock_irqsave(&jazz16_lock, flags);
+	outb((0xAF), 0x201);
+	outb((0x50), 0x201);
+	outb((bits), 0x201);
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+}
+
+static int init_Jazz16(sb_devc * devc, struct address_info *hw_config)
+{
+	char name[100];
+	/*
+	 * First try to check that the card has Jazz16 chip. It identifies itself
+	 * by returning 0x12 as response to DSP command 0xfa.
+	 */
+
+	if (!sb_dsp_command(devc, 0xfa))
+		return 0;
+
+	if (sb_dsp_get_byte(devc) != 0x12)
+		return 0;
+
+	/*
+	 * OK so far. Now configure the IRQ and DMA channel used by the card.
+	 */
+	if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0)
+	{
+		printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
+		return 0;
+	}
+	if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0)
+	{
+		  printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
+		  return 0;
+	}
+	if (hw_config->dma2 < 0)
+	{
+		printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n");
+		return 0;
+	}
+	if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0)
+	{
+		printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
+		return 0;
+	}
+	devc->dma16 = hw_config->dma2;
+
+	if (!sb_dsp_command(devc, 0xfb))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] |
+			(jazz_dma_bits[hw_config->dma2] << 4)))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq]))
+		return 0;
+
+	/*
+	 * Now we have configured a standard Jazz16 device. 
+	 */
+	devc->model = MDL_JAZZ;
+	strcpy(name, "Jazz16");
+
+	hw_config->name = "Jazz16";
+	devc->caps |= SB_NO_MIDI;
+	return 1;
+}
+
+static void relocate_ess1688(sb_devc * devc)
+{
+	unsigned char bits;
+
+	switch (devc->base)
+	{
+		case 0x220:
+			bits = 0x04;
+			break;
+		case 0x230:
+			bits = 0x05;
+			break;
+		case 0x240:
+			bits = 0x06;
+			break;
+		case 0x250:
+			bits = 0x07;
+			break;
+		default:
+			return;	/* Wrong port */
+	}
+
+	DDB(printk("Doing ESS1688 address selection\n"));
+	
+	/*
+	 * ES1688 supports two alternative ways for software address config.
+	 * First try the so called Read-Sequence-Key method.
+	 */
+
+	/* Reset the sequence logic */
+	inb(0x229);
+	inb(0x229);
+	inb(0x229);
+
+	/* Perform the read sequence */
+	inb(0x22b);
+	inb(0x229);
+	inb(0x22b);
+	inb(0x229);
+	inb(0x229);
+	inb(0x22b);
+	inb(0x229);
+
+	/* Select the base address by reading from it. Then probe using the port. */
+	inb(devc->base);
+	if (sb_dsp_reset(devc))	/* Bingo */
+		return;
+
+#if 0				/* This causes system lockups (Nokia 386/25 at least) */
+	/*
+	 * The last resort is the system control register method.
+	 */
+
+	outb((0x00), 0xfb);	/* 0xFB is the unlock register */
+	outb((0x00), 0xe0);	/* Select index 0 */
+	outb((bits), 0xe1);	/* Write the config bits */
+	outb((0x00), 0xf9);	/* 0xFB is the lock register */
+#endif
+}
+
+int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo)
+{
+	sb_devc sb_info;
+	sb_devc *devc = &sb_info;
+
+	memset((char *) &sb_info, 0, sizeof(sb_info));	/* Zero everything */
+
+	/* Copy module options in place */
+	if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options));
+
+	sb_info.my_mididev = -1;
+	sb_info.my_mixerdev = -1;
+	sb_info.dev = -1;
+
+	/*
+	 * Initialize variables 
+	 */
+	
+	DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base));
+
+	spin_lock_init(&devc->lock);
+	devc->type = hw_config->card_subtype;
+
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->dma8 = hw_config->dma;
+
+	devc->dma16 = -1;
+	devc->pcibase = pciio;
+	
+	if(pci == SB_PCI_ESSMAESTRO)
+	{
+		devc->model = MDL_ESSPCI;
+		devc->caps |= SB_PCI_IRQ;
+		hw_config->driver_use_1 |= SB_PCI_IRQ;
+		hw_config->card_subtype	= MDL_ESSPCI;
+	}
+	
+	if(pci == SB_PCI_YAMAHA)
+	{
+		devc->model = MDL_YMPCI;
+		devc->caps |= SB_PCI_IRQ;
+		hw_config->driver_use_1 |= SB_PCI_IRQ;
+		hw_config->card_subtype	= MDL_YMPCI;
+		
+		printk("Yamaha PCI mode.\n");
+	}
+	
+	if (devc->sbmo.acer)
+	{
+		unsigned long flags;
+
+		spin_lock_irqsave(&devc->lock, flags);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x00);
+		spin_unlock_irqrestore(&devc->lock, flags);
+	}
+	/*
+	 * Detect the device
+	 */
+
+	if (sb_dsp_reset(devc))
+		dsp_get_vers(devc);
+	else
+		devc->major = 0;
+
+	if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW)
+		if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
+			relocate_Jazz16(devc, hw_config);
+
+	if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0))
+		relocate_ess1688(devc);
+
+	if (!sb_dsp_reset(devc))
+	{
+		DDB(printk("SB reset failed\n"));
+#ifdef MODULE
+		printk(KERN_INFO "sb: dsp reset failed.\n");
+#endif
+		return 0;
+	}
+	if (devc->major == 0)
+		dsp_get_vers(devc);
+
+	if (devc->major == 3 && devc->minor == 1)
+	{
+		if (devc->type == MDL_AZTECH)		/* SG Washington? */
+		{
+			if (sb_dsp_command(devc, 0x09))
+				if (sb_dsp_command(devc, 0x00))	/* Enter WSS mode */
+				{
+					int i;
+
+					/* Have some delay */
+					for (i = 0; i < 10000; i++)
+						inb(DSP_DATA_AVAIL);
+					devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
+					devc->model = MDL_AZTECH;
+				}
+		}
+	}
+	
+	if(devc->type == MDL_ESSPCI)
+		devc->model = MDL_ESSPCI;
+		
+	if(devc->type == MDL_YMPCI)
+	{
+		printk("YMPCI selected\n");
+		devc->model = MDL_YMPCI;
+	}
+		
+	/*
+	 * Save device information for sb_dsp_init()
+	 */
+
+
+	detected_devc = kmalloc(sizeof(sb_devc), GFP_KERNEL);
+	if (detected_devc == NULL)
+	{
+		printk(KERN_ERR "sb: Can't allocate memory for device information\n");
+		return 0;
+	}
+	memcpy(detected_devc, devc, sizeof(sb_devc));
+	MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
+	return 1;
+}
+
+int sb_dsp_init(struct address_info *hw_config, struct module *owner)
+{
+	sb_devc *devc;
+	char name[100];
+	extern int sb_be_quiet;
+	int	mixer22, mixer30;
+	
+/*
+ * Check if we had detected a SB device earlier
+ */
+	DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base));
+	name[0] = 0;
+
+	if (detected_devc == NULL)
+	{
+		MDB(printk("No detected device\n"));
+		return 0;
+	}
+	devc = detected_devc;
+	detected_devc = NULL;
+
+	if (devc->base != hw_config->io_base)
+	{
+		DDB(printk("I/O port mismatch\n"));
+		release_region(devc->base, 16);
+		return 0;
+	}
+	/*
+	 * Now continue initialization of the device
+	 */
+
+	devc->caps = hw_config->driver_use_1;
+
+	if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0)
+	{			/* IRQ setup */
+		
+		/*
+		 *	ESS PCI cards do shared PCI IRQ stuff. Since they
+		 *	will get shared PCI irq lines we must cope.
+		 */
+		 
+		int i=(devc->caps&SB_PCI_IRQ)?IRQF_SHARED:0;
+		
+		if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0)
+		{
+			printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
+			release_region(devc->base, 16);
+			return 0;
+		}
+		devc->irq_ok = 0;
+
+		if (devc->major == 4)
+			if (!sb16_set_irq_hw(devc, devc->irq))	/* Unsupported IRQ */
+			{
+				free_irq(devc->irq, devc);
+				release_region(devc->base, 16);
+				return 0;
+			}
+		if ((devc->type == 0 || devc->type == MDL_ESS) &&
+			devc->major == 3 && devc->minor == 1)
+		{		/* Handle various chipsets which claim they are SB Pro compatible */
+			if ((devc->type != 0 && devc->type != MDL_ESS) ||
+				!ess_init(devc, hw_config))
+			{
+				if ((devc->type != 0 && devc->type != MDL_JAZZ &&
+					 devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config))
+				{
+					DDB(printk("This is a genuine SB Pro\n"));
+				}
+			}
+		}
+		if (devc->major == 4 && devc->minor <= 11 )	/* Won't work */
+			devc->irq_ok = 1;
+		else
+		{
+			int n;
+
+			for (n = 0; n < 3 && devc->irq_ok == 0; n++)
+			{
+				if (sb_dsp_command(devc, 0xf2))	/* Cause interrupt immediately */
+				{
+					int i;
+
+					for (i = 0; !devc->irq_ok && i < 10000; i++);
+				}
+			}
+			if (!devc->irq_ok)
+				printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
+			else
+			{
+				DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq));
+			}
+		}
+	}			/* IRQ setup */
+
+	last_sb = devc;
+	
+	switch (devc->major)
+	{
+		case 1:		/* SB 1.0 or 1.5 */
+			devc->model = hw_config->card_subtype = MDL_SB1;
+			break;
+
+		case 2:		/* SB 2.x */
+			if (devc->minor == 0)
+				devc->model = hw_config->card_subtype = MDL_SB2;
+			else
+				devc->model = hw_config->card_subtype = MDL_SB201;
+			break;
+
+		case 3:		/* SB Pro and most clones */
+			switch (devc->model) {
+			case 0:
+				devc->model = hw_config->card_subtype = MDL_SBPRO;
+				if (hw_config->name == NULL)
+					hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
+				break;
+			case MDL_ESS:
+				ess_dsp_init(devc, hw_config);
+				break;
+			}
+			break;
+
+		case 4:
+			devc->model = hw_config->card_subtype = MDL_SB16;
+			/* 
+			 * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0
+			 * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas
+			 * a "standard" SB16 doesn't have a register at 0x4c.  ALS100 actively
+			 * updates register 0x22 whenever 0x30 changes, as per the SB16 spec.
+			 * Since ALS007 doesn't, this can be used to differentiate the 2 cards.
+			 */
+			if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) 
+			{
+				mixer30 = sb_getmixer(devc,0x30);
+				sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f);
+				sb_setmixer(devc,0x30,0xff);
+				/* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */
+				/* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10.     */
+				if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) 
+				{
+					devc->submodel = SUBMDL_ALS100;
+					if (hw_config->name == NULL)
+						hw_config->name = "Sound Blaster 16 (ALS-100)";
+        			}
+        			else
+        			{
+        				sb_setmixer(devc,0x3c,0x1f);    /* Enable all inputs */
+					sb_setmixer(devc,0x4c,0x1f);
+					sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */
+					devc->submodel = SUBMDL_ALS007;
+					if (hw_config->name == NULL)
+						hw_config->name = "Sound Blaster 16 (ALS-007)";
+				}
+				sb_setmixer(devc,0x30,mixer30);
+			}
+			else if (hw_config->name == NULL)
+				hw_config->name = "Sound Blaster 16";
+
+			if (hw_config->dma2 == -1)
+				devc->dma16 = devc->dma8;
+			else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
+			{
+				printk(KERN_WARNING  "SB16: Bad or missing 16 bit DMA channel\n");
+				devc->dma16 = devc->dma8;
+			}
+			else
+				devc->dma16 = hw_config->dma2;
+
+			if(!sb16_set_dma_hw(devc)) {
+				free_irq(devc->irq, devc);
+			        release_region(hw_config->io_base, 16);
+				return 0;
+			}
+
+			devc->caps |= SB_NO_MIDI;
+	}
+
+	if (!(devc->caps & SB_NO_MIXER))
+		if (devc->major == 3 || devc->major == 4)
+			sb_mixer_init(devc, owner);
+
+	if (!(devc->caps & SB_NO_MIDI))
+		sb_dsp_midi_init(devc, owner);
+
+	if (hw_config->name == NULL)
+		hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)";
+
+	sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor);
+	conf_printf(name, hw_config);
+
+	/*
+	 * Assuming that a sound card is Sound Blaster (compatible) is the most common
+	 * configuration error and the mother of all problems. Usually sound cards
+	 * emulate SB Pro but in addition they have a 16 bit native mode which should be
+	 * used in Unix. See Readme.cards for more information about configuring OSS/Free
+	 * properly.
+	 */
+	if (devc->model <= MDL_SBPRO)
+	{
+		if (devc->major == 3 && devc->minor != 1)	/* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
+		{
+			printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n");
+			printk(KERN_INFO "In many cases there is another way to configure OSS so that\n");
+			printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n");
+			printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n");
+		}
+		else if (!sb_be_quiet && devc->model == MDL_SBPRO)
+		{
+			printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor);
+			printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n");
+			printk(KERN_INFO "is incorrectly configured.\n");
+		}
+	}
+	hw_config->card_subtype = devc->model;
+	hw_config->slots[0]=devc->dev;
+	last_devc = devc;	/* For SB MPU detection */
+
+	if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
+	{
+		if (sound_alloc_dma(devc->dma8, "SoundBlaster8"))
+		{
+			printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
+		}
+		if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+		{
+			if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
+				printk(KERN_WARNING "Sound Blaster:  can't allocate 16 bit DMA channel %d.\n", devc->dma16);
+		}
+		sb_audio_init(devc, name, owner);
+		hw_config->slots[0]=devc->dev;
+	}
+	else
+	{
+		MDB(printk("Sound Blaster:  no audio devices found.\n"));
+	}
+	return 1;
+}
+
+/* if (sbmpu) below we allow mpu401 to manage the midi devs
+   otherwise we have to unload them. (Andrzej Krzysztofowicz) */
+   
+void sb_dsp_unload(struct address_info *hw_config, int sbmpu)
+{
+	sb_devc *devc;
+
+	devc = audio_devs[hw_config->slots[0]]->devc;
+
+	if (devc && devc->base == hw_config->io_base)
+	{
+		if ((devc->model & MDL_ESS) && devc->pcibase)
+			release_region(devc->pcibase, 8);
+
+		release_region(devc->base, 16);
+
+		if (!(devc->caps & SB_NO_AUDIO))
+		{
+			sound_free_dma(devc->dma8);
+			if (devc->dma16 >= 0)
+				sound_free_dma(devc->dma16);
+		}
+		if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI))
+		{
+			if (devc->irq > 0)
+				free_irq(devc->irq, devc);
+
+			sb_mixer_unload(devc);
+			/* We don't have to do this bit any more the UART401 is its own
+				master  -- Krzysztof Halasa */
+			/* But we have to do it, if UART401 is not detected */
+			if (!sbmpu)
+				sound_unload_mididev(devc->my_mididev);
+			sound_unload_audiodev(devc->dev);
+		}
+		kfree(devc);
+	}
+	else
+		release_region(hw_config->io_base, 16);
+
+	kfree(detected_devc);
+}
+
+/*
+ *	Mixer access routines
+ *
+ *	ES1887 modifications: some mixer registers reside in the
+ *	range above 0xa0. These must be accessed in another way.
+ */
+
+void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value)
+{
+	unsigned long flags;
+
+	if (devc->model == MDL_ESS) {
+		ess_setmixer (devc, port, value);
+		return;
+	}
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+	udelay(20);
+	outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+	udelay(20);
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+unsigned int sb_getmixer(sb_devc * devc, unsigned int port)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	if (devc->model == MDL_ESS) return ess_getmixer (devc, port);
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+	udelay(20);
+	val = inb(MIXER_DATA);
+	udelay(20);
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	return val;
+}
+
+void sb_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = sb_getmixer(devc, reg);
+	value = (value & ~mask) | (val & mask);
+	sb_setmixer(devc, reg, value);
+}
+
+/*
+ *	MPU401 MIDI initialization.
+ */
+
+static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&jazz16_lock, flags);  /* NOT the SB card? */
+
+	outb((addr & 0xff), base + 1);	/* Low address bits */
+	outb((addr >> 8), base + 2);	/* High address bits */
+	outb((val), base);	/* Data */
+
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+}
+
+static unsigned char smw_getmem(sb_devc * devc, int base, int addr)
+{
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&jazz16_lock, flags);  /* NOT the SB card? */
+
+	outb((addr & 0xff), base + 1);	/* Low address bits */
+	outb((addr >> 8), base + 2);	/* High address bits */
+	val = inb(base);	/* Data */
+
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+	return val;
+}
+
+static int smw_midi_init(sb_devc * devc, struct address_info *hw_config)
+{
+	int mpu_base = hw_config->io_base;
+	int mp_base = mpu_base + 4;		/* Microcontroller base */
+	int i;
+	unsigned char control;
+
+
+	/*
+	 *  Reset the microcontroller so that the RAM can be accessed
+	 */
+
+	control = inb(mpu_base + 7);
+	outb((control | 3), mpu_base + 7);	/* Set last two bits to 1 (?) */
+	outb(((control & 0xfe) | 2), mpu_base + 7);	/* xxxxxxx0 resets the mc */
+
+	mdelay(3);	/* Wait at least 1ms */
+
+	outb((control & 0xfc), mpu_base + 7);	/* xxxxxx00 enables RAM */
+
+	/*
+	 *  Detect microcontroller by probing the 8k RAM area
+	 */
+	smw_putmem(devc, mp_base, 0, 0x00);
+	smw_putmem(devc, mp_base, 1, 0xff);
+	udelay(10);
+
+	if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff)
+	{
+		DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1)));
+		return 0;	/* No RAM */
+	}
+	/*
+	 *  There is RAM so assume it's really a SM Wave
+	 */
+
+	devc->model = MDL_SMW;
+	smw_mixer_init(devc);
+
+#ifdef MODULE
+	if (!smw_ucode)
+	{
+		smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode);
+		smw_free = smw_ucode;
+	}
+#endif
+	if (smw_ucodeLen > 0)
+	{
+		if (smw_ucodeLen != 8192)
+		{
+			printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n");
+			return 1;
+		}
+		/*
+		 *  Download microcode
+		 */
+
+		for (i = 0; i < 8192; i++)
+			smw_putmem(devc, mp_base, i, smw_ucode[i]);
+
+		/*
+		 *  Verify microcode
+		 */
+
+		for (i = 0; i < 8192; i++)
+			if (smw_getmem(devc, mp_base, i) != smw_ucode[i])
+			{
+				printk(KERN_ERR "SM Wave: Microcode verification failed\n");
+				return 0;
+			}
+	}
+	control = 0;
+#ifdef SMW_SCSI_IRQ
+	/*
+	 * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
+	 * is disabled by default.
+	 *
+	 * FIXME - make this a module option
+	 *
+	 * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
+	 */
+	{
+		static unsigned char scsi_irq_bits[] = {
+			0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0
+		};
+		control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
+	}
+#endif
+
+#ifdef SMW_OPL4_ENABLE
+	/*
+	 *  Make the OPL4 chip visible on the PC bus at 0x380.
+	 *
+	 *  There is no need to enable this feature since this driver
+	 *  doesn't support OPL4 yet. Also there is no RAM in SM Wave so
+	 *  enabling OPL4 is pretty useless.
+	 */
+	control |= 0x10;	/* Uses IRQ12 if bit 0x20 == 0 */
+	/* control |= 0x20;      Uncomment this if you want to use IRQ7 */
+#endif
+	outb((control | 0x03), mpu_base + 7);	/* xxxxxx11 restarts */
+	hw_config->name = "SoundMan Wave";
+	return 1;
+}
+
+static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config)
+{
+	int mpu_base = hw_config->io_base;
+	int sb_base = devc->base;
+	int irq = hw_config->irq;
+
+	unsigned char bits = 0;
+	unsigned long flags;
+
+	if (irq < 0)
+		irq *= -1;
+
+	if (irq < 1 || irq > 15 ||
+	    jazz_irq_bits[irq] == 0)
+	{
+		printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
+		return 0;
+	}
+	switch (sb_base)
+	{
+		case 0x220:
+			bits = 1;
+			break;
+		case 0x240:
+			bits = 2;
+			break;
+		case 0x260:
+			bits = 3;
+			break;
+		default:
+			return 0;
+	}
+	bits = jazz16_bits = bits << 5;
+	switch (mpu_base)
+	{
+		case 0x310:
+			bits |= 1;
+			break;
+		case 0x320:
+			bits |= 2;
+			break;
+		case 0x330:
+			bits |= 3;
+			break;
+		default:
+			printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
+			return 0;
+	}
+	/*
+	 *	Magic wake up sequence by writing to 0x201 (aka Joystick port)
+	 */
+	spin_lock_irqsave(&jazz16_lock, flags);
+	outb(0xAF, 0x201);
+	outb(0x50, 0x201);
+	outb(bits, 0x201);
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+
+	hw_config->name = "Jazz16";
+	smw_midi_init(devc, hw_config);
+
+	if (!sb_dsp_command(devc, 0xfb))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] |
+			    (jazz_dma_bits[devc->dma16] << 4)))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] |
+			    (jazz_irq_bits[irq] << 4)))
+		return 0;
+
+	return 1;
+}
+
+int probe_sbmpu(struct address_info *hw_config, struct module *owner)
+{
+	sb_devc *devc = last_devc;
+	int ret;
+
+	if (last_devc == NULL)
+		return 0;
+
+	last_devc = NULL;
+
+	if (hw_config->io_base <= 0)
+	{
+		/* The real vibra16 is fine about this, but we have to go
+		   wipe up after Cyrix again */
+		   	   
+		if(devc->model == MDL_SB16 && devc->minor >= 12)
+		{
+			unsigned char   bits = sb_getmixer(devc, 0x84) & ~0x06;
+			sb_setmixer(devc, 0x84, bits | 0x02);		/* Disable MPU */
+		}
+		return 0;
+	}
+
+#if defined(CONFIG_SOUND_MPU401)
+	if (devc->model == MDL_ESS)
+	{
+		struct resource *ports;
+		ports = request_region(hw_config->io_base, 2, "mpu401");
+		if (!ports) {
+			printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
+			return 0;
+		}
+		if (!ess_midi_init(devc, hw_config)) {
+			release_region(hw_config->io_base, 2);
+			return 0;
+		}
+		hw_config->name = "ESS1xxx MPU";
+		devc->midi_irq_cookie = NULL;
+		if (!probe_mpu401(hw_config, ports)) {
+			release_region(hw_config->io_base, 2);
+			return 0;
+		}
+		attach_mpu401(hw_config, owner);
+		if (last_sb->irq == -hw_config->irq)
+			last_sb->midi_irq_cookie =
+				(void *)(long) hw_config->slots[1];
+		return 1;
+	}
+#endif
+
+	switch (devc->model)
+	{
+		case MDL_SB16:
+			if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
+			{
+				printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base);
+				return 0;
+			}
+			hw_config->name = "Sound Blaster 16";
+			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+				hw_config->irq = -devc->irq;
+			if (devc->minor > 12)		/* What is Vibra's version??? */
+				sb16_set_mpu_port(devc, hw_config);
+			break;
+
+		case MDL_JAZZ:
+			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+				hw_config->irq = -devc->irq;
+			if (!init_Jazz16_midi(devc, hw_config))
+				return 0;
+			break;
+
+		case MDL_YMPCI:
+			hw_config->name = "Yamaha PCI Legacy";
+			printk("Yamaha PCI legacy UART401 check.\n");
+			break;
+		default:
+			return 0;
+	}
+	
+	ret = probe_uart401(hw_config, owner);
+	if (ret)
+		last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc;
+	return ret;
+}
+
+void unload_sbmpu(struct address_info *hw_config)
+{
+#if defined(CONFIG_SOUND_MPU401)
+	if (!strcmp (hw_config->name, "ESS1xxx MPU")) {
+		unload_mpu401(hw_config);
+		return;
+	}
+#endif
+	unload_uart401(hw_config);
+}
+
+EXPORT_SYMBOL(sb_dsp_init);
+EXPORT_SYMBOL(sb_dsp_detect);
+EXPORT_SYMBOL(sb_dsp_unload);
+EXPORT_SYMBOL(sb_be_quiet);
+EXPORT_SYMBOL(probe_sbmpu);
+EXPORT_SYMBOL(unload_sbmpu);
+EXPORT_SYMBOL(smw_free);
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c
new file mode 100644
index 00000000..5c773dff
--- /dev/null
+++ b/sound/oss/sb_ess.c
@@ -0,0 +1,1831 @@
+#undef FKS_LOGGING
+#undef FKS_TEST
+
+/*
+ * tabs should be 4 spaces, in vi(m): set tabstop=4
+ *
+ * TODO: 	consistency speed calculations!!
+ *			cleanup!
+ * ????:	Did I break MIDI support?
+ *
+ * History:
+ *
+ * Rolf Fokkens	 (Dec 20 1998):	ES188x recording level support on a per
+ * fokkensr@vertis.nl			input basis.
+ *				 (Dec 24 1998):	Recognition of ES1788, ES1887, ES1888,
+ *								ES1868, ES1869 and ES1878. Could be used for
+ *								specific handling in the future. All except
+ *								ES1887 and ES1888 and ES688 are handled like
+ *								ES1688.
+ *				 (Dec 27 1998):	RECLEV for all (?) ES1688+ chips. ES188x now
+ *								have the "Dec 20" support + RECLEV
+ *				 (Jan  2 1999):	Preparation for Full Duplex. This means
+ *								Audio 2 is now used for playback when dma16
+ *								is specified. The next step would be to use
+ *								Audio 1 and Audio 2 at the same time.
+ *				 (Jan  9 1999):	Put all ESS stuff into sb_ess.[ch], this
+ *								includes both the ESS stuff that has been in
+ *								sb_*[ch] before I touched it and the ESS support
+ *								I added later
+ *				 (Jan 23 1999):	Full Duplex seems to work. I wrote a small
+ *								test proggy which works OK. Haven't found
+ *								any applications to test it though. So why did
+ *								I bother to create it anyway?? :) Just for
+ *								fun.
+ *				 (May  2 1999):	I tried to be too smart by "introducing"
+ *								ess_calc_best_speed (). The idea was that two
+ *								dividers could be used to setup a samplerate,
+ *								ess_calc_best_speed () would choose the best.
+ *								This works for playback, but results in
+ *								recording problems for high samplerates. I
+ *								fixed this by removing ess_calc_best_speed ()
+ *								and just doing what the documentation says. 
+ * Andy Sloane   (Jun  4 1999): Stole some code from ALSA to fix the playback
+ * andy@guildsoftware.com		speed on ES1869, ES1879, ES1887, and ES1888.
+ * 								1879's were previously ignored by this driver;
+ * 								added (untested) support for those.
+ * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for
+ * zezo@inet.bg					_ALL_ ESS models, not only ES1887
+ *
+ * This files contains ESS chip specifics. It's based on the existing ESS
+ * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This
+ * file adds features like:
+ * - Chip Identification (as shown in /proc/sound)
+ * - RECLEV support for ES1688 and later
+ * - 6 bits playback level support chips later than ES1688
+ * - Recording level support on a per-device basis for ES1887
+ * - Full-Duplex for ES1887
+ *
+ * Full duplex is enabled by specifying dma16. While the normal dma must
+ * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit
+ * DMA channel, while the others are 8 bit..
+ *
+ * ESS detection isn't full proof (yet). If it fails an additional module
+ * parameter esstype can be specified to be one of the following:
+ * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888
+ * -1 means: mimic 2.0 behaviour, 
+ *  0 means: auto detect.
+ *   others: explicitly specify chip
+ * -1 is default, cause auto detect still doesn't work.
+ */
+
+/*
+ * About the documentation
+ *
+ * I don't know if the chips all are OK, but the documentation is buggy. 'cause
+ * I don't have all the cips myself, there's a lot I cannot verify. I'll try to
+ * keep track of my latest insights about his here. If you have additional info,
+ * please enlighten me (fokkensr@vertis.nl)!
+ *
+ * I had the impression that ES1688 also has 6 bit master volume control. The
+ * documentation about ES1888 (rev C, october '95) claims that ES1888 has
+ * the following features ES1688 doesn't have:
+ * - 6 bit master volume
+ * - Full Duplex
+ * So ES1688 apparently doesn't have 6 bit master volume control, but the
+ * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too?
+ * Without RECLEV ES688 won't be much fun I guess.
+ *
+ * From the ES1888 (rev C, october '95) documentation I got the impression
+ * that registers 0x68 to 0x6e don't exist which means: no recording volume
+ * controls. To my surprise the ES888 documentation (1/14/96) claims that
+ * ES888 does have these record mixer registers, but that ES1888 doesn't have
+ * 0x69 and 0x6b. So the rest should be there.
+ *
+ * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2
+ * is both record and playback. I think I should use Audio 2 for all playback.
+ *
+ * The documentation is an adventure: it's close but not fully accurate. I
+ * found out that after a reset some registers are *NOT* reset, though the
+ * docs say the would be. Interesting ones are 0x7f, 0x7d and 0x7a. They are
+ * related to the Audio 2 channel. I also was surprised about the consequences
+ * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves
+ * into ES1888 mode. This means that it claims IRQ 11, which happens to be my
+ * ISDN adapter. Needless to say it no longer worked. I now understand why
+ * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS
+ * did it.
+ *
+ * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is
+ * described as if it's exactly the same as register 0xa1. This is *NOT* true.
+ * The description of 0x70 in ES1869 docs is accurate however.
+ * Well, the assumption about ES1869 was wrong: register 0x70 is very much
+ * like register 0xa1, except that bit 7 is always 1, whatever you want
+ * it to be.
+ *
+ * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2
+ * has effect.
+ *
+ * Software reset not being able to reset all registers is great! Especially
+ * the fact that register 0x78 isn't reset is great when you wanna change back
+ * to single dma operation (simplex): audio 2 is still operational, and uses
+ * the same dma as audio 1: your ess changes into a funny echo machine.
+ *
+ * Received the news that ES1688 is detected as a ES1788. Did some thinking:
+ * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register
+ * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If
+ * can be modified, it's a 1688", which lead to a correct detection
+ * of my ES1887. It resulted however in bad detection of 1688 (reported by mail)
+ * and 1868 (if no PnP detection first): they result in a 1788 being detected.
+ * I don't have docs on 1688, but I do have docs on 1868: The documentation is
+ * probably inaccurate in the fact that I should check bit 2, not bit 3. This
+ * is what I do now.
+ */
+
+/*
+ * About recognition of ESS chips
+ *
+ * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in
+ * a (preliminary ??) datasheet on ES1887. Its aim is to identify ES1887, but
+ * during detection the text claims that "this chip may be ..." when a step
+ * fails. This scheme is used to distinct between the above chips.
+ * It appears however that some PnP chips like ES1868 are recognized as ES1788
+ * by the ES1887 detection scheme. These PnP chips can be detected in another
+ * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think)
+ * by repeatedly reading mixer register 0x40. This is done by ess_identify in
+ * sb_common.c.
+ * This results in the following detection steps:
+ * - distinct between ES688 and ES1688+ (as always done in this driver)
+ *   if ES688 we're ready
+ * - try to detect ES1868, ES1869 or ES1878
+ *   if successful we're ready
+ * - try to detect ES1888, ES1887 or ES1788
+ *   if successful we're ready
+ * - Dunno. Must be 1688. Will do in general
+ *
+ * About RECLEV support:
+ *
+ * The existing ES1688 support didn't take care of the ES1688+ recording
+ * levels very well. Whenever a device was selected (recmask) for recording
+ * its recording level was loud, and it couldn't be changed. The fact that
+ * internal register 0xb4 could take care of RECLEV, didn't work meaning until
+ * its value was restored every time the chip was reset; this reset the
+ * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with.
+ *
+ * About ES1887 support:
+ *
+ * The ES1887 has separate registers to control the recording levels, for all
+ * inputs. The ES1887 specific software makes these levels the same as their
+ * corresponding playback levels, unless recmask says they aren't recorded. In
+ * the latter case the recording volumes are 0.
+ * Now recording levels of inputs can be controlled, by changing the playback
+ * levels. Furthermore several devices can be recorded together (which is not
+ * possible with the ES1688).
+ * Besides the separate recording level control for each input, the common
+ * recording level can also be controlled by RECLEV as described above.
+ *
+ * Not only ES1887 have this recording mixer. I know the following from the
+ * documentation:
+ * ES688	no
+ * ES1688	no
+ * ES1868	no
+ * ES1869	yes
+ * ES1878	no
+ * ES1879	yes
+ * ES1888	no/yes	Contradicting documentation; most recent: yes
+ * ES1946	yes		This is a PCI chip; not handled by this driver
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+#include "sb_mixer.h"
+#include "sb.h"
+
+#include "sb_ess.h"
+
+#define ESSTYPE_LIKE20	-1		/* Mimic 2.0 behaviour					*/
+#define ESSTYPE_DETECT	0		/* Mimic 2.0 behaviour					*/
+
+#define SUBMDL_ES1788	0x10	/* Subtype ES1788 for specific handling */
+#define SUBMDL_ES1868	0x11	/* Subtype ES1868 for specific handling */
+#define SUBMDL_ES1869	0x12	/* Subtype ES1869 for specific handling */
+#define SUBMDL_ES1878	0x13	/* Subtype ES1878 for specific handling */
+#define SUBMDL_ES1879	0x16    /* ES1879 was initially forgotten */
+#define SUBMDL_ES1887	0x14	/* Subtype ES1887 for specific handling */
+#define SUBMDL_ES1888	0x15	/* Subtype ES1888 for specific handling */
+
+#define SB_CAP_ES18XX_RATE 0x100
+
+#define ES1688_CLOCK1 795444 /* 128 - div */
+#define ES1688_CLOCK2 397722 /* 256 - div */
+#define ES18XX_CLOCK1 793800 /* 128 - div */
+#define ES18XX_CLOCK2 768000 /* 256 - div */
+
+#ifdef FKS_LOGGING
+static void ess_show_mixerregs (sb_devc *devc);
+#endif
+static int ess_read (sb_devc * devc, unsigned char reg);
+static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data);
+static void ess_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
+
+/****************************************************************************
+ *																			*
+ *									ESS audio								*
+ *																			*
+ ****************************************************************************/
+
+struct ess_command {short cmd; short data;};
+
+/*
+ * Commands for initializing Audio 1 for input (record)
+ */
+static struct ess_command ess_i08m[] =		/* input 8 bit mono */
+	{ {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
+static struct ess_command ess_i16m[] =		/* input 16 bit mono */
+	{ {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
+static struct ess_command ess_i08s[] =		/* input 8 bit stereo */
+	{ {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
+static struct ess_command ess_i16s[] =		/* input 16 bit stereo */
+	{ {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
+
+static struct ess_command *ess_inp_cmds[] =
+	{ ess_i08m, ess_i16m, ess_i08s, ess_i16s };
+
+
+/*
+ * Commands for initializing Audio 1 for output (playback)
+ */
+static struct ess_command ess_o08m[] =		/* output 8 bit mono */
+	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
+static struct ess_command ess_o16m[] =		/* output 16 bit mono */
+	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
+static struct ess_command ess_o08s[] =		/* output 8 bit stereo */
+	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
+static struct ess_command ess_o16s[] =		/* output 16 bit stereo */
+	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
+
+static struct ess_command *ess_out_cmds[] =
+	{ ess_o08m, ess_o16m, ess_o08s, ess_o16s };
+
+static void ess_exec_commands
+	(sb_devc *devc, struct ess_command *cmdtab[])
+{
+	struct ess_command *cmd;
+
+	cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ];
+
+	while (cmd->cmd != -1) {
+		ess_write (devc, cmd->cmd, cmd->data);
+		cmd++;
+	}
+}
+
+static void ess_change
+	(sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = ess_read (devc, reg);
+	value = (value & ~mask) | (val & mask);
+	ess_write (devc, reg, value);
+}
+
+static void ess_set_output_parms
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (devc->duplex) {
+		devc->trg_buf_16 = buf;
+		devc->trg_bytes_16 = nr_bytes;
+		devc->trg_intrflag_16 = intrflag;
+		devc->irq_mode_16 = IMODE_OUTPUT;
+	} else {
+		devc->trg_buf = buf;
+		devc->trg_bytes = nr_bytes;
+		devc->trg_intrflag = intrflag;
+		devc->irq_mode = IMODE_OUTPUT;
+	}
+}
+
+static void ess_set_input_parms
+	(int dev, unsigned long buf, int count, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	devc->trg_buf = buf;
+	devc->trg_bytes = count;
+	devc->trg_intrflag = intrflag;
+	devc->irq_mode = IMODE_INPUT;
+}
+
+static int ess_calc_div (int clock, int revert, int *speedp, int *diffp)
+{
+	int divider;
+	int speed, diff;
+	int retval;
+
+	speed   = *speedp;
+	divider = (clock + speed / 2) / speed;
+	retval  = revert - divider;
+	if (retval > revert - 1) {
+		retval  = revert - 1;
+		divider = revert - retval;
+	}
+	/* This line is suggested. Must be wrong I think
+	*speedp = (clock + divider / 2) / divider;
+	So I chose the next one */
+
+	*speedp	= clock / divider;
+	diff	= speed - *speedp;
+	if (diff < 0) diff =-diff;
+	*diffp  = diff;
+
+	return retval;
+}
+
+static int ess_calc_best_speed
+	(int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp)
+{
+	int speed1 = *speedp, speed2 = *speedp;
+	int div1, div2;
+	int diff1, diff2;
+	int retval;
+
+	div1 = ess_calc_div (clock1, rev1, &speed1, &diff1);
+	div2 = ess_calc_div (clock2, rev2, &speed2, &diff2);
+
+	if (diff1 < diff2) {
+		*divp   = div1;
+		*speedp = speed1;
+		retval  = 1;
+	} else {
+	/*	*divp   = div2; */
+		*divp   = 0x80 | div2;
+		*speedp = speed2;
+		retval  = 2;
+	}
+
+	return retval;
+}
+
+/*
+ * Depending on the audiochannel ESS devices can
+ * have different clock settings. These are made consistent for duplex
+ * however.
+ * callers of ess_speed only do an audionum suggestion, which means
+ * input suggests 1, output suggests 2. This suggestion is only true
+ * however when doing duplex.
+ */
+static void ess_common_speed (sb_devc *devc, int *speedp, int *divp)
+{
+	int diff = 0, div;
+
+	if (devc->duplex) {
+		/*
+		 * The 0x80 is important for the first audio channel
+		 */
+		if (devc->submodel == SUBMDL_ES1888) {
+			div = 0x80 | ess_calc_div (795500, 256, speedp, &diff);
+		} else {
+			div = 0x80 | ess_calc_div (795500, 128, speedp, &diff);
+		}
+	} else if(devc->caps & SB_CAP_ES18XX_RATE) {
+		if (devc->submodel == SUBMDL_ES1888) {
+			ess_calc_best_speed(397700, 128, 795500, 256, 
+						&div, speedp);
+		} else {
+			ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, 
+						&div, speedp);
+		}
+	} else {
+		if (*speedp > 22000) {
+			div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff);
+		} else {
+			div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff);
+		}
+	}
+	*divp = div;
+}
+
+static void ess_speed (sb_devc *devc, int audionum)
+{
+	int speed;
+	int div, div2;
+
+	ess_common_speed (devc, &(devc->speed), &div);
+
+#ifdef FKS_REG_LOGGING
+printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div);
+#endif
+
+	/* Set filter roll-off to 90% of speed/2 */
+	speed = (devc->speed * 9) / 20;
+
+	div2 = 256 - 7160000 / (speed * 82);
+
+	if (!devc->duplex) audionum = 1;
+
+	if (audionum == 1) {
+		/* Change behaviour of register A1 *
+		sb_chg_mixer(devc, 0x71, 0x20, 0x20)
+		* For ES1869 only??? */
+		ess_write (devc, 0xa1, div);
+		ess_write (devc, 0xa2, div2);
+	} else {
+		ess_setmixer (devc, 0x70, div);
+		/*
+		 * FKS: fascinating: 0x72 doesn't seem to work.
+		 */
+		ess_write (devc, 0xa2, div2);
+		ess_setmixer (devc, 0x72, div2);
+	}
+}
+
+static int ess_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	ess_speed(devc, 1);
+
+	sb_dsp_command(devc, DSP_CMD_SPKOFF);
+
+	ess_write (devc, 0xb8, 0x0e);	/* Auto init DMA mode */
+	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
+	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/DMA request) */
+
+	ess_exec_commands (devc, ess_inp_cmds);
+
+	ess_change (devc, 0xb1, 0xf0, 0x50);
+	ess_change (devc, 0xb2, 0xf0, 0x50);
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	sb_dsp_reset(devc);
+	ess_speed(devc, 1);
+	ess_write (devc, 0xb8, 4);	/* Auto init DMA mode */
+	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
+	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/request) */
+
+	ess_exec_commands (devc, ess_out_cmds);
+
+	ess_change (devc, 0xb1, 0xf0, 0x50);	/* Enable DMA */
+	ess_change (devc, 0xb2, 0xf0, 0x50);	/* Enable IRQ */
+
+	sb_dsp_command(devc, DSP_CMD_SPKON);	/* There be sound! */
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned char bits;
+
+/* FKS: qqq
+	sb_dsp_reset(devc);
+*/
+
+	/*
+	 * Auto-Initialize:
+	 * DMA mode + demand mode (8 bytes/request, yes I want it all!)
+	 * But leave 16-bit DMA bit untouched!
+	 */
+	ess_chgmixer (devc, 0x78, 0xd0, 0xd0);
+
+	ess_speed(devc, 2);
+
+	/* bits 4:3 on ES1887 represent recording source. Keep them! */
+	bits = ess_getmixer (devc, 0x7a) & 0x18;
+
+	/* Set stereo/mono */
+	if (devc->channels != 1) bits |= 0x02;
+
+	/* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */
+	if (devc->bits != AFMT_U8) bits |= 0x05;	/* 16 bit */
+
+	/* Enable DMA, IRQ will be shared (hopefully)*/
+	bits |= 0x60;
+
+	ess_setmixer (devc, 0x7a, bits);
+
+	ess_mixer_reload (devc, SOUND_MIXER_PCM);	/* There be sound! */
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n"
+, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma);
+#endif
+
+	if (devc->duplex) {
+		return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount);
+	} else {
+		return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount);
+	}
+}
+
+static void ess_audio_halt_xfer(int dev)
+{
+	unsigned long flags;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	sb_dsp_reset(devc);
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	/*
+	 * Audio 2 may still be operational! Creates awful sounds!
+	 */
+	if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00);
+}
+
+static void ess_audio_start_input
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	/*
+	 * Start a DMA input to the buffer pointed by dmaqtail
+	 */
+
+	if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_INPUT;
+
+	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+
+	ess_change (devc, 0xb8, 0x0f, 0x0f);	/* Go */
+	devc->intr_active = 1;
+}
+
+static void ess_audio_output_block_audio1
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_OUTPUT;
+
+	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+
+	ess_change (devc, 0xb8, 0x05, 0x05);	/* Go */
+	devc->intr_active = 1;
+}
+
+static void ess_audio_output_block_audio2
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1;
+	count--;
+
+	ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff));
+	ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+	ess_chgmixer (devc, 0x78, 0x03, 0x03);   /* Go */
+
+	devc->irq_mode_16 = IMODE_OUTPUT;
+		devc->intr_active_16 = 1;
+}
+
+static void ess_audio_output_block
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (devc->duplex) {
+		ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag);
+	} else {
+		ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag);
+	}
+}
+
+/*
+ * FKS: the if-statements for both bits and bits_16 are quite alike.
+ * Combine this...
+ */
+static void ess_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	int bits_16 = bits & devc->irq_mode_16;
+	bits &= devc->irq_mode;
+
+	if (!bits && !bits_16) {
+		/* FKS oh oh.... wrong?? for dma 16? */
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	}
+
+	if (bits) {
+		switch (devc->irq_mode)
+		{
+			case IMODE_INPUT:
+				ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+					devc->trg_intrflag);
+				break;
+
+			case IMODE_OUTPUT:
+				ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+					devc->trg_intrflag);
+				break;
+		}
+	}
+
+	if (bits_16) {
+		switch (devc->irq_mode_16) {
+		case IMODE_INPUT:
+			ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16,
+					devc->trg_intrflag_16);
+			break;
+
+		case IMODE_OUTPUT:
+			ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16,
+					devc->trg_intrflag_16);
+			break;
+		}
+	}
+
+	devc->trigger_bits = bits | bits_16;
+}
+
+static int ess_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	int minspeed, maxspeed, dummydiv;
+
+	if (speed > 0) {
+		minspeed = (devc->duplex ? 6215  : 5000 );
+		maxspeed = (devc->duplex ? 44100 : 48000);
+		if (speed < minspeed) speed = minspeed;
+		if (speed > maxspeed) speed = maxspeed;
+
+		ess_common_speed (devc, &speed, &dummydiv);
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+/*
+ * FKS: This is a one-on-one copy of sb1_audio_set_bits
+ */
+static unsigned int ess_audio_set_bits(int dev, unsigned int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (bits != 0) {
+		if (bits == AFMT_U8 || bits == AFMT_S16_LE) {
+			devc->bits = bits;
+		} else {
+			devc->bits = AFMT_U8;
+		}
+	}
+
+	return devc->bits;
+}
+
+/*
+ * FKS: This is a one-on-one copy of sbpro_audio_set_channels
+ * (*) Modified it!!
+ */
+static short ess_audio_set_channels(int dev, short channels)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (channels == 1 || channels == 2) devc->channels = channels;
+
+	return devc->channels;
+}
+
+static struct audio_driver ess_audio_driver =   /* ESS ES688/1688 */
+{
+	.owner			= THIS_MODULE,
+	.open			= sb_audio_open,
+	.close			= sb_audio_close,
+	.output_block		= ess_set_output_parms,
+	.start_input		= ess_set_input_parms,
+	.prepare_for_input	= ess_audio_prepare_for_input,
+	.prepare_for_output	= ess_audio_prepare_for_output,
+	.halt_io		= ess_audio_halt_xfer,
+	.trigger		= ess_audio_trigger,
+	.set_speed		= ess_audio_set_speed,
+	.set_bits		= ess_audio_set_bits,
+	.set_channels		= ess_audio_set_channels
+};
+
+/*
+ * ess_audio_init must be called from sb_audio_init
+ */
+struct audio_driver *ess_audio_init
+		(sb_devc *devc, int *audio_flags, int *format_mask)
+{
+	*audio_flags = DMA_AUTOMODE;
+	*format_mask |= AFMT_S16_LE;
+
+	if (devc->duplex) {
+		int tmp_dma;
+		/*
+		 * sb_audio_init thinks dma8 is for playback and
+		 * dma16 is for record. Not now! So swap them.
+		 */
+		tmp_dma		= devc->dma16;
+		devc->dma16	= devc->dma8;
+		devc->dma8	= tmp_dma;
+
+		*audio_flags |= DMA_DUPLEX;
+	}
+
+	return &ess_audio_driver;
+}
+
+/****************************************************************************
+ *																			*
+ *								ESS common									*
+ *																			*
+ ****************************************************************************/
+static void ess_handle_channel
+	(char *channel, int dev, int intr_active, unsigned char flag, int irq_mode)
+{
+	if (!intr_active || !flag) return;
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode);
+#endif
+	switch (irq_mode) {
+		case IMODE_OUTPUT:
+			DMAbuf_outputintr (dev, 1);
+			break;
+
+		case IMODE_INPUT:
+			DMAbuf_inputintr (dev);
+			break;
+
+		case IMODE_INIT:
+			break;
+
+		default:;
+			/* printk(KERN_WARNING "ESS: Unexpected interrupt\n"); */
+	}
+}
+
+/*
+ * FKS: TODO!!! Finish this!
+ *
+ * I think midi stuff uses uart401, without interrupts.
+ * So IMODE_MIDI isn't a value for devc->irq_mode.
+ */
+void ess_intr (sb_devc *devc)
+{
+	int				status;
+	unsigned char	src;
+
+	if (devc->submodel == SUBMDL_ES1887) {
+		src = ess_getmixer (devc, 0x7f) >> 4;
+	} else {
+		src = 0xff;
+	}
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src);
+#endif
+	ess_handle_channel
+		( "Audio 1"
+		, devc->dev, devc->intr_active   , src & 0x01, devc->irq_mode   );
+	ess_handle_channel
+		( "Audio 2"
+		, devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16);
+	/*
+	 * Acknowledge interrupts
+	 */
+	if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) {
+		ess_chgmixer (devc, 0x7a, 0x80, 0x00);
+	}
+
+	if (src & 0x01) {
+		status = inb(DSP_DATA_AVAIL);
+	}
+}
+
+static void ess_extended (sb_devc * devc)
+{
+	/* Enable extended mode */
+
+	sb_dsp_command(devc, 0xc6);
+}
+
+static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
+{
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data);
+#endif
+	/* Write a byte to an extended mode register of ES1688 */
+
+	if (!sb_dsp_command(devc, reg))
+		return 0;
+
+	return sb_dsp_command(devc, data);
+}
+
+static int ess_read (sb_devc * devc, unsigned char reg)
+{
+	/* Read a byte from an extended mode register of ES1688 */
+
+	/* Read register command */
+	if (!sb_dsp_command(devc, 0xc0)) return -1;
+
+	if (!sb_dsp_command(devc, reg )) return -1;
+
+	return sb_dsp_get_byte(devc);
+}
+
+int ess_dsp_reset(sb_devc * devc)
+{
+	int loopc;
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: ess_dsp_reset 1\n");
+ess_show_mixerregs (devc);
+#endif
+
+	DEB(printk("Entered ess_dsp_reset()\n"));
+
+	outb(3, DSP_RESET); /* Reset FIFO too */
+
+	udelay(10);
+	outb(0, DSP_RESET);
+	udelay(30);
+
+	for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
+
+	if (inb(DSP_READ) != 0xAA) {
+		DDB(printk("sb: No response to RESET\n"));
+		return 0;   /* Sorry */
+	}
+	ess_extended (devc);
+
+	DEB(printk("sb_dsp_reset() OK\n"));
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: dsp_reset 2\n");
+ess_show_mixerregs (devc);
+#endif
+
+	return 1;
+}
+
+static int ess_irq_bits (int irq)
+{
+	switch (irq) {
+	case 2:
+	case 9:
+		return 0;
+
+	case 5:
+		return 1;
+
+	case 7:
+		return 2;
+
+	case 10:
+		return 3;
+
+	default:
+		printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq);
+		return -1;
+	}
+}
+
+/*
+ *	Set IRQ configuration register for all ESS models
+ */
+static int ess_common_set_irq_hw (sb_devc * devc)
+{
+	int irq_bits;
+
+	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0;
+
+	if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) {
+		printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * I wanna use modern ES1887 mixer irq handling. Funny is the
+ * fact that my BIOS wants the same. But suppose someone's BIOS
+ * doesn't do this!
+ * This is independent of duplex. If there's a 1887 this will
+ * prevent it from going into 1888 mode.
+ */
+static void ess_es1887_set_irq_hw (sb_devc * devc)
+{
+	int irq_bits;
+
+	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return;
+
+	ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1));
+}
+
+static int ess_set_irq_hw (sb_devc * devc)
+{
+	if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc);
+
+	return ess_common_set_irq_hw (devc);
+}
+
+#ifdef FKS_TEST
+
+/*
+ * FKS_test:
+ *	for ES1887: 00, 18, non wr bits: 0001 1000
+ *	for ES1868: 00, b8, non wr bits: 1011 1000
+ *	for ES1888: 00, f8, non wr bits: 1111 1000
+ *	for ES1688: 00, f8, non wr bits: 1111 1000
+ *	+   ES968
+ */
+
+static void FKS_test (sb_devc * devc)
+{
+	int val1, val2;
+	val1 = ess_getmixer (devc, 0x64);
+	ess_setmixer (devc, 0x64, ~val1);
+	val2 = ess_getmixer (devc, 0x64) ^ ~val1;
+	ess_setmixer (devc, 0x64, val1);
+	val1 ^= ess_getmixer (devc, 0x64);
+printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff));
+};
+#endif
+
+static unsigned int ess_identify (sb_devc * devc)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR);
+
+	udelay(20);
+	val  = inb(MIXER_DATA) << 8;
+	udelay(20);
+	val |= inb(MIXER_DATA);
+	udelay(20);
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	return val;
+}
+
+/*
+ * ESS technology describes a detection scheme in their docs. It involves
+ * fiddling with the bits in certain mixer registers. ess_probe is supposed
+ * to help.
+ *
+ * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but
+ * should be written 0 only. Check this.
+ */
+static int ess_probe (sb_devc * devc, int reg, int xorval)
+{
+	int  val1, val2, val3;
+
+	val1 = ess_getmixer (devc, reg);
+	val2 = val1 ^ xorval;
+	ess_setmixer (devc, reg, val2);
+	val3 = ess_getmixer (devc, reg);
+	ess_setmixer (devc, reg, val1);
+
+	return (val2 == val3);
+}
+
+int ess_init(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char cfg;
+	int ess_major = 0, ess_minor = 0;
+	int i;
+	static char name[100], modelname[10];
+
+	/*
+	 * Try to detect ESS chips.
+	 */
+
+	sb_dsp_command(devc, 0xe7); /* Return identification */
+
+	for (i = 1000; i; i--) {
+		if (inb(DSP_DATA_AVAIL) & 0x80) {
+			if (ess_major == 0) {
+				ess_major = inb(DSP_READ);
+			} else {
+				ess_minor = inb(DSP_READ);
+				break;
+			}
+		}
+	}
+
+	if (ess_major == 0) return 0;
+
+	if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+		sprintf(name, "ESS ES488 AudioDrive (rev %d)",
+			ess_minor & 0x0f);
+		hw_config->name = name;
+		devc->model = MDL_SBPRO;
+		return 1;
+	}
+
+	/*
+	 * This the detection heuristic of ESS technology, though somewhat
+	 * changed to actually make it work.
+	 * This results in the following detection steps:
+	 * - distinct between ES688 and ES1688+ (as always done in this driver)
+	 *   if ES688 we're ready
+	 * - try to detect ES1868, ES1869 or ES1878 (ess_identify)
+	 *   if successful we're ready
+	 * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887)
+	 *   if successful we're ready
+	 * - Dunno. Must be 1688. Will do in general
+	 *
+	 * This is the most BETA part of the software: Will the detection
+	 * always work?
+	 */
+	devc->model = MDL_ESS;
+	devc->submodel = ess_minor & 0x0f;
+
+	if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+		char *chip = NULL;
+		int submodel = -1;
+
+		switch (devc->sbmo.esstype) {
+		case ESSTYPE_DETECT:
+		case ESSTYPE_LIKE20:
+			break;
+		case 688:
+			submodel = 0x00;
+			break;
+		case 1688:
+			submodel = 0x08;
+			break;
+		case 1868:
+			submodel = SUBMDL_ES1868;
+			break;
+		case 1869:
+			submodel = SUBMDL_ES1869;
+			break;
+		case 1788:
+			submodel = SUBMDL_ES1788;
+			break;
+		case 1878:
+			submodel = SUBMDL_ES1878;
+			break;
+		case 1879:
+			submodel = SUBMDL_ES1879;
+			break;
+		case 1887:
+			submodel = SUBMDL_ES1887;
+			break;
+		case 1888:
+			submodel = SUBMDL_ES1888;
+			break;
+		default:
+			printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype);
+			return 0;
+		};
+		if (submodel != -1) {
+			devc->submodel = submodel;
+			sprintf (modelname, "ES%d", devc->sbmo.esstype);
+			chip = modelname;
+		};
+		if (chip == NULL && (ess_minor & 0x0f) < 8) {
+			chip = "ES688";
+		};
+#ifdef FKS_TEST
+FKS_test (devc);
+#endif
+		/*
+		 * If Nothing detected yet, and we want 2.0 behaviour...
+		 * Then let's assume it's ES1688.
+		 */
+		if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) {
+			chip = "ES1688";
+		};
+
+		if (chip == NULL) {
+			int type;
+
+			type = ess_identify (devc);
+
+			switch (type) {
+			case 0x1868:
+				chip = "ES1868";
+				devc->submodel = SUBMDL_ES1868;
+				break;
+			case 0x1869:
+				chip = "ES1869";
+				devc->submodel = SUBMDL_ES1869;
+				break;
+			case 0x1878:
+				chip = "ES1878";
+				devc->submodel = SUBMDL_ES1878;
+				break;
+			case 0x1879:
+				chip = "ES1879";
+				devc->submodel = SUBMDL_ES1879;
+				break;
+			default:
+				if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) {
+					printk ("ess_init: Unrecognized %04x\n", type);
+				}
+			};
+		};
+#if 0
+		/*
+		 * this one failed:
+		 * the probing of bit 4 is another thought: from ES1788 and up, all
+		 * chips seem to have hardware volume control. Bit 4 is readonly to
+		 * check if a hardware volume interrupt has fired.
+		 * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable
+		 * for these chips.
+		 */
+		if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) {
+#endif
+		/*
+		 * the probing of bit 2 is my idea. The ES1887 docs want me to probe
+		 * bit 3. This results in ES1688 being detected as ES1788.
+		 * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have
+		 * HardWare Volume, I think they don't have this IRQE.
+		 */
+		if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) {
+			if (ess_probe (devc, 0x70, 0x7f)) {
+				if (ess_probe (devc, 0x64, (1 << 5))) {
+					chip = "ES1887";
+					devc->submodel = SUBMDL_ES1887;
+				} else {
+					chip = "ES1888";
+					devc->submodel = SUBMDL_ES1888;
+				}
+			} else {
+				chip = "ES1788";
+				devc->submodel = SUBMDL_ES1788;
+			}
+		};
+		if (chip == NULL) {
+			chip = "ES1688";
+		};
+
+	    printk ( KERN_INFO "ESS chip %s %s%s\n"
+               , chip
+               , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20
+                 ? "detected"
+                 : "specified"
+                 )
+               , ( devc->sbmo.esstype == ESSTYPE_LIKE20
+                 ? " (kernel 2.0 compatible)"
+                 : ""
+                 )
+               );
+
+		sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f);
+	} else {
+		strcpy(name, "Jazz16");
+	}
+
+	/* AAS: info stolen from ALSA: these boards have different clocks */
+	switch(devc->submodel) {
+/* APPARENTLY NOT 1869 AND 1887
+		case SUBMDL_ES1869:
+		case SUBMDL_ES1887:
+*/		
+		case SUBMDL_ES1888:
+			devc->caps |= SB_CAP_ES18XX_RATE;
+			break;
+	}
+
+	hw_config->name = name;
+	/* FKS: sb_dsp_reset to enable extended mode???? */
+	sb_dsp_reset(devc); /* Turn on extended mode */
+
+	/*
+	 *  Enable joystick and OPL3
+	 */
+	cfg = ess_getmixer (devc, 0x40);
+	ess_setmixer (devc, 0x40, cfg | 0x03);
+	if (devc->submodel >= 8) {		/* ES1688 */
+		devc->caps |= SB_NO_MIDI;   /* ES1688 uses MPU401 MIDI mode */
+	}
+	sb_dsp_reset (devc);
+
+	/*
+	 * This is important! If it's not done, the IRQ probe in sb_dsp_init
+	 * may fail.
+	 */
+	return ess_set_irq_hw (devc);
+}
+
+static int ess_set_dma_hw(sb_devc * devc)
+{
+	unsigned char cfg, dma_bits = 0, dma16_bits;
+	int dma;
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n"
+, devc->dma8, devc->dma16, devc->duplex);
+#endif
+
+	/*
+	 * FKS: It seems as if this duplex flag isn't set yet. Check it.
+	 */
+	dma = devc->dma8;
+
+	if (dma > 3 || dma < 0 || dma == 2) {
+		dma_bits = 0;
+		printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma);
+		return 0;
+	} else {
+		/* Extended mode DMA enable */
+		cfg = 0x50;
+
+		if (dma == 3) {
+			dma_bits = 3;
+		} else {
+			dma_bits = dma + 1;
+		}
+	}
+
+	if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) {
+		printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
+		return 0;
+	}
+
+	if (devc->duplex) {
+		dma = devc->dma16;
+		dma16_bits = 0;
+
+		if (dma >= 0) {
+			switch (dma) {
+			case 0:
+				dma_bits = 0x04;
+				break;
+			case 1:
+				dma_bits = 0x05;
+				break;
+			case 3:
+				dma_bits = 0x06;
+				break;
+			case 5:
+				dma_bits   = 0x07;
+				dma16_bits = 0x20;
+				break;
+			default:
+				printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma);
+				return 0;
+			};
+			ess_chgmixer (devc, 0x78, 0x20, dma16_bits);
+			ess_chgmixer (devc, 0x7d, 0x07, dma_bits);
+		}
+	}
+	return 1;
+}
+
+/*
+ * This one is called from sb_dsp_init.
+ *
+ * Return values:
+ *  0: Failed
+ *  1: Succeeded or doesn't apply (not SUBMDL_ES1887)
+ */
+int ess_dsp_init (sb_devc *devc, struct address_info *hw_config)
+{
+	/*
+	 * Caller also checks this, but anyway
+	 */
+	if (devc->model != MDL_ESS) {
+		printk (KERN_INFO "ess_dsp_init for non ESS chip\n");
+		return 1;
+	}
+	/*
+	 * This for ES1887 to run Full Duplex. Actually ES1888
+	 * is allowed to do so too. I have no idea yet if this
+	 * will work for ES1888 however.
+	 *
+	 * For SB16 having both dma8 and dma16 means enable
+	 * Full Duplex. Let's try this for ES1887 too
+	 *
+	 */
+	if (devc->submodel == SUBMDL_ES1887) {
+		if (hw_config->dma2 != -1) {
+			devc->dma16 = hw_config->dma2;
+		}
+		/*
+		 * devc->duplex initialization is put here, cause
+		 * ess_set_dma_hw needs it.
+		 */
+		if (devc->dma8 != devc->dma16 && devc->dma16 != -1) {
+			devc->duplex = 1;
+		}
+	}
+	if (!ess_set_dma_hw (devc)) {
+		free_irq(devc->irq, devc);
+		return 0;
+	}
+	return 1;
+}
+
+/****************************************************************************
+ *																			*
+ *									ESS mixer								*
+ *																			*
+ ****************************************************************************/
+
+#define ES688_RECORDING_DEVICES	\
+			( SOUND_MASK_LINE	| SOUND_MASK_MIC	| SOUND_MASK_CD		)
+#define ES688_MIXER_DEVICES		\
+			( SOUND_MASK_SYNTH	| SOUND_MASK_PCM	| SOUND_MASK_LINE	\
+			| SOUND_MASK_MIC	| SOUND_MASK_CD		| SOUND_MASK_VOLUME	\
+			| SOUND_MASK_LINE2	| SOUND_MASK_SPEAKER					)
+
+#define ES1688_RECORDING_DEVICES	\
+			( ES688_RECORDING_DEVICES					)
+#define ES1688_MIXER_DEVICES		\
+			( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV	)
+
+#define ES1887_RECORDING_DEVICES	\
+			( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH)
+#define ES1887_MIXER_DEVICES		\
+			( ES1688_MIXER_DEVICES											)
+
+/*
+ * Mixer registers of ES1887
+ *
+ * These registers specifically take care of recording levels. To make the
+ * mapping from playback devices to recording devices every recording
+ * devices = playback device + ES_REC_MIXER_RECDIFF
+ */
+#define ES_REC_MIXER_RECBASE	(SOUND_MIXER_LINE3 + 1)
+#define ES_REC_MIXER_RECDIFF	(ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH)
+
+#define ES_REC_MIXER_RECSYNTH	(SOUND_MIXER_SYNTH	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECPCM		(SOUND_MIXER_PCM	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECSPEAKER	(SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE	(SOUND_MIXER_LINE	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECMIC		(SOUND_MIXER_MIC	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECCD		(SOUND_MIXER_CD		 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECIMIX	(SOUND_MIXER_IMIX	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECALTPCM	(SOUND_MIXER_ALTPCM	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECRECLEV	(SOUND_MIXER_RECLEV	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECIGAIN	(SOUND_MIXER_IGAIN	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECOGAIN	(SOUND_MIXER_OGAIN	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE1	(SOUND_MIXER_LINE1	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE2	(SOUND_MIXER_LINE2	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE3	(SOUND_MIXER_LINE3	 + ES_REC_MIXER_RECDIFF)
+
+static mixer_tab es688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x32, 7, 4, 0x32, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * The ES1688 specifics... hopefully correct...
+ * - 6 bit master volume
+ *   I was wrong, ES1888 docs say ES1688 didn't have it.
+ * - RECLEV control
+ * These may apply to ES688 too. I have no idea.
+ */
+static mixer_tab es1688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x32, 7, 4, 0x32, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+static mixer_tab es1688later_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * This one is for all ESS chips with a record mixer.
+ * It's not used (yet) however
+ */
+static mixer_tab es_rec_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSYNTH,		0x6b, 7, 4, 0x6b, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE,		0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECMIC,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECCD,			0x6a, 7, 4, 0x6a, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECIMIX,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECALTPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECRECLEV,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECIGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECOGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE1,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE2,		0x6c, 7, 4, 0x6c, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECLINE3,		0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * This one is for ES1887. It's little different from es_rec_mix: it
+ * has 0x7c for PCM playback level. This is because ES1887 uses
+ * Audio 2 for playback.
+ */
+static mixer_tab es1887_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x7c, 7, 4, 0x7c, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSYNTH,		0x6b, 7, 4, 0x6b, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE,		0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECMIC,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECCD,			0x6a, 7, 4, 0x6a, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECIMIX,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECALTPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECRECLEV,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECIGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECOGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE1,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE2,		0x6c, 7, 4, 0x6c, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECLINE3,		0x00, 0, 0, 0x00, 0, 0)
+};
+
+static int ess_has_rec_mixer (int submodel)
+{
+	switch (submodel) {
+	case SUBMDL_ES1887:
+		return 1;
+	default:
+		return 0;
+	};
+};
+
+#ifdef FKS_LOGGING
+static int ess_mixer_mon_regs[]
+	= { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f
+	  , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9
+	  , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9
+	  , 0x00};
+
+static void ess_show_mixerregs (sb_devc *devc)
+{
+	int *mp = ess_mixer_mon_regs;
+
+return;
+
+	while (*mp != 0) {
+		printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp)));
+		mp++;
+	}
+}
+#endif
+
+void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value)
+{
+	unsigned long flags;
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value);
+#endif
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (port >= 0xa0) {
+		ess_write (devc, port, value);
+	} else {
+		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+		udelay(20);
+		outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+		udelay(20);
+	};
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+unsigned int ess_getmixer (sb_devc * devc, unsigned int port)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	if (port >= 0xa0) {
+		val = ess_read (devc, port);
+	} else {
+		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+		udelay(20);
+		val = inb(MIXER_DATA);
+		udelay(20);
+	}
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	return val;
+}
+
+static void ess_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = ess_getmixer (devc, reg);
+	value = (value & ~mask) | (val & mask);
+	ess_setmixer (devc, reg, value);
+}
+
+/*
+ * ess_mixer_init must be called from sb_mixer_init
+ */
+void ess_mixer_init (sb_devc * devc)
+{
+	devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+
+	/*
+	* Take care of ES1887 specifics...
+	*/
+	switch (devc->submodel) {
+	case SUBMDL_ES1887:
+		devc->supported_devices		= ES1887_MIXER_DEVICES;
+		devc->supported_rec_devices	= ES1887_RECORDING_DEVICES;
+#ifdef FKS_LOGGING
+printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex);
+#endif
+		if (devc->duplex) {
+			devc->iomap				= &es1887_mix;
+			devc->iomap_sz                          = ARRAY_SIZE(es1887_mix);
+		} else {
+			devc->iomap				= &es_rec_mix;
+			devc->iomap_sz                          = ARRAY_SIZE(es_rec_mix);
+		}
+		break;
+	default:
+		if (devc->submodel < 8) {
+			devc->supported_devices		= ES688_MIXER_DEVICES;
+			devc->supported_rec_devices	= ES688_RECORDING_DEVICES;
+			devc->iomap					= &es688_mix;
+			devc->iomap_sz                                  = ARRAY_SIZE(es688_mix);
+		} else {
+			/*
+			 * es1688 has 4 bits master vol.
+			 * later chips have 6 bits (?)
+			 */
+			devc->supported_devices		= ES1688_MIXER_DEVICES;
+			devc->supported_rec_devices	= ES1688_RECORDING_DEVICES;
+			if (devc->submodel < 0x10) {
+				devc->iomap				= &es1688_mix;
+				devc->iomap_sz                          = ARRAY_SIZE(es688_mix);
+			} else {
+				devc->iomap				= &es1688later_mix;
+				devc->iomap_sz                          = ARRAY_SIZE(es1688later_mix);
+			}
+		}
+	}
+}
+
+/*
+ * Changing playback levels at an ESS chip with record mixer means having to
+ * take care of recording levels of recorded inputs (devc->recmask) too!
+ */
+int ess_mixer_set(sb_devc *devc, int dev, int left, int right)
+{
+	if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) {
+		sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right);
+	}
+	return sb_common_mixer_set (devc, dev, left, right);
+}
+
+/*
+ * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After
+ * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload
+ * helps.
+ */
+void ess_mixer_reload (sb_devc *devc, int dev)
+{
+	int left, right, value;
+
+	value = devc->levels[dev];
+	left  = value & 0x000000ff;
+	right = (value & 0x0000ff00) >> 8;
+
+	sb_common_mixer_set(devc, dev, left, right);
+}
+
+static int es_rec_set_recmask(sb_devc * devc, int mask)
+{
+	int i, i_mask, cur_mask, diff_mask;
+	int value, left, right;
+
+#ifdef FKS_LOGGING
+printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask);
+#endif
+	/*
+	 * Changing the recmask on an ESS chip with recording mixer means:
+	 * (1) Find the differences
+	 * (2) For "turned-on"  inputs: make the recording level the playback level
+	 * (3) For "turned-off" inputs: make the recording level zero
+	 */
+	cur_mask  = devc->recmask;
+	diff_mask = (cur_mask ^ mask);
+
+	for (i = 0; i < 32; i++) {
+		i_mask = (1 << i);
+		if (diff_mask & i_mask) {	/* Difference? (1)  */
+			if (mask & i_mask) {	/* Turn it on  (2)  */
+				value = devc->levels[i];
+				left  = value & 0x000000ff;
+				right = (value & 0x0000ff00) >> 8;
+			} else {				/* Turn it off (3)  */
+				left  = 0;
+				right = 0;
+			}
+			sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right);
+		}
+	}
+	return mask;
+}
+
+int ess_set_recmask(sb_devc * devc, int *mask)
+{
+	/* This applies to ESS chips with record mixers only! */
+
+	if (ess_has_rec_mixer (devc->submodel)) {
+		*mask	= es_rec_set_recmask (devc, *mask);
+		return 1;									/* Applied		*/
+	} else {
+		return 0;									/* Not applied	*/
+	}
+}
+
+/*
+ * ess_mixer_reset must be called from sb_mixer_reset
+ */
+int ess_mixer_reset (sb_devc * devc)
+{
+	/*
+	 * Separate actions for ESS chips with a record mixer:
+	 */
+	if (ess_has_rec_mixer (devc->submodel)) {
+		switch (devc->submodel) {
+		case SUBMDL_ES1887:
+			/*
+			 * Separate actions for ES1887:
+			 * Change registers 7a and 1c to make the record mixer the
+			 * actual recording source.
+			 */
+			ess_chgmixer(devc, 0x7a, 0x18, 0x08);
+			ess_chgmixer(devc, 0x1c, 0x07, 0x07);
+			break;
+		};
+		/*
+		 * Call set_recmask for proper initialization
+		 */
+		devc->recmask = devc->supported_rec_devices;
+		es_rec_set_recmask(devc, 0);
+		devc->recmask = 0;
+
+		return 1;	/* We took care of recmask.				*/
+	} else {
+		return 0;	/* We didn't take care; caller do it	*/
+	}
+}
+
+/****************************************************************************
+ *																			*
+ *								ESS midi									*
+ *																			*
+ ****************************************************************************/
+
+/*
+ * FKS: IRQ may be shared. Hm. And if so? Then What?
+ */
+int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char   cfg, tmp;
+
+	cfg = ess_getmixer (devc, 0x40) & 0x03;
+
+	if (devc->submodel < 8) {
+		ess_setmixer (devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
+		return 0;  					 /* ES688 doesn't support MPU401 mode */
+	}
+	tmp = (hw_config->io_base & 0x0f0) >> 4;
+
+	if (tmp > 3) {
+		ess_setmixer (devc, 0x40, cfg);
+		return 0;
+	}
+	cfg |= tmp << 3;
+
+	tmp = 1;		/* MPU enabled without interrupts */
+
+	/* May be shared: if so the value is -ve */
+
+	switch (abs(hw_config->irq)) {
+		case 9:
+			tmp = 0x4;
+			break;
+		case 5:
+			tmp = 0x5;
+			break;
+		case 7:
+			tmp = 0x6;
+			break;
+		case 10:
+			tmp = 0x7;
+			break;
+		default:
+			return 0;
+	}
+
+	cfg |= tmp << 5;
+	ess_setmixer (devc, 0x40, cfg | 0x03);
+
+	return 1;
+}
+
diff --git a/sound/oss/sb_ess.h b/sound/oss/sb_ess.h
new file mode 100644
index 00000000..38aa072e
--- /dev/null
+++ b/sound/oss/sb_ess.h
@@ -0,0 +1,34 @@
+/*
+ * Created: 9-Jan-1999 Rolf Fokkens
+ */
+
+extern void ess_intr
+		(sb_devc *devc);
+extern int ess_dsp_init
+		(sb_devc *devc, struct address_info *hw_config);
+
+extern struct audio_driver *ess_audio_init
+		(sb_devc *devc, int *audio_flags, int *format_mask);
+extern int ess_midi_init
+		(sb_devc *devc, struct address_info *hw_config);
+extern void ess_mixer_init
+		(sb_devc *devc);
+
+extern int ess_init
+		(sb_devc *devc, struct address_info *hw_config);
+extern int ess_dsp_reset
+		(sb_devc *devc);
+
+extern void ess_setmixer
+		(sb_devc *devc, unsigned int port, unsigned int value);
+extern unsigned int ess_getmixer
+		(sb_devc *devc, unsigned int port);
+extern int ess_mixer_set
+		(sb_devc *devc, int dev, int left, int right);
+extern int ess_mixer_reset
+		(sb_devc *devc);
+extern void ess_mixer_reload
+		(sb_devc * devc, int dev);
+extern int ess_set_recmask
+		(sb_devc *devc, int *mask);
+
diff --git a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c
new file mode 100644
index 00000000..f139028e
--- /dev/null
+++ b/sound/oss/sb_midi.c
@@ -0,0 +1,206 @@
+/*
+ * sound/oss/sb_midi.c
+ *
+ * The low level driver for the Sound Blaster DS chips.
+ *
+ *
+ * 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/spinlock.h>
+#include <linux/slab.h>
+
+#include "sound_config.h"
+
+#include "sb.h"
+#undef SB_TEST_IRQ
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+
+static int sb_midi_open(int dev, int mode,
+	     void            (*input) (int dev, unsigned char data),
+	     void            (*output) (int dev)
+)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return -ENXIO;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (devc->opened)
+	{
+		spin_unlock_irqrestore(&devc->lock, flags);
+		return -EBUSY;
+	}
+	devc->opened = 1;
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->irq_mode = IMODE_MIDI;
+	devc->midi_broken = 0;
+
+	sb_dsp_reset(devc);
+
+	if (!sb_dsp_command(devc, 0x35))	/* Start MIDI UART mode */
+	{
+		  devc->opened = 0;
+		  return -EIO;
+	}
+	devc->intr_active = 1;
+
+	if (mode & OPEN_READ)
+	{
+		devc->input_opened = 1;
+		devc->midi_input_intr = input;
+	}
+	return 0;
+}
+
+static void sb_midi_close(int dev)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	sb_dsp_reset(devc);
+	devc->intr_active = 0;
+	devc->input_opened = 0;
+	devc->opened = 0;
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+static int sb_midi_out(int dev, unsigned char midi_byte)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+
+	if (devc == NULL)
+		return 1;
+
+	if (devc->midi_broken)
+		return 1;
+
+	if (!sb_dsp_command(devc, midi_byte))
+	{
+		devc->midi_broken = 1;
+		return 1;
+	}
+	return 1;
+}
+
+static int sb_midi_start_read(int dev)
+{
+	return 0;
+}
+
+static int sb_midi_end_read(int dev)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+
+	if (devc == NULL)
+		return -ENXIO;
+
+	sb_dsp_reset(devc);
+	devc->intr_active = 0;
+	return 0;
+}
+
+static int sb_midi_ioctl(int dev, unsigned cmd, void __user *arg)
+{
+        return -EINVAL;
+}
+
+void sb_midi_interrupt(sb_devc * devc)
+{
+	unsigned long   flags;
+	unsigned char   data;
+
+	if (devc == NULL)
+		return;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	data = inb(DSP_READ);
+	if (devc->input_opened)
+		devc->midi_input_intr(devc->my_mididev, data);
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+#define MIDI_SYNTH_NAME	"Sound Blaster Midi"
+#define MIDI_SYNTH_CAPS	0
+#include "midi_synth.h"
+
+static struct midi_operations sb_midi_operations =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"Sound Blaster", 0, 0, SNDCARD_SB},
+	.converter	= &std_midi_synth,
+	.in_info	= {0},
+	.open		= sb_midi_open,
+	.close		= sb_midi_close,
+	.ioctl		= sb_midi_ioctl,
+	.outputc	= sb_midi_out,
+	.start_read	= sb_midi_start_read,
+	.end_read	= sb_midi_end_read,
+};
+
+void sb_dsp_midi_init(sb_devc * devc, struct module *owner)
+{
+	int dev;
+
+	if (devc->model < 2)	/* No MIDI support for SB 1.x */
+		return;
+
+	dev = sound_alloc_mididev();
+
+	if (dev == -1)
+	{
+		printk(KERN_ERR "sb_midi: too many MIDI devices detected\n");
+		return;
+	}
+	std_midi_synth.midi_dev = devc->my_mididev = dev;
+	midi_devs[dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
+	if (midi_devs[dev] == NULL)
+	{
+		printk(KERN_WARNING "Sound Blaster:  failed to allocate MIDI memory.\n");
+		sound_unload_mididev(dev);
+		  return;
+	}
+	memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
+	       sizeof(struct midi_operations));
+
+	if (owner)
+			midi_devs[dev]->owner = owner;
+	
+	midi_devs[dev]->devc = devc;
+
+
+	midi_devs[dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
+	if (midi_devs[dev]->converter == NULL)
+	{
+		  printk(KERN_WARNING "Sound Blaster:  failed to allocate MIDI memory.\n");
+		  kfree(midi_devs[dev]);
+		  sound_unload_mididev(dev);
+		  return;
+	}
+	memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth,
+	       sizeof(struct synth_operations));
+
+	midi_devs[dev]->converter->id = "SBMIDI";
+	sequencer_init();
+}
diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c
new file mode 100644
index 00000000..f8f3b7a6
--- /dev/null
+++ b/sound/oss/sb_mixer.c
@@ -0,0 +1,770 @@
+/*
+ * sound/oss/sb_mixer.c
+ *
+ * The low level mixer driver for the Sound Blaster compatible cards.
+ */
+/*
+ * 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)
+ * Rolf Fokkens (Dec 20 1998)	: Moved ESS stuff into sb_ess.[ch]
+ * Stanislav Voronyi <stas@esc.kharkov.com>	: Support for AWE 3DSE device (Jun 7 1999)
+ */
+
+#include <linux/slab.h>
+
+#include "sound_config.h"
+
+#define __SB_MIXER_C__
+
+#include "sb.h"
+#include "sb_mixer.h"
+
+#include "sb_ess.h"
+
+#define SBPRO_RECORDING_DEVICES	(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
+#define SBPRO_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
+ * channel is the COVOX/DisneySoundSource emulation volume control
+ * on the mixer. It does NOT control speaker volume. Should have own
+ * mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES	(SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
+				 SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
+
+#define SB16_RECORDING_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD)
+
+#define SB16_OUTFILTER_DEVICES		(SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD)
+
+#define SB16_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | \
+					 SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
+					 SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
+					SOUND_MASK_IMIX)
+
+/* These are the only devices that are working at the moment.  Others could
+ * be added once they are identified and a method is found to control them.
+ */
+#define ALS007_MIXER_DEVICES	(SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
+				 SOUND_MASK_PCM | SOUND_MASK_MIC | \
+				 SOUND_MASK_CD | \
+				 SOUND_MASK_VOLUME)
+
+static mixer_tab sbpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+static mixer_tab sb16_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x30, 7, 5, 0x31, 7, 5),
+MIX_ENT(SOUND_MIXER_BASS,	0x46, 7, 4, 0x47, 7, 4),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 7, 4, 0x45, 7, 4),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x34, 7, 5, 0x35, 7, 5),
+MIX_ENT(SOUND_MIXER_PCM,	0x32, 7, 5, 0x33, 7, 5),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x3b, 7, 2, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x38, 7, 5, 0x39, 7, 5),
+MIX_ENT(SOUND_MIXER_MIC,	0x3a, 7, 5, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x36, 7, 5, 0x37, 7, 5),
+MIX_ENT(SOUND_MIXER_IMIX,	0x3c, 0, 1, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
+MIX_ENT(SOUND_MIXER_IGAIN,	0x3f, 7, 2, 0x40, 7, 2),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x41, 7, 2, 0x42, 7, 2)
+};
+
+static mixer_tab als007_mix = 
+{
+MIX_ENT(SOUND_MIXER_VOLUME,	0x62, 7, 4, 0x62, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x66, 7, 4, 0x66, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x64, 7, 4, 0x64, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x6a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */
+MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+
+/* SM_GAMES          Master volume is lower and PCM & FM volumes
+			     higher than with SB Pro. This improves the
+			     sound quality */
+
+static int smg_default_levels[32] =
+{
+  0x2020,			/* Master Volume */
+  0x4b4b,			/* Bass */
+  0x4b4b,			/* Treble */
+  0x6464,			/* FM */
+  0x6464,			/* PCM */
+  0x4b4b,			/* PC Speaker */
+  0x4b4b,			/* Ext Line */
+  0x0000,			/* Mic */
+  0x4b4b,			/* CD */
+  0x4b4b,			/* Recording monitor */
+  0x4b4b,			/* SB PCM */
+  0x4b4b,			/* Recording level */
+  0x4b4b,			/* Input gain */
+  0x4b4b,			/* Output gain */
+  0x4040,			/* Line1 */
+  0x4040,			/* Line2 */
+  0x1515			/* Line3 */
+};
+
+static int sb_default_levels[32] =
+{
+  0x5a5a,			/* Master Volume */
+  0x4b4b,			/* Bass */
+  0x4b4b,			/* Treble */
+  0x4b4b,			/* FM */
+  0x4b4b,			/* PCM */
+  0x4b4b,			/* PC Speaker */
+  0x4b4b,			/* Ext Line */
+  0x1010,			/* Mic */
+  0x4b4b,			/* CD */
+  0x0000,			/* Recording monitor */
+  0x4b4b,			/* SB PCM */
+  0x4b4b,			/* Recording level */
+  0x4b4b,			/* Input gain */
+  0x4b4b,			/* Output gain */
+  0x4040,			/* Line1 */
+  0x4040,			/* Line2 */
+  0x1515			/* Line3 */
+};
+
+static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
+{
+	0x00,	/* SOUND_MIXER_VOLUME	*/
+	0x00,	/* SOUND_MIXER_BASS	*/
+	0x00,	/* SOUND_MIXER_TREBLE	*/
+	0x40,	/* SOUND_MIXER_SYNTH	*/
+	0x00,	/* SOUND_MIXER_PCM	*/
+	0x00,	/* SOUND_MIXER_SPEAKER	*/
+	0x10,	/* SOUND_MIXER_LINE	*/
+	0x01,	/* SOUND_MIXER_MIC	*/
+	0x04,	/* SOUND_MIXER_CD	*/
+	0x00,	/* SOUND_MIXER_IMIX	*/
+	0x00,	/* SOUND_MIXER_ALTPCM	*/
+	0x00,	/* SOUND_MIXER_RECLEV	*/
+	0x00,	/* SOUND_MIXER_IGAIN	*/
+	0x00	/* SOUND_MIXER_OGAIN	*/
+};
+
+static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
+{
+	0x00,	/* SOUND_MIXER_VOLUME	*/
+	0x00,	/* SOUND_MIXER_BASS	*/
+	0x00,	/* SOUND_MIXER_TREBLE	*/
+	0x20,	/* SOUND_MIXER_SYNTH	*/
+	0x00,	/* SOUND_MIXER_PCM	*/
+	0x00,	/* SOUND_MIXER_SPEAKER	*/
+	0x08,	/* SOUND_MIXER_LINE	*/
+	0x01,	/* SOUND_MIXER_MIC	*/
+	0x02,	/* SOUND_MIXER_CD	*/
+	0x00,	/* SOUND_MIXER_IMIX	*/
+	0x00,	/* SOUND_MIXER_ALTPCM	*/
+	0x00,	/* SOUND_MIXER_RECLEV	*/
+	0x00,	/* SOUND_MIXER_IGAIN	*/
+	0x00	/* SOUND_MIXER_OGAIN	*/
+};
+
+static char     smw_mix_regs[] =	/* Left mixer registers */
+{
+  0x0b,				/* SOUND_MIXER_VOLUME */
+  0x0d,				/* SOUND_MIXER_BASS */
+  0x0d,				/* SOUND_MIXER_TREBLE */
+  0x05,				/* SOUND_MIXER_SYNTH */
+  0x09,				/* SOUND_MIXER_PCM */
+  0x00,				/* SOUND_MIXER_SPEAKER */
+  0x03,				/* SOUND_MIXER_LINE */
+  0x01,				/* SOUND_MIXER_MIC */
+  0x07,				/* SOUND_MIXER_CD */
+  0x00,				/* SOUND_MIXER_IMIX */
+  0x00,				/* SOUND_MIXER_ALTPCM */
+  0x00,				/* SOUND_MIXER_RECLEV */
+  0x00,				/* SOUND_MIXER_IGAIN */
+  0x00,				/* SOUND_MIXER_OGAIN */
+  0x00,				/* SOUND_MIXER_LINE1 */
+  0x00,				/* SOUND_MIXER_LINE2 */
+  0x00				/* SOUND_MIXER_LINE3 */
+};
+
+static int      sbmixnum = 1;
+
+static void     sb_mixer_reset(sb_devc * devc);
+
+void sb_mixer_set_stereo(sb_devc * devc, int mode)
+{
+	sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
+}
+
+static int detect_mixer(sb_devc * devc)
+{
+	/* Just trust the mixer is there */
+	return 1;
+}
+
+static void oss_change_bits(sb_devc *devc, unsigned char *regval, int dev, int chn, int newval)
+{
+	unsigned char mask;
+	int shift;
+
+	mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
+	newval = (int) ((newval * mask) + 50) / 100;	/* Scale */
+
+	shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
+
+	*regval &= ~(mask << shift);	/* Mask out previous value */
+	*regval |= (newval & mask) << shift;	/* Set the new value */
+}
+
+static int sb_mixer_get(sb_devc * devc, int dev)
+{
+	if (!((1 << dev) & devc->supported_devices))
+		return -EINVAL;
+	return devc->levels[dev];
+}
+
+void smw_mixer_init(sb_devc * devc)
+{
+	int i;
+
+	sb_setmixer(devc, 0x00, 0x18);	/* Mute unused (Telephone) line */
+	sb_setmixer(devc, 0x10, 0x38);	/* Config register 2 */
+
+	devc->supported_devices = 0;
+	for (i = 0; i < sizeof(smw_mix_regs); i++)
+		if (smw_mix_regs[i] != 0)
+			devc->supported_devices |= (1 << i);
+
+	devc->supported_rec_devices = devc->supported_devices &
+		~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME);
+	sb_mixer_reset(devc);
+}
+
+int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right)
+{
+	int regoffs;
+	unsigned char val;
+
+	if ((dev < 0) || (dev >= devc->iomap_sz))
+		return -EINVAL;
+
+	regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
+
+	if (regoffs == 0)
+		return -EINVAL;
+
+	val = sb_getmixer(devc, regoffs);
+	oss_change_bits(devc, &val, dev, LEFT_CHN, left);
+
+	if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs)	/*
+								 * Change register
+								 */
+	{
+		sb_setmixer(devc, regoffs, val);	/*
+							 * Save the old one
+							 */
+		regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
+
+		if (regoffs == 0)
+			return left | (left << 8);	/*
+							 * Just left channel present
+							 */
+
+		val = sb_getmixer(devc, regoffs);	/*
+							 * Read the new one
+							 */
+	}
+	oss_change_bits(devc, &val, dev, RIGHT_CHN, right);
+
+	sb_setmixer(devc, regoffs, val);
+
+	return left | (right << 8);
+}
+
+static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
+{
+	int reg, val;
+
+	switch (dev)
+	{
+		case SOUND_MIXER_VOLUME:
+			sb_setmixer(devc, 0x0b, 96 - (96 * left / 100));	/* 96=mute, 0=max */
+			sb_setmixer(devc, 0x0c, 96 - (96 * right / 100));
+			break;
+
+		case SOUND_MIXER_BASS:
+		case SOUND_MIXER_TREBLE:
+			devc->levels[dev] = left | (right << 8);
+			/* Set left bass and treble values */
+			val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
+			val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
+			sb_setmixer(devc, 0x0d, val);
+
+			/* Set right bass and treble values */
+			val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
+			val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
+			sb_setmixer(devc, 0x0e, val);
+		
+			break;
+
+		default:
+			/* bounds check */
+			if (dev < 0 || dev >= ARRAY_SIZE(smw_mix_regs))
+				return -EINVAL;
+			reg = smw_mix_regs[dev];
+			if (reg == 0)
+				return -EINVAL;
+			sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20);	/* 24=mute, 0=max */
+			sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
+	}
+
+	devc->levels[dev] = left | (right << 8);
+	return left | (right << 8);
+}
+
+static int sb_mixer_set(sb_devc * devc, int dev, int value)
+{
+	int left = value & 0x000000ff;
+	int right = (value & 0x0000ff00) >> 8;
+	int retval;
+
+	if (left > 100)
+		left = 100;
+	if (right > 100)
+		right = 100;
+
+	if ((dev < 0) || (dev > 31))
+		return -EINVAL;
+
+	if (!(devc->supported_devices & (1 << dev)))	/*
+							 * Not supported
+							 */
+		return -EINVAL;
+
+	/* Differentiate depending on the chipsets */
+	switch (devc->model) {
+	case MDL_SMW:
+		retval = smw_mixer_set(devc, dev, left, right);
+		break;
+	case MDL_ESS:
+		retval = ess_mixer_set(devc, dev, left, right);
+		break;
+	default:
+		retval = sb_common_mixer_set(devc, dev, left, right);
+	}
+	if (retval >= 0) devc->levels[dev] = retval;
+
+	return retval;
+}
+
+/*
+ * set_recsrc doesn't apply to ES188x
+ */
+static void set_recsrc(sb_devc * devc, int src)
+{
+	sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
+}
+
+static int set_recmask(sb_devc * devc, int mask)
+{
+	int devmask, i;
+	unsigned char  regimageL, regimageR;
+
+	devmask = mask & devc->supported_rec_devices;
+
+	switch (devc->model)
+	{
+		case MDL_SBPRO:
+		case MDL_ESS:
+		case MDL_JAZZ:
+		case MDL_SMW:
+			if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
+				break;
+			};
+			if (devmask != SOUND_MASK_MIC &&
+				devmask != SOUND_MASK_LINE &&
+				devmask != SOUND_MASK_CD)
+			{
+				/*
+				 * More than one device selected. Drop the
+				 * previous selection
+				 */
+				devmask &= ~devc->recmask;
+			}
+			if (devmask != SOUND_MASK_MIC &&
+				devmask != SOUND_MASK_LINE &&
+				devmask != SOUND_MASK_CD)
+			{
+				/*
+				 * More than one device selected. Default to
+				 * mic
+				 */
+				devmask = SOUND_MASK_MIC;
+			}
+			if (devmask ^ devc->recmask)	/*
+							 *	Input source changed
+							 */
+			{
+				switch (devmask)
+				{
+					case SOUND_MASK_MIC:
+						set_recsrc(devc, SRC__MIC);
+						break;
+
+					case SOUND_MASK_LINE:
+						set_recsrc(devc, SRC__LINE);
+						break;
+
+					case SOUND_MASK_CD:
+						set_recsrc(devc, SRC__CD);
+						break;
+
+					default:
+						set_recsrc(devc, SRC__MIC);
+				}
+			}
+			break;
+
+		case MDL_SB16:
+			if (!devmask)
+				devmask = SOUND_MASK_MIC;
+
+			if (devc->submodel == SUBMDL_ALS007) 
+			{
+				switch (devmask) 
+				{
+					case SOUND_MASK_LINE:
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE);
+						break;
+					case SOUND_MASK_CD:
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD);
+						break;
+					case SOUND_MASK_SYNTH:
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH);
+						break;
+					default:           /* Also takes care of SOUND_MASK_MIC case */
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC);
+						break;
+				}
+			}
+			else
+			{
+				regimageL = regimageR = 0;
+				for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				{
+					if ((1 << i) & devmask)
+					{
+						regimageL |= sb16_recmasks_L[i];
+						regimageR |= sb16_recmasks_R[i];
+					}
+					sb_setmixer (devc, SB16_IMASK_L, regimageL);
+					sb_setmixer (devc, SB16_IMASK_R, regimageR);
+				}
+			}
+			break;
+	}
+	devc->recmask = devmask;
+	return devc->recmask;
+}
+
+static int set_outmask(sb_devc * devc, int mask)
+{
+	int devmask, i;
+	unsigned char  regimage;
+
+	devmask = mask & devc->supported_out_devices;
+
+	switch (devc->model)
+	{
+		case MDL_SB16:
+			if (devc->submodel == SUBMDL_ALS007) 
+				break;
+			else
+			{
+				regimage = 0;
+				for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				{
+					if ((1 << i) & devmask)
+					{
+						regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]);
+					}
+					sb_setmixer (devc, SB16_OMASK, regimage);
+				}
+			}
+			break;
+		default:
+			break;
+	}
+
+	devc->outmask = devmask;
+	return devc->outmask;
+}
+
+static int sb_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	sb_devc *devc = mixer_devs[dev]->devc;
+	int val, ret;
+	int __user *p = arg;
+
+	/*
+	 * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1).
+	 * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1)
+	 *					      or mode==2 put 3DSE state to mode.
+	 */
+	if (devc->model == MDL_SB16) {
+		if (cmd == SOUND_MIXER_AGC) 
+		{
+			if (get_user(val, p))
+				return -EFAULT;
+			sb_setmixer(devc, 0x43, (~val) & 0x01);
+			return 0;
+		}
+		if (cmd == SOUND_MIXER_3DSE) 
+		{
+			/* I put here 15, but I don't know the exact version.
+			   At least my 4.13 havn't 3DSE, 4.16 has it. */
+			if (devc->minor < 15)
+				return -EINVAL;
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val == 0 || val == 1)
+				sb_chgmixer(devc, AWE_3DSE, 0x01, val);
+			else if (val == 2)
+			{
+				ret = sb_getmixer(devc, AWE_3DSE)&0x01;
+				return put_user(ret, p);
+			}
+			else
+				return -EINVAL;
+			return 0;
+		}
+	}
+	if (((cmd >> 8) & 0xff) == 'M') 
+	{
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) 
+		{
+			if (get_user(val, p))
+				return -EFAULT;
+			switch (cmd & 0xff) 
+			{
+				case SOUND_MIXER_RECSRC:
+					ret = set_recmask(devc, val);
+					break;
+
+				case SOUND_MIXER_OUTSRC:
+					ret = set_outmask(devc, val);
+					break;
+
+				default:
+					ret = sb_mixer_set(devc, cmd & 0xff, val);
+			}
+		}
+		else switch (cmd & 0xff) 
+		{
+			case SOUND_MIXER_RECSRC:
+				ret = devc->recmask;
+				break;
+				  
+			case SOUND_MIXER_OUTSRC:
+				ret = devc->outmask;
+				break;
+				  
+			case SOUND_MIXER_DEVMASK:
+				ret = devc->supported_devices;
+				break;
+				  
+			case SOUND_MIXER_STEREODEVS:
+				ret = devc->supported_devices;
+				/* The ESS seems to have stereo mic controls */
+				if (devc->model == MDL_ESS)
+					ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX);
+				else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
+					ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+				break;
+				  
+			case SOUND_MIXER_RECMASK:
+				ret = devc->supported_rec_devices;
+				break;
+				  
+			case SOUND_MIXER_OUTMASK:
+				ret = devc->supported_out_devices;
+				break;
+				  
+			case SOUND_MIXER_CAPS:
+				ret = devc->mixer_caps;
+				break;
+				    
+			default:
+				ret = sb_mixer_get(devc, cmd & 0xff);
+				break;
+		}
+		return put_user(ret, p); 
+	} else
+		return -EINVAL;
+}
+
+static struct mixer_operations sb_mixer_operations =
+{
+	.owner	= THIS_MODULE,
+	.id	= "SB",
+	.name	= "Sound Blaster",
+	.ioctl	= sb_mixer_ioctl
+};
+
+static struct mixer_operations als007_mixer_operations =
+{
+	.owner	= THIS_MODULE,
+	.id	= "ALS007",
+	.name	= "Avance ALS-007",
+	.ioctl	= sb_mixer_ioctl
+};
+
+static void sb_mixer_reset(sb_devc * devc)
+{
+	char name[32];
+	int i;
+
+	sprintf(name, "SB_%d", devc->sbmixnum);
+
+	if (devc->sbmo.sm_games)
+		devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
+	else
+		devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		sb_mixer_set(devc, i, devc->levels[i]);
+
+	if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
+		set_recmask(devc, SOUND_MASK_MIC);
+	};
+}
+
+int sb_mixer_init(sb_devc * devc, struct module *owner)
+{
+	int mixer_type = 0;
+	int m;
+
+	devc->sbmixnum = sbmixnum++;
+	devc->levels = NULL;
+
+	sb_setmixer(devc, 0x00, 0);	/* Reset mixer */
+
+	if (!(mixer_type = detect_mixer(devc)))
+		return 0;	/* No mixer. Why? */
+
+	switch (devc->model)
+	{
+		case MDL_ESSPCI:
+		case MDL_YMPCI:
+		case MDL_SBPRO:
+		case MDL_AZTECH:
+		case MDL_JAZZ:
+			devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+			devc->supported_devices = SBPRO_MIXER_DEVICES;
+			devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
+			devc->iomap = &sbpro_mix;
+			devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
+			break;
+
+		case MDL_ESS:
+			ess_mixer_init (devc);
+			break;
+
+		case MDL_SMW:
+			devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+			devc->supported_devices = 0;
+			devc->supported_rec_devices = 0;
+			devc->iomap = &sbpro_mix;
+			devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
+			smw_mixer_init(devc);
+			break;
+
+		case MDL_SB16:
+			devc->mixer_caps = 0;
+			devc->supported_rec_devices = SB16_RECORDING_DEVICES;
+			devc->supported_out_devices = SB16_OUTFILTER_DEVICES;
+			if (devc->submodel != SUBMDL_ALS007)
+			{
+				devc->supported_devices = SB16_MIXER_DEVICES;
+				devc->iomap = &sb16_mix;
+				devc->iomap_sz = ARRAY_SIZE(sb16_mix);
+			}
+			else
+			{
+				devc->supported_devices = ALS007_MIXER_DEVICES;
+				devc->iomap = &als007_mix;
+				devc->iomap_sz = ARRAY_SIZE(als007_mix);
+			}
+			break;
+
+		default:
+			printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model);
+			return 0;
+	}
+
+	m = sound_alloc_mixerdev();
+	if (m == -1)
+		return 0;
+
+	mixer_devs[m] = kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
+	if (mixer_devs[m] == NULL)
+	{
+		printk(KERN_ERR "sb_mixer: Can't allocate memory\n");
+		sound_unload_mixerdev(m);
+		return 0;
+	}
+
+	if (devc->submodel != SUBMDL_ALS007)
+		memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations));
+	else
+		memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations));
+
+	mixer_devs[m]->devc = devc;
+
+	if (owner)
+			 mixer_devs[m]->owner = owner;
+	
+	devc->my_mixerdev = m;
+	sb_mixer_reset(devc);
+	return 1;
+}
+
+void sb_mixer_unload(sb_devc *devc)
+{
+	if (devc->my_mixerdev == -1)
+		return;
+
+	kfree(mixer_devs[devc->my_mixerdev]);
+	sound_unload_mixerdev(devc->my_mixerdev);
+	sbmixnum--;
+}
diff --git a/sound/oss/sb_mixer.h b/sound/oss/sb_mixer.h
new file mode 100644
index 00000000..4b9425f0
--- /dev/null
+++ b/sound/oss/sb_mixer.h
@@ -0,0 +1,105 @@
+/*
+ * sound/oss/sb_mixer.h
+ * 
+ * Definitions for the SB Pro and SB16 mixers
+ */
+/*
+ * 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.
+ */
+
+/*
+ * Modified:
+ *	Hunyue Yau	Jan 6 1994
+ *	Added defines for the Sound Galaxy NX Pro mixer.
+ *
+ *	Rolf Fokkens	Dec 20 1998
+ *	Added defines for some ES188x chips.
+ *
+ *	Rolf Fokkens	Dec 27 1998
+ *	Moved static stuff to sb_mixer.c
+ *
+ */
+/*
+ * Mixer registers
+ * 
+ * NOTE!	RECORD_SRC == IN_FILTER
+ */
+
+/* 
+ * Mixer registers of SB Pro
+ */
+#define VOC_VOL		0x04
+#define MIC_VOL		0x0A
+#define MIC_MIX		0x0A
+#define RECORD_SRC	0x0C
+#define IN_FILTER	0x0C
+#define OUT_FILTER	0x0E
+#define MASTER_VOL	0x22
+#define FM_VOL		0x26
+#define CD_VOL		0x28
+#define LINE_VOL	0x2E
+#define IRQ_NR		0x80
+#define DMA_NR		0x81
+#define IRQ_STAT	0x82
+#define OPSW		0x3c
+
+/*
+ * Additional registers on the SG NX Pro 
+ */
+#define COVOX_VOL	0x42
+#define TREBLE_LVL	0x44
+#define BASS_LVL	0x46
+
+#define FREQ_HI         (1 << 3)/* Use High-frequency ANFI filters */
+#define FREQ_LOW        0	/* Use Low-frequency ANFI filters */
+#define FILT_ON         0	/* Yes, 0 to turn it on, 1 for off */
+#define FILT_OFF        (1 << 5)
+
+#define MONO_DAC	0x00
+#define STEREO_DAC	0x02
+
+/*
+ * Mixer registers of SB16
+ */
+#define SB16_OMASK	0x3c
+#define SB16_IMASK_L	0x3d
+#define SB16_IMASK_R	0x3e
+
+#define LEFT_CHN	0
+#define RIGHT_CHN	1
+
+/*
+ * 3DSE register of AWE32/64
+ */
+#define AWE_3DSE	0x90
+
+/*
+ * Mixer registers of ALS007
+ */
+#define ALS007_RECORD_SRC	0x6c
+#define ALS007_OUTPUT_CTRL1	0x3c
+#define ALS007_OUTPUT_CTRL2	0x4c
+
+#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r)	\
+	{{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
+
+/*
+ *	Recording sources (SB Pro)
+ */
+
+#define SRC__MIC         1	/* Select Microphone recording source */
+#define SRC__CD          3	/* Select CD recording source */
+#define SRC__LINE        7	/* Use Line-in for recording source */
+
+/*
+ *	Recording sources for ALS-007
+ */
+
+#define ALS007_MIC	4
+#define ALS007_LINE	6
+#define ALS007_CD	2
+#define ALS007_SYNTH	7
diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c
new file mode 100644
index 00000000..30bcfe47
--- /dev/null
+++ b/sound/oss/sequencer.c
@@ -0,0 +1,1671 @@
+/*
+ * sound/oss/sequencer.c
+ *
+ * The sequencer personality manager.
+ */
+/*
+ * 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)
+ * Alan Cox	   : reformatted and fixed a pair of null pointer bugs
+ */
+#include <linux/kmod.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#include "midi_ctrl.h"
+
+static int      sequencer_ok;
+static struct sound_timer_operations *tmr;
+static int      tmr_no = -1;	/* Currently selected timer */
+static int      pending_timer = -1;	/* For timer change operation */
+extern unsigned long seq_time;
+
+static int      obsolete_api_used;
+static DEFINE_SPINLOCK(lock);
+
+/*
+ * Local counts for number of synth and MIDI devices. These are initialized
+ * by the sequencer_open.
+ */
+static int      max_mididev;
+static int      max_synthdev;
+
+/*
+ * The seq_mode gives the operating mode of the sequencer:
+ *      1 = level1 (the default)
+ *      2 = level2 (extended capabilities)
+ */
+
+#define SEQ_1	1
+#define SEQ_2	2
+static int      seq_mode = SEQ_1;
+
+static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper);
+static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper);
+
+static int      midi_opened[MAX_MIDI_DEV];
+
+static int      midi_written[MAX_MIDI_DEV];
+
+static unsigned long prev_input_time;
+static int      prev_event_time;
+
+#include "tuning.h"
+
+#define EV_SZ	8
+#define IEV_SZ	8
+
+static unsigned char *queue;
+static unsigned char *iqueue;
+
+static volatile int qhead, qtail, qlen;
+static volatile int iqhead, iqtail, iqlen;
+static volatile int seq_playing;
+static volatile int sequencer_busy;
+static int      output_threshold;
+static long     pre_event_timeout;
+static unsigned synth_open_mask;
+
+static int      seq_queue(unsigned char *note, char nonblock);
+static void     seq_startplay(void);
+static int      seq_sync(void);
+static void     seq_reset(void);
+
+#if MAX_SYNTH_DEV > 15
+#error Too many synthesizer devices enabled.
+#endif
+
+int sequencer_read(int dev, struct file *file, char __user *buf, int count)
+{
+	int c = count, p = 0;
+	int ev_len;
+	unsigned long flags;
+
+	dev = dev >> 4;
+
+	ev_len = seq_mode == SEQ_1 ? 4 : 8;
+
+	spin_lock_irqsave(&lock,flags);
+
+	if (!iqlen)
+	{
+		spin_unlock_irqrestore(&lock,flags);
+ 		if (file->f_flags & O_NONBLOCK) {
+  			return -EAGAIN;
+  		}
+
+ 		interruptible_sleep_on_timeout(&midi_sleeper,
+					       pre_event_timeout);
+		spin_lock_irqsave(&lock,flags);
+		if (!iqlen)
+		{
+			spin_unlock_irqrestore(&lock,flags);
+			return 0;
+		}
+	}
+	while (iqlen && c >= ev_len)
+	{
+		char *fixit = (char *) &iqueue[iqhead * IEV_SZ];
+		spin_unlock_irqrestore(&lock,flags);
+		if (copy_to_user(&(buf)[p], fixit, ev_len))
+			return count - c;
+		p += ev_len;
+		c -= ev_len;
+
+		spin_lock_irqsave(&lock,flags);
+		iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+		iqlen--;
+	}
+	spin_unlock_irqrestore(&lock,flags);
+	return count - c;
+}
+
+static void sequencer_midi_output(int dev)
+{
+	/*
+	 * Currently NOP
+	 */
+}
+
+void seq_copy_to_input(unsigned char *event_rec, int len)
+{
+	unsigned long flags;
+
+	/*
+	 * Verify that the len is valid for the current mode.
+	 */
+
+	if (len != 4 && len != 8)
+		return;
+	if ((seq_mode == SEQ_1) != (len == 4))
+		return;
+
+	if (iqlen >= (SEQ_MAX_QUEUE - 1))
+		return;		/* Overflow */
+
+	spin_lock_irqsave(&lock,flags);
+	memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len);
+	iqlen++;
+	iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
+	wake_up(&midi_sleeper);
+	spin_unlock_irqrestore(&lock,flags);
+}
+EXPORT_SYMBOL(seq_copy_to_input);
+
+static void sequencer_midi_input(int dev, unsigned char data)
+{
+	unsigned int tstamp;
+	unsigned char event_rec[4];
+
+	if (data == 0xfe)	/* Ignore active sensing */
+		return;
+
+	tstamp = jiffies - seq_time;
+
+	if (tstamp != prev_input_time)
+	{
+		tstamp = (tstamp << 8) | SEQ_WAIT;
+		seq_copy_to_input((unsigned char *) &tstamp, 4);
+		prev_input_time = tstamp;
+	}
+	event_rec[0] = SEQ_MIDIPUTC;
+	event_rec[1] = data;
+	event_rec[2] = dev;
+	event_rec[3] = 0;
+
+	seq_copy_to_input(event_rec, 4);
+}
+
+void seq_input_event(unsigned char *event_rec, int len)
+{
+	unsigned long this_time;
+
+	if (seq_mode == SEQ_2)
+		this_time = tmr->get_time(tmr_no);
+	else
+		this_time = jiffies - seq_time;
+
+	if (this_time != prev_input_time)
+	{
+		unsigned char   tmp_event[8];
+
+		tmp_event[0] = EV_TIMING;
+		tmp_event[1] = TMR_WAIT_ABS;
+		tmp_event[2] = 0;
+		tmp_event[3] = 0;
+		*(unsigned int *) &tmp_event[4] = this_time;
+
+		seq_copy_to_input(tmp_event, 8);
+		prev_input_time = this_time;
+	}
+	seq_copy_to_input(event_rec, len);
+}
+EXPORT_SYMBOL(seq_input_event);
+
+int sequencer_write(int dev, struct file *file, const char __user *buf, int count)
+{
+	unsigned char event_rec[EV_SZ], ev_code;
+	int p = 0, c, ev_size;
+	int mode = translate_mode(file);
+
+	dev = dev >> 4;
+
+	DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count));
+
+	if (mode == OPEN_READ)
+		return -EIO;
+
+	c = count;
+
+	while (c >= 4)
+	{
+		if (copy_from_user((char *) event_rec, &(buf)[p], 4))
+			goto out;
+		ev_code = event_rec[0];
+
+		if (ev_code == SEQ_FULLSIZE)
+		{
+			int err, fmt;
+
+			dev = *(unsigned short *) &event_rec[2];
+			if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL)
+				return -ENXIO;
+
+			if (!(synth_open_mask & (1 << dev)))
+				return -ENXIO;
+
+			fmt = (*(short *) &event_rec[0]) & 0xffff;
+			err = synth_devs[dev]->load_patch(dev, fmt, buf + p, c, 0);
+			if (err < 0)
+				return err;
+
+			return err;
+		}
+		if (ev_code >= 128)
+		{
+			if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
+			{
+				printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code);
+				return -EINVAL;
+			}
+			ev_size = 8;
+
+			if (c < ev_size)
+			{
+				if (!seq_playing)
+					seq_startplay();
+				return count - c;
+			}
+			if (copy_from_user((char *)&event_rec[4],
+					   &(buf)[p + 4], 4))
+				goto out;
+
+		}
+		else
+		{
+			if (seq_mode == SEQ_2)
+			{
+				printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n");
+				return -EINVAL;
+			}
+			ev_size = 4;
+
+			if (event_rec[0] != SEQ_MIDIPUTC)
+				obsolete_api_used = 1;
+		}
+
+		if (event_rec[0] == SEQ_MIDIPUTC)
+		{
+			if (!midi_opened[event_rec[2]])
+			{
+				int err, mode;
+				int dev = event_rec[2];
+
+				if (dev >= max_mididev || midi_devs[dev]==NULL)
+				{
+					/*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/
+					return -ENXIO;
+				}
+				mode = translate_mode(file);
+
+				if ((err = midi_devs[dev]->open(dev, mode,
+								sequencer_midi_input, sequencer_midi_output)) < 0)
+				{
+					seq_reset();
+					printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev);
+					return err;
+				}
+				midi_opened[dev] = 1;
+			}
+		}
+		if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0)))
+		{
+			int processed = count - c;
+
+			if (!seq_playing)
+				seq_startplay();
+
+			if (!processed && (file->f_flags & O_NONBLOCK))
+				return -EAGAIN;
+			else
+				return processed;
+		}
+		p += ev_size;
+		c -= ev_size;
+	}
+
+	if (!seq_playing)
+		seq_startplay();
+out:
+	return count;
+}
+
+static int seq_queue(unsigned char *note, char nonblock)
+{
+
+	/*
+	 * Test if there is space in the queue
+	 */
+
+	if (qlen >= SEQ_MAX_QUEUE)
+		if (!seq_playing)
+			seq_startplay();	/*
+						 * Give chance to drain the queue
+						 */
+
+	if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) {
+		/*
+		 * Sleep until there is enough space on the queue
+		 */
+		interruptible_sleep_on(&seq_sleeper);
+	}
+	if (qlen >= SEQ_MAX_QUEUE)
+	{
+		return 0;	/*
+				 * To be sure
+				 */
+	}
+	memcpy(&queue[qtail * EV_SZ], note, EV_SZ);
+
+	qtail = (qtail + 1) % SEQ_MAX_QUEUE;
+	qlen++;
+
+	return 1;
+}
+
+static int extended_event(unsigned char *q)
+{
+	int dev = q[2];
+
+	if (dev < 0 || dev >= max_synthdev)
+		return -ENXIO;
+
+	if (!(synth_open_mask & (1 << dev)))
+		return -ENXIO;
+
+	switch (q[1])
+	{
+		case SEQ_NOTEOFF:
+			synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+			break;
+
+		case SEQ_NOTEON:
+			if (q[4] > 127 && q[4] != 255)
+				return 0;
+
+			if (q[5] == 0)
+			{
+				synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+				break;
+			}
+			synth_devs[dev]->start_note(dev, q[3], q[4], q[5]);
+			break;
+
+		case SEQ_PGMCHANGE:
+			synth_devs[dev]->set_instr(dev, q[3], q[4]);
+			break;
+
+		case SEQ_AFTERTOUCH:
+			synth_devs[dev]->aftertouch(dev, q[3], q[4]);
+			break;
+
+		case SEQ_BALANCE:
+			synth_devs[dev]->panning(dev, q[3], (char) q[4]);
+			break;
+
+		case SEQ_CONTROLLER:
+			synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
+			break;
+
+		case SEQ_VOLMODE:
+			if (synth_devs[dev]->volume_method != NULL)
+				synth_devs[dev]->volume_method(dev, q[3]);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int find_voice(int dev, int chn, int note)
+{
+	unsigned short key;
+	int i;
+
+	key = (chn << 8) | (note + 1);
+	for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+		if (synth_devs[dev]->alloc.map[i] == key)
+			return i;
+	return -1;
+}
+
+static int alloc_voice(int dev, int chn, int note)
+{
+	unsigned short  key;
+	int voice;
+
+	key = (chn << 8) | (note + 1);
+
+	voice = synth_devs[dev]->alloc_voice(dev, chn, note,
+					     &synth_devs[dev]->alloc);
+	synth_devs[dev]->alloc.map[voice] = key;
+	synth_devs[dev]->alloc.alloc_times[voice] =
+			synth_devs[dev]->alloc.timestamp++;
+	return voice;
+}
+
+static void seq_chn_voice_event(unsigned char *event_rec)
+{
+#define dev event_rec[1]
+#define cmd event_rec[2]
+#define chn event_rec[3]
+#define note event_rec[4]
+#define parm event_rec[5]
+
+	int voice = -1;
+
+	if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
+		return;
+	if (!(synth_open_mask & (1 << dev)))
+		return;
+	if (!synth_devs[dev])
+		return;
+
+	if (seq_mode == SEQ_2)
+	{
+		if (synth_devs[dev]->alloc_voice)
+			voice = find_voice(dev, chn, note);
+
+		if (cmd == MIDI_NOTEON && parm == 0)
+		{
+			cmd = MIDI_NOTEOFF;
+			parm = 64;
+		}
+	}
+
+	switch (cmd)
+	{
+		case MIDI_NOTEON:
+			if (note > 127 && note != 255)	/* Not a seq2 feature */
+				return;
+
+			if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
+			{
+				/* Internal synthesizer (FM, GUS, etc) */
+				voice = alloc_voice(dev, chn, note);
+			}
+			if (voice == -1)
+				voice = chn;
+
+			if (seq_mode == SEQ_2 && (int) dev < num_synths)
+			{
+				/*
+				 * The MIDI channel 10 is a percussive channel. Use the note
+				 * number to select the proper patch (128 to 255) to play.
+				 */
+
+				if (chn == 9)
+				{
+					synth_devs[dev]->set_instr(dev, voice, 128 + note);
+					synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
+				}
+				synth_devs[dev]->setup_voice(dev, voice, chn);
+			}
+			synth_devs[dev]->start_note(dev, voice, note, parm);
+			break;
+
+		case MIDI_NOTEOFF:
+			if (voice == -1)
+				voice = chn;
+			synth_devs[dev]->kill_note(dev, voice, note, parm);
+			break;
+
+		case MIDI_KEY_PRESSURE:
+			if (voice == -1)
+				voice = chn;
+			synth_devs[dev]->aftertouch(dev, voice, parm);
+			break;
+
+		default:;
+	}
+#undef dev
+#undef cmd
+#undef chn
+#undef note
+#undef parm
+}
+
+
+static void seq_chn_common_event(unsigned char *event_rec)
+{
+	unsigned char dev = event_rec[1];
+	unsigned char cmd = event_rec[2];
+	unsigned char chn = event_rec[3];
+	unsigned char p1 = event_rec[4];
+
+	/* unsigned char p2 = event_rec[5]; */
+	unsigned short w14 = *(short *) &event_rec[6];
+
+	if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
+		return;
+	if (!(synth_open_mask & (1 << dev)))
+		return;
+	if (!synth_devs[dev])
+		return;
+
+	switch (cmd)
+	{
+		case MIDI_PGM_CHANGE:
+			if (seq_mode == SEQ_2)
+			{
+				synth_devs[dev]->chn_info[chn].pgm_num = p1;
+				if ((int) dev >= num_synths)
+					synth_devs[dev]->set_instr(dev, chn, p1);
+			}
+			else
+				synth_devs[dev]->set_instr(dev, chn, p1);
+
+			break;
+
+		case MIDI_CTL_CHANGE:
+			if (seq_mode == SEQ_2)
+			{
+				if (chn > 15 || p1 > 127)
+					break;
+
+				synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
+
+				if (p1 < 32)	/* Setting MSB should clear LSB to 0 */
+					synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
+
+				if ((int) dev < num_synths)
+				{
+					int val = w14 & 0x7f;
+					int i, key;
+
+					if (p1 < 64)	/* Combine MSB and LSB */
+					{
+						val = ((synth_devs[dev]->
+							chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+							| (synth_devs[dev]->
+							chn_info[chn].controllers[p1 | 32] & 0x7f);
+						p1 &= ~32;
+					}
+					/* Handle all playing notes on this channel */
+
+					key = ((int) chn << 8);
+
+					for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+						if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+							synth_devs[dev]->controller(dev, i, p1, val);
+				}
+				else
+					synth_devs[dev]->controller(dev, chn, p1, w14);
+			}
+			else	/* Mode 1 */
+				synth_devs[dev]->controller(dev, chn, p1, w14);
+			break;
+
+		case MIDI_PITCH_BEND:
+			if (seq_mode == SEQ_2)
+			{
+				synth_devs[dev]->chn_info[chn].bender_value = w14;
+
+				if ((int) dev < num_synths)
+				{
+					/* Handle all playing notes on this channel */
+					int i, key;
+
+					key = (chn << 8);
+
+					for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+						if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+							synth_devs[dev]->bender(dev, i, w14);
+				}
+				else
+					synth_devs[dev]->bender(dev, chn, w14);
+			}
+			else	/* MODE 1 */
+				synth_devs[dev]->bender(dev, chn, w14);
+			break;
+
+		default:;
+	}
+}
+
+static int seq_timing_event(unsigned char *event_rec)
+{
+	unsigned char cmd = event_rec[1];
+	unsigned int parm = *(int *) &event_rec[4];
+
+	if (seq_mode == SEQ_2)
+	{
+		int ret;
+
+		if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
+			if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+				wake_up(&seq_sleeper);
+		return ret;
+	}
+	switch (cmd)
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+
+			/*
+			 * NOTE!  No break here. Execution of TMR_WAIT_REL continues in the
+			 * next case (TMR_WAIT_ABS)
+			 */
+
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				time = parm;
+				prev_event_time = time;
+
+				seq_playing = 1;
+				request_sound_timer(time);
+
+				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+					wake_up(&seq_sleeper);
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			seq_time = jiffies;
+			prev_input_time = 0;
+			prev_event_time = 0;
+			break;
+
+		case TMR_STOP:
+			break;
+
+		case TMR_CONTINUE:
+			break;
+
+		case TMR_TEMPO:
+			break;
+
+		case TMR_ECHO:
+			if (seq_mode == SEQ_2)
+				seq_copy_to_input(event_rec, 8);
+			else
+			{
+				parm = (parm << 8 | SEQ_ECHO);
+				seq_copy_to_input((unsigned char *) &parm, 4);
+			}
+			break;
+
+		default:;
+	}
+
+	return TIMER_NOT_ARMED;
+}
+
+static void seq_local_event(unsigned char *event_rec)
+{
+	unsigned char   cmd = event_rec[1];
+	unsigned int    parm = *((unsigned int *) &event_rec[4]);
+
+	switch (cmd)
+	{
+		case LOCL_STARTAUDIO:
+			DMAbuf_start_devices(parm);
+			break;
+
+		default:;
+	}
+}
+
+static void seq_sysex_message(unsigned char *event_rec)
+{
+	unsigned int dev = event_rec[1];
+	int i, l = 0;
+	unsigned char  *buf = &event_rec[2];
+
+	if (dev > max_synthdev)
+		return;
+	if (!(synth_open_mask & (1 << dev)))
+		return;
+	if (!synth_devs[dev])
+		return;
+
+	l = 0;
+	for (i = 0; i < 6 && buf[i] != 0xff; i++)
+		l = i + 1;
+
+	if (!synth_devs[dev]->send_sysex)
+		return;
+	if (l > 0)
+		synth_devs[dev]->send_sysex(dev, buf, l);
+}
+
+static int play_event(unsigned char *q)
+{
+	/*
+	 * NOTE! This routine returns
+	 *   0 = normal event played.
+	 *   1 = Timer armed. Suspend playback until timer callback.
+	 *   2 = MIDI output buffer full. Restore queue and suspend until timer
+	 */
+	unsigned int *delay;
+
+	switch (q[0])
+	{
+		case SEQ_NOTEOFF:
+			if (synth_open_mask & (1 << 0))
+				if (synth_devs[0])
+					synth_devs[0]->kill_note(0, q[1], 255, q[3]);
+			break;
+
+		case SEQ_NOTEON:
+			if (q[4] < 128 || q[4] == 255)
+				if (synth_open_mask & (1 << 0))
+					if (synth_devs[0])
+						synth_devs[0]->start_note(0, q[1], q[2], q[3]);
+			break;
+
+		case SEQ_WAIT:
+			delay = (unsigned int *) q;	/*
+							 * Bytes 1 to 3 are containing the *
+							 * delay in 'ticks'
+							 */
+			*delay = (*delay >> 8) & 0xffffff;
+
+			if (*delay > 0)
+			{
+				long time;
+
+				seq_playing = 1;
+				time = *delay;
+				prev_event_time = time;
+
+				request_sound_timer(time);
+
+				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+					wake_up(&seq_sleeper);
+				/*
+				 * The timer is now active and will reinvoke this function
+				 * after the timer expires. Return to the caller now.
+				 */
+				return 1;
+			}
+			break;
+
+		case SEQ_PGMCHANGE:
+			if (synth_open_mask & (1 << 0))
+				if (synth_devs[0])
+					synth_devs[0]->set_instr(0, q[1], q[2]);
+			break;
+
+		case SEQ_SYNCTIMER: 	/*
+					 * Reset timer
+					 */
+			seq_time = jiffies;
+			prev_input_time = 0;
+			prev_event_time = 0;
+			break;
+
+		case SEQ_MIDIPUTC:	/*
+					 * Put a midi character
+					 */
+			if (midi_opened[q[2]])
+			{
+				int dev;
+
+				dev = q[2];
+
+				if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+					break;
+
+				if (!midi_devs[dev]->outputc(dev, q[1]))
+				{
+					/*
+					 * Output FIFO is full. Wait one timer cycle and try again.
+					 */
+
+					seq_playing = 1;
+					request_sound_timer(-1);
+					return 2;
+				}
+				else
+					midi_written[dev] = 1;
+			}
+			break;
+
+		case SEQ_ECHO:
+			seq_copy_to_input(q, 4);	/*
+							 * Echo back to the process
+							 */
+			break;
+
+		case SEQ_PRIVATE:
+			if ((int) q[1] < max_synthdev)
+				synth_devs[q[1]]->hw_control(q[1], q);
+			break;
+
+		case SEQ_EXTENDED:
+			extended_event(q);
+			break;
+
+		case EV_CHN_VOICE:
+			seq_chn_voice_event(q);
+			break;
+
+		case EV_CHN_COMMON:
+			seq_chn_common_event(q);
+			break;
+
+		case EV_TIMING:
+			if (seq_timing_event(q) == TIMER_ARMED)
+			{
+				return 1;
+			}
+			break;
+
+		case EV_SEQ_LOCAL:
+			seq_local_event(q);
+			break;
+
+		case EV_SYSEX:
+			seq_sysex_message(q);
+			break;
+
+		default:;
+	}
+	return 0;
+}
+
+/* called also as timer in irq context */
+static void seq_startplay(void)
+{
+	int this_one, action;
+	unsigned long flags;
+
+	while (qlen > 0)
+	{
+
+		spin_lock_irqsave(&lock,flags);
+		qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+		qlen--;
+		spin_unlock_irqrestore(&lock,flags);
+
+		seq_playing = 1;
+
+		if ((action = play_event(&queue[this_one * EV_SZ])))
+		{		/* Suspend playback. Next timer routine invokes this routine again */
+			if (action == 2)
+			{
+				qlen++;
+				qhead = this_one;
+			}
+			return;
+		}
+	}
+
+	seq_playing = 0;
+
+	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+		wake_up(&seq_sleeper);
+}
+
+static void reset_controllers(int dev, unsigned char *controller, int update_dev)
+{
+	int i;
+	for (i = 0; i < 128; i++)
+		controller[i] = ctrl_def_values[i];
+}
+
+static void setup_mode2(void)
+{
+	int dev;
+
+	max_synthdev = num_synths;
+
+	for (dev = 0; dev < num_midis; dev++)
+	{
+		if (midi_devs[dev] && midi_devs[dev]->converter != NULL)
+		{
+			synth_devs[max_synthdev++] = midi_devs[dev]->converter;
+		}
+	}
+
+	for (dev = 0; dev < max_synthdev; dev++)
+	{
+		int chn;
+
+		synth_devs[dev]->sysex_ptr = 0;
+		synth_devs[dev]->emulation = 0;
+
+		for (chn = 0; chn < 16; chn++)
+		{
+			synth_devs[dev]->chn_info[chn].pgm_num = 0;
+			reset_controllers(dev,
+				synth_devs[dev]->chn_info[chn].controllers,0);
+			synth_devs[dev]->chn_info[chn].bender_value = (1 << 7);	/* Neutral */
+			synth_devs[dev]->chn_info[chn].bender_range = 200;
+		}
+	}
+	max_mididev = 0;
+	seq_mode = SEQ_2;
+}
+
+int sequencer_open(int dev, struct file *file)
+{
+	int retval, mode, i;
+	int level, tmp;
+
+	if (!sequencer_ok)
+		sequencer_init();
+
+	level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
+
+	dev = dev >> 4;
+	mode = translate_mode(file);
+
+	DEB(printk("sequencer_open(dev=%d)\n", dev));
+
+	if (!sequencer_ok)
+	{
+/*		printk("Sound card: sequencer not initialized\n");*/
+		return -ENXIO;
+	}
+	if (dev)		/* Patch manager device (obsolete) */
+		return -ENXIO;
+
+	if(synth_devs[dev] == NULL)
+		request_module("synth0");
+
+	if (mode == OPEN_READ)
+	{
+		if (!num_midis)
+		{
+			/*printk("Sequencer: No MIDI devices. Input not possible\n");*/
+			sequencer_busy = 0;
+			return -ENXIO;
+		}
+	}
+	if (sequencer_busy)
+	{
+		return -EBUSY;
+	}
+	sequencer_busy = 1;
+	obsolete_api_used = 0;
+
+	max_mididev = num_midis;
+	max_synthdev = num_synths;
+	pre_event_timeout = MAX_SCHEDULE_TIMEOUT;
+	seq_mode = SEQ_1;
+
+	if (pending_timer != -1)
+	{
+		tmr_no = pending_timer;
+		pending_timer = -1;
+	}
+	if (tmr_no == -1)	/* Not selected yet */
+	{
+		int i, best;
+
+		best = -1;
+		for (i = 0; i < num_sound_timers; i++)
+			if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best)
+			{
+				tmr_no = i;
+				best = sound_timer_devs[i]->priority;
+			}
+		if (tmr_no == -1)	/* Should not be */
+			tmr_no = 0;
+	}
+	tmr = sound_timer_devs[tmr_no];
+
+	if (level == 2)
+	{
+		if (tmr == NULL)
+		{
+			/*printk("sequencer: No timer for level 2\n");*/
+			sequencer_busy = 0;
+			return -ENXIO;
+		}
+		setup_mode2();
+	}
+	if (!max_synthdev && !max_mididev)
+	{
+		sequencer_busy=0;
+		return -ENXIO;
+	}
+
+	synth_open_mask = 0;
+
+	for (i = 0; i < max_mididev; i++)
+	{
+		midi_opened[i] = 0;
+		midi_written[i] = 0;
+	}
+
+	for (i = 0; i < max_synthdev; i++)
+	{
+		if (synth_devs[i]==NULL)
+			continue;
+
+		if (!try_module_get(synth_devs[i]->owner))
+			continue;
+
+		if ((tmp = synth_devs[i]->open(i, mode)) < 0)
+		{
+			printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+			if (synth_devs[i]->midi_dev)
+				printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
+		}
+		else
+		{
+			synth_open_mask |= (1 << i);
+			if (synth_devs[i]->midi_dev)
+				midi_opened[synth_devs[i]->midi_dev] = 1;
+		}
+	}
+
+	seq_time = jiffies;
+
+	prev_input_time = 0;
+	prev_event_time = 0;
+
+	if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
+	{
+		/*
+		 * Initialize midi input devices
+		 */
+
+		for (i = 0; i < max_mididev; i++)
+			if (!midi_opened[i] && midi_devs[i])
+			{
+				if (!try_module_get(midi_devs[i]->owner))
+					continue;
+	
+				if ((retval = midi_devs[i]->open(i, mode,
+					sequencer_midi_input, sequencer_midi_output)) >= 0)
+				{
+					midi_opened[i] = 1;
+				}
+			}
+	}
+
+	if (seq_mode == SEQ_2) {
+		if (try_module_get(tmr->owner))
+			tmr->open(tmr_no, seq_mode);
+	}
+
+ 	init_waitqueue_head(&seq_sleeper);
+ 	init_waitqueue_head(&midi_sleeper);
+	output_threshold = SEQ_MAX_QUEUE / 2;
+
+	return 0;
+}
+
+static void seq_drain_midi_queues(void)
+{
+	int i, n;
+
+	/*
+	 * Give the Midi drivers time to drain their output queues
+	 */
+
+	n = 1;
+
+	while (!signal_pending(current) && n)
+	{
+		n = 0;
+
+		for (i = 0; i < max_mididev; i++)
+			if (midi_opened[i] && midi_written[i])
+				if (midi_devs[i]->buffer_status != NULL)
+					if (midi_devs[i]->buffer_status(i))
+						n++;
+
+		/*
+		 * Let's have a delay
+		 */
+
+ 		if (n)
+ 			interruptible_sleep_on_timeout(&seq_sleeper,
+						       HZ/10);
+	}
+}
+
+void sequencer_release(int dev, struct file *file)
+{
+	int i;
+	int mode = translate_mode(file);
+
+	dev = dev >> 4;
+
+	DEB(printk("sequencer_release(dev=%d)\n", dev));
+
+	/*
+	 * Wait until the queue is empty (if we don't have nonblock)
+	 */
+
+	if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK))
+	{
+		while (!signal_pending(current) && qlen > 0)
+		{
+  			seq_sync();
+ 			interruptible_sleep_on_timeout(&seq_sleeper,
+						       3*HZ);
+ 			/* Extra delay */
+		}
+	}
+
+	if (mode != OPEN_READ)
+		seq_drain_midi_queues();	/*
+						 * Ensure the output queues are empty
+						 */
+	seq_reset();
+	if (mode != OPEN_READ)
+		seq_drain_midi_queues();	/*
+						 * Flush the all notes off messages
+						 */
+
+	for (i = 0; i < max_synthdev; i++)
+	{
+		if (synth_open_mask & (1 << i))	/*
+						 * Actually opened
+						 */
+			if (synth_devs[i])
+			{
+				synth_devs[i]->close(i);
+
+				module_put(synth_devs[i]->owner);
+
+				if (synth_devs[i]->midi_dev)
+					midi_opened[synth_devs[i]->midi_dev] = 0;
+			}
+	}
+
+	for (i = 0; i < max_mididev; i++)
+	{
+		if (midi_opened[i]) {
+			midi_devs[i]->close(i);
+			module_put(midi_devs[i]->owner);
+		}
+	}
+
+	if (seq_mode == SEQ_2) {
+		tmr->close(tmr_no);
+		module_put(tmr->owner);
+	}
+
+	if (obsolete_api_used)
+		printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm);
+	sequencer_busy = 0;
+}
+
+static int seq_sync(void)
+{
+	if (qlen && !seq_playing && !signal_pending(current))
+		seq_startplay();
+
+ 	if (qlen > 0)
+ 		interruptible_sleep_on_timeout(&seq_sleeper, HZ);
+	return qlen;
+}
+
+static void midi_outc(int dev, unsigned char data)
+{
+	/*
+	 * NOTE! Calls sleep(). Don't call this from interrupt.
+	 */
+
+	int n;
+	unsigned long flags;
+
+	/*
+	 * This routine sends one byte to the Midi channel.
+	 * If the output FIFO is full, it waits until there
+	 * is space in the queue
+	 */
+
+	n = 3 * HZ;		/* Timeout */
+
+	spin_lock_irqsave(&lock,flags);
+ 	while (n && !midi_devs[dev]->outputc(dev, data)) {
+ 		interruptible_sleep_on_timeout(&seq_sleeper, HZ/25);
+  		n--;
+  	}
+	spin_unlock_irqrestore(&lock,flags);
+}
+
+static void seq_reset(void)
+{
+	/*
+	 * NOTE! Calls sleep(). Don't call this from interrupt.
+	 */
+
+	int i;
+	int chn;
+	unsigned long flags;
+
+	sound_stop_timer();
+
+	seq_time = jiffies;
+	prev_input_time = 0;
+	prev_event_time = 0;
+
+	qlen = qhead = qtail = 0;
+	iqlen = iqhead = iqtail = 0;
+
+	for (i = 0; i < max_synthdev; i++)
+		if (synth_open_mask & (1 << i))
+			if (synth_devs[i])
+				synth_devs[i]->reset(i);
+
+	if (seq_mode == SEQ_2)
+	{
+		for (chn = 0; chn < 16; chn++)
+			for (i = 0; i < max_synthdev; i++)
+				if (synth_open_mask & (1 << i))
+					if (synth_devs[i])
+					{
+						synth_devs[i]->controller(i, chn, 123, 0);	/* All notes off */
+						synth_devs[i]->controller(i, chn, 121, 0);	/* Reset all ctl */
+						synth_devs[i]->bender(i, chn, 1 << 13);	/* Bender off */
+					}
+	}
+	else	/* seq_mode == SEQ_1 */
+	{
+		for (i = 0; i < max_mididev; i++)
+			if (midi_written[i])	/*
+						 * Midi used. Some notes may still be playing
+						 */
+			{
+				/*
+				 *      Sending just a ACTIVE SENSING message should be enough to stop all
+				 *      playing notes. Since there are devices not recognizing the
+				 *      active sensing, we have to send some all notes off messages also.
+				 */
+				midi_outc(i, 0xfe);
+
+				for (chn = 0; chn < 16; chn++)
+				{
+					midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f)));		/* control change */
+					midi_outc(i, 0x7b);	/* All notes off */
+					midi_outc(i, 0);	/* Dummy parameter */
+				}
+
+				midi_devs[i]->close(i);
+
+				midi_written[i] = 0;
+				midi_opened[i] = 0;
+			}
+	}
+
+	seq_playing = 0;
+
+	spin_lock_irqsave(&lock,flags);
+
+	if (waitqueue_active(&seq_sleeper)) {
+		/*      printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
+		wake_up(&seq_sleeper);
+	}
+	spin_unlock_irqrestore(&lock,flags);
+}
+
+static void seq_panic(void)
+{
+	/*
+	 * This routine is called by the application in case the user
+	 * wants to reset the system to the default state.
+	 */
+
+	seq_reset();
+
+	/*
+	 * Since some of the devices don't recognize the active sensing and
+	 * all notes off messages, we have to shut all notes manually.
+	 *
+	 *      TO BE IMPLEMENTED LATER
+	 */
+
+	/*
+	 * Also return the controllers to their default states
+	 */
+}
+
+int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
+{
+	int midi_dev, orig_dev, val, err;
+	int mode = translate_mode(file);
+	struct synth_info inf;
+	struct seq_event_rec event_rec;
+	unsigned long flags;
+	int __user *p = arg;
+
+	orig_dev = dev = dev >> 4;
+
+	switch (cmd)
+	{
+		case SNDCTL_TMR_TIMEBASE:
+		case SNDCTL_TMR_TEMPO:
+		case SNDCTL_TMR_START:
+		case SNDCTL_TMR_STOP:
+		case SNDCTL_TMR_CONTINUE:
+		case SNDCTL_TMR_METRONOME:
+		case SNDCTL_TMR_SOURCE:
+			if (seq_mode != SEQ_2)
+				return -EINVAL;
+			return tmr->ioctl(tmr_no, cmd, arg);
+
+		case SNDCTL_TMR_SELECT:
+			if (seq_mode != SEQ_2)
+				return -EINVAL;
+			if (get_user(pending_timer, p))
+				return -EFAULT;
+			if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL)
+			{
+				pending_timer = -1;
+				return -EINVAL;
+			}
+			val = pending_timer;
+			break;
+
+		case SNDCTL_SEQ_PANIC:
+			seq_panic();
+			return -EINVAL;
+
+		case SNDCTL_SEQ_SYNC:
+			if (mode == OPEN_READ)
+				return 0;
+			while (qlen > 0 && !signal_pending(current))
+				seq_sync();
+			return qlen ? -EINTR : 0;
+
+		case SNDCTL_SEQ_RESET:
+			seq_reset();
+			return 0;
+
+		case SNDCTL_SEQ_TESTMIDI:
+			if (__get_user(midi_dev, p))
+				return -EFAULT;
+			if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev])
+				return -ENXIO;
+
+			if (!midi_opened[midi_dev] &&
+				(err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input,
+						     sequencer_midi_output)) < 0)
+				return err;
+			midi_opened[midi_dev] = 1;
+			return 0;
+
+		case SNDCTL_SEQ_GETINCOUNT:
+			if (mode == OPEN_WRITE)
+				return 0;
+			val = iqlen;
+			break;
+
+		case SNDCTL_SEQ_GETOUTCOUNT:
+			if (mode == OPEN_READ)
+				return 0;
+			val = SEQ_MAX_QUEUE - qlen;
+			break;
+
+		case SNDCTL_SEQ_GETTIME:
+			if (seq_mode == SEQ_2)
+				return tmr->ioctl(tmr_no, cmd, arg);
+			val = jiffies - seq_time;
+			break;
+
+		case SNDCTL_SEQ_CTRLRATE:
+			/*
+			 * If *arg == 0, just return the current rate
+			 */
+			if (seq_mode == SEQ_2)
+				return tmr->ioctl(tmr_no, cmd, arg);
+
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val != 0)
+				return -EINVAL;
+			val = HZ;
+			break;
+
+		case SNDCTL_SEQ_RESETSAMPLES:
+		case SNDCTL_SYNTH_REMOVESAMPLE:
+		case SNDCTL_SYNTH_CONTROL:
+			if (get_user(dev, p))
+				return -EFAULT;
+			if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			return synth_devs[dev]->ioctl(dev, cmd, arg);
+
+		case SNDCTL_SEQ_NRSYNTHS:
+			val = max_synthdev;
+			break;
+
+		case SNDCTL_SEQ_NRMIDIS:
+			val = max_mididev;
+			break;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			if (get_user(dev, p))
+				return -EFAULT;
+			if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			val = synth_devs[dev]->ioctl(dev, cmd, arg);
+			break;
+
+		case SNDCTL_FM_4OP_ENABLE:
+			if (get_user(dev, p))
+				return -EFAULT;
+			if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)))
+				return -ENXIO;
+			synth_devs[dev]->ioctl(dev, cmd, arg);
+			return 0;
+
+		case SNDCTL_SYNTH_INFO:
+			if (get_user(dev, &((struct synth_info __user *)arg)->device))
+				return -EFAULT;
+			if (dev < 0 || dev >= max_synthdev)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			return synth_devs[dev]->ioctl(dev, cmd, arg);
+
+		/* Like SYNTH_INFO but returns ID in the name field */
+		case SNDCTL_SYNTH_ID:
+			if (get_user(dev, &((struct synth_info __user *)arg)->device))
+				return -EFAULT;
+			if (dev < 0 || dev >= max_synthdev)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			memcpy(&inf, synth_devs[dev]->info, sizeof(inf));
+			strlcpy(inf.name, synth_devs[dev]->id, sizeof(inf.name));
+			inf.device = dev;
+			return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0;
+
+		case SNDCTL_SEQ_OUTOFBAND:
+			if (copy_from_user(&event_rec, arg, sizeof(event_rec)))
+				return -EFAULT;
+			spin_lock_irqsave(&lock,flags);
+			play_event(event_rec.arr);
+			spin_unlock_irqrestore(&lock,flags);
+			return 0;
+
+		case SNDCTL_MIDI_INFO:
+			if (get_user(dev, &((struct midi_info __user *)arg)->device))
+				return -EFAULT;
+			if (dev < 0 || dev >= max_mididev || !midi_devs[dev])
+				return -ENXIO;
+			midi_devs[dev]->info.device = dev;
+			return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0;
+
+		case SNDCTL_SEQ_THRESHOLD:
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val < 1)
+				val = 1;
+			if (val >= SEQ_MAX_QUEUE)
+				val = SEQ_MAX_QUEUE - 1;
+			output_threshold = val;
+			return 0;
+
+		case SNDCTL_MIDI_PRETIME:
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val < 0)
+				val = 0;
+			val = (HZ * val) / 10;
+			pre_event_timeout = val;
+			break;
+
+		default:
+			if (mode == OPEN_READ)
+				return -EIO;
+			if (!synth_devs[0])
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << 0)))
+				return -ENXIO;
+			if (!synth_devs[0]->ioctl)
+				return -EINVAL;
+			return synth_devs[0]->ioctl(0, cmd, arg);
+	}
+	return put_user(val, p);
+}
+
+/* No kernel lock - we're using the global irq lock here */
+unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait)
+{
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	dev = dev >> 4;
+
+	spin_lock_irqsave(&lock,flags);
+	/* input */
+	poll_wait(file, &midi_sleeper, wait);
+	if (iqlen)
+		mask |= POLLIN | POLLRDNORM;
+
+	/* output */
+	poll_wait(file, &seq_sleeper, wait);
+	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+		mask |= POLLOUT | POLLWRNORM;
+	spin_unlock_irqrestore(&lock,flags);
+	return mask;
+}
+
+
+void sequencer_timer(unsigned long dummy)
+{
+	seq_startplay();
+}
+EXPORT_SYMBOL(sequencer_timer);
+
+int note_to_freq(int note_num)
+{
+
+	/*
+	 * This routine converts a midi note to a frequency (multiplied by 1000)
+	 */
+
+	int note, octave, note_freq;
+	static int notes[] =
+	{
+		261632, 277189, 293671, 311132, 329632, 349232,
+		369998, 391998, 415306, 440000, 466162, 493880
+	};
+
+#define BASE_OCTAVE	5
+
+	octave = note_num / 12;
+	note = note_num % 12;
+
+	note_freq = notes[note];
+
+	if (octave < BASE_OCTAVE)
+		note_freq >>= (BASE_OCTAVE - octave);
+	else if (octave > BASE_OCTAVE)
+		note_freq <<= (octave - BASE_OCTAVE);
+
+	/*
+	 * note_freq >>= 1;
+	 */
+
+	return note_freq;
+}
+EXPORT_SYMBOL(note_to_freq);
+
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
+		 int vibrato_cents)
+{
+	unsigned long amount;
+	int negative, semitones, cents, multiplier = 1;
+
+	if (!bend)
+		return base_freq;
+	if (!range)
+		return base_freq;
+
+	if (!base_freq)
+		return base_freq;
+
+	if (range >= 8192)
+		range = 8192;
+
+	bend = bend * range / 8192;	/* Convert to cents */
+	bend += vibrato_cents;
+
+	if (!bend)
+		return base_freq;
+
+	negative = bend < 0 ? 1 : 0;
+
+	if (bend < 0)
+		bend *= -1;
+	if (bend > range)
+		bend = range;
+
+	/*
+	   if (bend > 2399)
+	   bend = 2399;
+	 */
+	while (bend > 2399)
+	{
+		multiplier *= 4;
+		bend -= 2400;
+	}
+
+	semitones = bend / 100;
+	cents = bend % 100;
+
+	amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000;
+
+	if (negative)
+		return (base_freq * 10000) / amount;	/* Bend down */
+	else
+		return (base_freq * amount) / 10000;	/* Bend up */
+}
+EXPORT_SYMBOL(compute_finetune);
+
+void sequencer_init(void)
+{
+	if (sequencer_ok)
+		return;
+	queue = vmalloc(SEQ_MAX_QUEUE * EV_SZ);
+	if (queue == NULL)
+	{
+		printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n");
+		return;
+	}
+	iqueue = vmalloc(SEQ_MAX_QUEUE * IEV_SZ);
+	if (iqueue == NULL)
+	{
+		printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n");
+		vfree(queue);
+		return;
+	}
+	sequencer_ok = 1;
+}
+EXPORT_SYMBOL(sequencer_init);
+
+void sequencer_unload(void)
+{
+	vfree(queue);
+	vfree(iqueue);
+	queue = iqueue = NULL;
+}
diff --git a/sound/oss/sound_calls.h b/sound/oss/sound_calls.h
new file mode 100644
index 00000000..87d8ad4a
--- /dev/null
+++ b/sound/oss/sound_calls.h
@@ -0,0 +1,87 @@
+/*
+ *	DMA buffer calls
+ */
+
+int DMAbuf_open(int dev, int mode);
+int DMAbuf_release(int dev, int mode);
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
+int DMAbuf_rmchars(int dev, int buff_no, int c);
+int DMAbuf_start_output(int dev, int buff_no, int l);
+int DMAbuf_move_wrpointer(int dev, int l);
+/* int DMAbuf_ioctl(int dev, unsigned int cmd, void __user *arg, int local); */
+void DMAbuf_init(int dev, int dma1, int dma2);
+void DMAbuf_deinit(int dev);
+int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+void DMAbuf_inputintr(int dev);
+void DMAbuf_outputintr(int dev, int underflow_flag);
+struct dma_buffparms;
+int DMAbuf_space_in_queue (int dev);
+int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
+int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
+void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
+unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait);
+void DMAbuf_start_devices(unsigned int devmask);
+void DMAbuf_reset (int dev);
+int DMAbuf_sync (int dev);
+
+/*
+ *	System calls for /dev/dsp and /dev/audio (audio.c)
+ */
+
+int audio_read (int dev, struct file *file, char __user *buf, int count);
+int audio_write (int dev, struct file *file, const char __user *buf, int count);
+int audio_open (int dev, struct file *file);
+void audio_release (int dev, struct file *file);
+int audio_ioctl (int dev, struct file *file,
+	   unsigned int cmd, void __user *arg);
+void audio_init_devices (void);
+void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
+
+/*
+ *	System calls for the /dev/sequencer
+ */
+
+int sequencer_read (int dev, struct file *file, char __user *buf, int count);
+int sequencer_write (int dev, struct file *file, const char __user *buf, int count);
+int sequencer_open (int dev, struct file *file);
+void sequencer_release (int dev, struct file *file);
+int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
+unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait);
+
+void sequencer_init (void);
+void sequencer_unload (void);
+void sequencer_timer(unsigned long dummy);
+int note_to_freq(int note_num);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
+			       int vibrato_bend);
+void seq_input_event(unsigned char *event, int len);
+void seq_copy_to_input (unsigned char *event, int len);
+
+/*
+ *	System calls for the /dev/midi
+ */
+
+int MIDIbuf_read (int dev, struct file *file, char __user *buf, int count);
+int MIDIbuf_write (int dev, struct file *file, const char __user *buf, int count);
+int MIDIbuf_open (int dev, struct file *file);
+void MIDIbuf_release (int dev, struct file *file);
+int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
+unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait);
+int MIDIbuf_avail(int dev);
+
+void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
+
+
+/*	From soundcard.c	*/
+void request_sound_timer (int count);
+void sound_stop_timer(void);
+void conf_printf(char *name, struct address_info *hw_config);
+void conf_printf2(char *name, int base, int irq, int dma, int dma2);
+
+/*	From sound_timer.c */
+void sound_timer_interrupt(void);
+void sound_timer_syncinterval(unsigned int new_usecs);
+
+/*      From midi_synth.c       */
+void do_midi_msg (int synthno, unsigned char *msg, int mlen);
diff --git a/sound/oss/sound_config.h b/sound/oss/sound_config.h
new file mode 100644
index 00000000..9d35c4c6
--- /dev/null
+++ b/sound/oss/sound_config.h
@@ -0,0 +1,147 @@
+/* sound_config.h
+ *
+ * A driver for sound cards, misc. configuration parameters.
+ */
+/*
+ * 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.
+ */
+
+
+#ifndef  _SOUND_CONFIG_H_
+#define  _SOUND_CONFIG_H_
+
+#include <linux/fs.h>
+#include <linux/sound.h>
+
+#include "os.h"
+#include "soundvers.h"
+
+
+#ifndef SND_DEFAULT_ENABLE
+#define SND_DEFAULT_ENABLE	1
+#endif
+
+#ifndef MAX_REALTIME_FACTOR
+#define MAX_REALTIME_FACTOR	4
+#endif
+
+/*
+ * Use always 64k buffer size. There is no reason to use shorter.
+ */
+#undef DSP_BUFFSIZE
+#define DSP_BUFFSIZE		(64*1024)
+
+#ifndef DSP_BUFFCOUNT
+#define DSP_BUFFCOUNT		1	/* 1 is recommended. */
+#endif
+
+#define FM_MONO		0x388	/* This is the I/O address used by AdLib */
+
+#ifndef CONFIG_PAS_BASE
+#define CONFIG_PAS_BASE	0x388
+#endif
+
+/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
+   driver. (There is no need to alter this) */
+#define SEQ_MAX_QUEUE	1024
+
+#define SBFM_MAXINSTR		(256)	/* Size of the FM Instrument bank */
+/* 128 instruments for general MIDI setup and 16 unassigned	 */
+
+#define SND_NDEVS	256	/* Number of supported devices */
+
+#define DSP_DEFAULT_SPEED	8000
+
+#define MAX_AUDIO_DEV	5
+#define MAX_MIXER_DEV	5
+#define MAX_SYNTH_DEV	5
+#define MAX_MIDI_DEV	6
+#define MAX_TIMER_DEV	4
+
+struct address_info {
+	int io_base;
+	int irq;
+	int dma;
+	int dma2;
+	int always_detect;	/* 1=Trust me, it's there */
+	char *name;
+	int driver_use_1;	/* Driver defined field 1 */
+	int driver_use_2;	/* Driver defined field 2 */
+	int *osp;	/* OS specific info */
+	int card_subtype;	/* Driver specific. Usually 0 */
+	void *memptr;           /* Module memory chainer */
+	int slots[6];           /* To remember driver slot ids */
+};
+
+#define SYNTH_MAX_VOICES	32
+
+struct voice_alloc_info {
+		int max_voice;
+		int used_voices;
+		int ptr;		/* For device specific use */
+		unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
+		int timestamp;
+		int alloc_times[SYNTH_MAX_VOICES];
+	};
+
+struct channel_info {
+		int pgm_num;
+		int bender_value;
+		int bender_range;
+		unsigned char controllers[128];
+	};
+
+/*
+ * Process wakeup reasons
+ */
+#define WK_NONE		0x00
+#define WK_WAKEUP	0x01
+#define WK_TIMEOUT	0x02
+#define WK_SIGNAL	0x04
+#define WK_SLEEP	0x08
+#define WK_SELECT	0x10
+#define WK_ABORT	0x20
+
+#define OPEN_READ	PCM_ENABLE_INPUT
+#define OPEN_WRITE	PCM_ENABLE_OUTPUT
+#define OPEN_READWRITE	(OPEN_READ|OPEN_WRITE)
+
+static inline int translate_mode(struct file *file)
+{
+	if (OPEN_READ == (__force int)FMODE_READ &&
+	    OPEN_WRITE == (__force int)FMODE_WRITE)
+		return (__force int)(file->f_mode & (FMODE_READ | FMODE_WRITE));
+	else
+		return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) |
+			((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0);
+}
+
+#include "sound_calls.h"
+#include "dev_table.h"
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+#ifndef DDB
+#define DDB(x) do {} while (0)
+#endif
+
+#ifndef MDB
+#ifdef MODULE
+#define MDB(x) x
+#else
+#define MDB(x)
+#endif
+#endif
+
+#define TIMER_ARMED	121234
+#define TIMER_NOT_ARMED	1
+
+#define MAX_MEM_BLOCKS 1024
+
+#endif
diff --git a/sound/oss/sound_firmware.h b/sound/oss/sound_firmware.h
new file mode 100644
index 00000000..0a0cbfdf
--- /dev/null
+++ b/sound/oss/sound_firmware.h
@@ -0,0 +1,2 @@
+extern int mod_firmware_load(const char *fn, char **fp);
+
diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c
new file mode 100644
index 00000000..8021c85f
--- /dev/null
+++ b/sound/oss/sound_timer.c
@@ -0,0 +1,327 @@
+/*
+ * sound/oss/sound_timer.c
+ */
+/*
+ * 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)
+ */
+#include <linux/string.h>
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+
+static volatile int initialized, opened, tmr_running;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+static volatile unsigned long usecs_per_tmr;	/* Length of the current interval */
+
+static struct sound_lowlev_timer *tmr;
+static DEFINE_SPINLOCK(lock);
+
+static unsigned long tmr2ticks(int tmr_value)
+{
+	/*
+	 *    Convert timer ticks to MIDI ticks
+	 */
+
+	unsigned long tmp;
+	unsigned long scale;
+
+	tmp = tmr_value * usecs_per_tmr;	/* Convert to usecs */
+	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
+	return (tmp + (scale / 2)) / scale;
+}
+
+void reprogram_timer(void)
+{
+	unsigned long   usecs_per_tick;
+
+	/*
+	 *	The user is changing the timer rate before setting a timer
+	 *	slap, bad bad not allowed.
+	 */
+	 
+	if(!tmr)
+		return;
+		
+	usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
+
+	/*
+	 * Don't kill the system by setting too high timer rate
+	 */
+	if (usecs_per_tick < 2000)
+		usecs_per_tick = 2000;
+
+	usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
+}
+
+void sound_timer_syncinterval(unsigned int new_usecs)
+{
+	/*
+	 *    This routine is called by the hardware level if
+	 *      the clock frequency has changed for some reason.
+	 */
+	tmr_offs = tmr_ctr;
+	ticks_offs += tmr2ticks(tmr_ctr);
+	tmr_ctr = 0;
+	usecs_per_tmr = new_usecs;
+}
+EXPORT_SYMBOL(sound_timer_syncinterval);
+
+static void tmr_reset(void)
+{
+	unsigned long   flags;
+
+	spin_lock_irqsave(&lock,flags);
+	tmr_offs = 0;
+	ticks_offs = 0;
+	tmr_ctr = 0;
+	next_event_time = (unsigned long) -1;
+	prev_event_time = 0;
+	curr_ticks = 0;
+	spin_unlock_irqrestore(&lock,flags);
+}
+
+static int timer_open(int dev, int mode)
+{
+	if (opened)
+		return -EBUSY;
+	tmr_reset();
+	curr_tempo = 60;
+	curr_timebase = 100;
+	opened = 1;
+	reprogram_timer();
+	return 0;
+}
+
+static void timer_close(int dev)
+{
+	opened = tmr_running = 0;
+	tmr->tmr_disable(tmr->dev);
+}
+
+static int timer_event(int dev, unsigned char *event)
+{
+	unsigned char cmd = event[1];
+	unsigned long parm = *(int *) &event[4];
+
+	switch (cmd)
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				if (parm <= curr_ticks)	/* It's the time */
+					return TIMER_NOT_ARMED;
+				time = parm;
+				next_event_time = prev_event_time = time;
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			tmr_reset();
+			tmr_running = 1;
+			reprogram_timer();
+			break;
+
+		case TMR_STOP:
+			tmr_running = 0;
+			break;
+
+		case TMR_CONTINUE:
+			tmr_running = 1;
+			reprogram_timer();
+			break;
+
+		case TMR_TEMPO:
+			if (parm)
+			{
+				if (parm < 8)
+					parm = 8;
+				if (parm > 250)
+					parm = 250;
+				tmr_offs = tmr_ctr;
+				ticks_offs += tmr2ticks(tmr_ctr);
+				tmr_ctr = 0;
+				curr_tempo = parm;
+				reprogram_timer();
+			}
+			break;
+
+		case TMR_ECHO:
+			seq_copy_to_input(event, 8);
+			break;
+
+		default:;
+	}
+	return TIMER_NOT_ARMED;
+}
+
+static unsigned long timer_get_time(int dev)
+{
+	if (!opened)
+		return 0;
+	return curr_ticks;
+}
+
+static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	int __user *p = arg;
+	int val;
+
+	switch (cmd) 
+	{
+		case SNDCTL_TMR_SOURCE:
+			val = TMR_INTERNAL;
+			break;
+
+		case SNDCTL_TMR_START:
+			tmr_reset();
+			tmr_running = 1;
+			return 0;
+		
+		case SNDCTL_TMR_STOP:
+			tmr_running = 0;
+			return 0;
+
+		case SNDCTL_TMR_CONTINUE:
+			tmr_running = 1;
+			return 0;
+
+		case SNDCTL_TMR_TIMEBASE:
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val) 
+			{
+				if (val < 1)
+					val = 1;
+				if (val > 1000)
+					val = 1000;
+				curr_timebase = val;
+			}
+			val = curr_timebase;
+			break;
+
+		case SNDCTL_TMR_TEMPO:
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val) 
+			{
+				if (val < 8)
+					val = 8;
+				if (val > 250)
+					val = 250;
+				tmr_offs = tmr_ctr;
+				ticks_offs += tmr2ticks(tmr_ctr);
+				tmr_ctr = 0;
+				curr_tempo = val;
+				reprogram_timer();
+			}
+			val = curr_tempo;
+			break;
+
+		case SNDCTL_SEQ_CTRLRATE:
+			if (get_user(val, p))
+				return -EFAULT;
+			if (val != 0)	/* Can't change */
+				return -EINVAL;
+			val = ((curr_tempo * curr_timebase) + 30) / 60;
+			break;
+		
+		case SNDCTL_SEQ_GETTIME:
+			val = curr_ticks;
+			break;
+		
+		case SNDCTL_TMR_METRONOME:
+		default:
+			return -EINVAL;
+	}
+	return put_user(val, p);
+}
+
+static void timer_arm(int dev, long time)
+{
+	if (time < 0)
+		time = curr_ticks + 1;
+	else if (time <= curr_ticks)	/* It's the time */
+		return;
+
+	next_event_time = prev_event_time = time;
+	return;
+}
+
+static struct sound_timer_operations sound_timer =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"Sound Timer", 0},
+	.priority	= 1,	/* Priority */
+	.devlink	= 0,	/* Local device link */
+	.open		= timer_open,
+	.close		= timer_close,
+	.event		= timer_event,
+	.get_time	= timer_get_time,
+	.ioctl		= timer_ioctl,
+	.arm_timer	= timer_arm
+};
+
+void sound_timer_interrupt(void)
+{
+	unsigned long flags;
+	
+	if (!opened)
+		return;
+
+	tmr->tmr_restart(tmr->dev);
+
+	if (!tmr_running)
+		return;
+
+	spin_lock_irqsave(&lock,flags);
+	tmr_ctr++;
+	curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
+
+	if (curr_ticks >= next_event_time)
+	{
+		next_event_time = (unsigned long) -1;
+		sequencer_timer(0);
+	}
+	spin_unlock_irqrestore(&lock,flags);
+}
+EXPORT_SYMBOL(sound_timer_interrupt);
+
+void  sound_timer_init(struct sound_lowlev_timer *t, char *name)
+{
+	int n;
+
+	if (initialized)
+	{
+		if (t->priority <= tmr->priority)
+			return;	/* There is already a similar or better timer */
+		tmr = t;
+		return;
+	}
+	initialized = 1;
+	tmr = t;
+
+	n = sound_alloc_timerdev();
+	if (n == -1)
+		n = 0;		/* Overwrite the system timer */
+	strlcpy(sound_timer.info.name, name, sizeof(sound_timer.info.name));
+	sound_timer_devs[n] = &sound_timer;
+}
+EXPORT_SYMBOL(sound_timer_init);
+
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
new file mode 100644
index 00000000..7c7793a0
--- /dev/null
+++ b/sound/oss/soundcard.c
@@ -0,0 +1,739 @@
+/*
+ * linux/sound/oss/soundcard.c
+ *
+ * Sound card driver for Linux
+ *
+ *
+ * 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)
+ *                   integrated sound_switch.c
+ * Stefan Reinauer   : integrated /proc/sound (equals to /dev/sndstat,
+ *                   which should disappear in the near future)
+ * Eric Dumas	     : devfs support (22-Jan-98) <dumas@linux.eu.org> with
+ *                   fixups by C. Scott Ananian <cananian@alumni.princeton.edu>
+ * Richard Gooch     : moved common (non OSS-specific) devices to sound_core.c
+ * Rob Riggs	     : Added persistent DMA buffers support (1998/10/17)
+ * Christoph Hellwig : Some cleanup work (2000/03/01)
+ */
+
+
+#include "sound_config.h"
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/wait.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+
+/*
+ * This ought to be moved into include/asm/dma.h
+ */
+#ifndef valid_dma
+#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4)
+#endif
+
+/*
+ * Table for permanently allocated memory (used when unloading the module)
+ */
+void *          sound_mem_blocks[MAX_MEM_BLOCKS];
+static DEFINE_MUTEX(soundcard_mutex);
+int             sound_nblocks = 0;
+
+/* Persistent DMA buffers */
+#ifdef CONFIG_SOUND_DMAP
+int             sound_dmap_flag = 1;
+#else
+int             sound_dmap_flag = 0;
+#endif
+
+static char     dma_alloc_map[MAX_DMA_CHANNELS];
+
+#define DMA_MAP_UNAVAIL		0
+#define DMA_MAP_FREE		1
+#define DMA_MAP_BUSY		2
+
+
+unsigned long seq_time = 0;	/* Time for /dev/sequencer */
+extern struct class *sound_class;
+
+/*
+ * Table for configurable mixer volume handling
+ */
+static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
+static int num_mixer_volumes;
+
+int *load_mixer_volumes(char *name, int *levels, int present)
+{
+	int             i, n;
+
+	for (i = 0; i < num_mixer_volumes; i++) {
+		if (strncmp(name, mixer_vols[i].name, 32) == 0) {
+			if (present)
+				mixer_vols[i].num = i;
+			return mixer_vols[i].levels;
+		}
+	}
+	if (num_mixer_volumes >= MAX_MIXER_DEV) {
+		printk(KERN_ERR "Sound: Too many mixers (%s)\n", name);
+		return levels;
+	}
+	n = num_mixer_volumes++;
+
+	strncpy(mixer_vols[n].name, name, 32);
+
+	if (present)
+		mixer_vols[n].num = n;
+	else
+		mixer_vols[n].num = -1;
+
+	for (i = 0; i < 32; i++)
+		mixer_vols[n].levels[i] = levels[i];
+	return mixer_vols[n].levels;
+}
+EXPORT_SYMBOL(load_mixer_volumes);
+
+static int set_mixer_levels(void __user * arg)
+{
+        /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */
+	mixer_vol_table buf;   
+
+	if (__copy_from_user(&buf, arg, sizeof(buf)))
+		return -EFAULT;
+	load_mixer_volumes(buf.name, buf.levels, 0);
+	if (__copy_to_user(arg, &buf, sizeof(buf)))
+		return -EFAULT;
+	return 0;
+}
+
+static int get_mixer_levels(void __user * arg)
+{
+	int n;
+
+	if (__get_user(n, (int __user *)(&(((mixer_vol_table __user *)arg)->num))))
+		return -EFAULT;
+	if (n < 0 || n >= num_mixer_volumes)
+		return -EINVAL;
+	if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table)))
+		return -EFAULT;
+	return 0;
+}
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+static ssize_t sound_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	int dev = iminor(file->f_path.dentry->d_inode);
+	int ret = -EINVAL;
+
+	/*
+	 *	The OSS drivers aren't remotely happy without this locking,
+	 *	and unless someone fixes them when they are about to bite the
+	 *	big one anyway, we might as well bandage here..
+	 */
+	 
+	mutex_lock(&soundcard_mutex);
+	
+	DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		ret = audio_read(dev, file, buf, count);
+		break;
+
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		ret = sequencer_read(dev, file, buf, count);
+		break;
+
+	case SND_DEV_MIDIN:
+		ret = MIDIbuf_read(dev, file, buf, count);
+	}
+	mutex_unlock(&soundcard_mutex);
+	return ret;
+}
+
+static ssize_t sound_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+	int dev = iminor(file->f_path.dentry->d_inode);
+	int ret = -EINVAL;
+	
+	mutex_lock(&soundcard_mutex);
+	DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		ret =  sequencer_write(dev, file, buf, count);
+		break;
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		ret = audio_write(dev, file, buf, count);
+		break;
+
+	case SND_DEV_MIDIN:
+		ret =  MIDIbuf_write(dev, file, buf, count);
+		break;
+	}
+	mutex_unlock(&soundcard_mutex);
+	return ret;
+}
+
+static int sound_open(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+	int retval;
+
+	DEB(printk("sound_open(dev=%d)\n", dev));
+	if ((dev >= SND_NDEVS) || (dev < 0)) {
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		return -ENXIO;
+	}
+	mutex_lock(&soundcard_mutex);
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		dev >>= 4;
+		if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
+			request_module("mixer%d", dev);
+		}
+		retval = -ENXIO;
+		if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
+			break;
+	
+		if (!try_module_get(mixer_devs[dev]->owner))
+			break;
+
+		retval = 0;
+		break;
+
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		retval = sequencer_open(dev, file);
+		break;
+
+	case SND_DEV_MIDIN:
+		retval = MIDIbuf_open(dev, file);
+		break;
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		retval = audio_open(dev, file);
+		break;
+
+	default:
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		retval = -ENXIO;
+	}
+
+	mutex_unlock(&soundcard_mutex);
+	return retval;
+}
+
+static int sound_release(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+
+	mutex_lock(&soundcard_mutex);
+	DEB(printk("sound_release(dev=%d)\n", dev));
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		module_put(mixer_devs[dev >> 4]->owner);
+		break;
+		
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		sequencer_release(dev, file);
+		break;
+
+	case SND_DEV_MIDIN:
+		MIDIbuf_release(dev, file);
+		break;
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		audio_release(dev, file);
+		break;
+
+	default:
+		printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
+	}
+	mutex_unlock(&soundcard_mutex);
+
+	return 0;
+}
+
+static int get_mixer_info(int dev, void __user *arg)
+{
+	mixer_info info;
+	memset(&info, 0, sizeof(info));
+	strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
+	strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
+	info.modify_counter = mixer_devs[dev]->modify_counter;
+	if (__copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int get_old_mixer_info(int dev, void __user *arg)
+{
+	_old_mixer_info info;
+	memset(&info, 0, sizeof(info));
+ 	strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
+ 	strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
+ 	if (copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int sound_mixer_ioctl(int mixdev, unsigned int cmd, void __user *arg)
+{
+ 	if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
+ 		return -ENXIO;
+ 	/* Try to load the mixer... */
+ 	if (mixer_devs[mixdev] == NULL) {
+ 		request_module("mixer%d", mixdev);
+ 	}
+ 	if (mixdev >= num_mixers || !mixer_devs[mixdev])
+ 		return -ENXIO;
+	if (cmd == SOUND_MIXER_INFO)
+		return get_mixer_info(mixdev, arg);
+	if (cmd == SOUND_OLD_MIXER_INFO)
+		return get_old_mixer_info(mixdev, arg);
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+		mixer_devs[mixdev]->modify_counter++;
+	if (!mixer_devs[mixdev]->ioctl)
+		return -EINVAL;
+	return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
+}
+
+static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int len = 0, dtype;
+	int dev = iminor(file->f_dentry->d_inode);
+	long ret = -EINVAL;
+	void __user *p = (void __user *)arg;
+
+	if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
+		/*
+		 * Have to validate the address given by the process.
+		 */
+		len = _SIOC_SIZE(cmd);
+		if (len < 1 || len > 65536 || !p)
+			return -EFAULT;
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+			if (!access_ok(VERIFY_READ, p, len))
+				return -EFAULT;
+		if (_SIOC_DIR(cmd) & _SIOC_READ)
+			if (!access_ok(VERIFY_WRITE, p, len))
+				return -EFAULT;
+	}
+	DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+	if (cmd == OSS_GETVERSION)
+		return __put_user(SOUND_VERSION, (int __user *)p);
+	
+	mutex_lock(&soundcard_mutex);
+	if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 &&   /* Mixer ioctl */
+	    (dev & 0x0f) != SND_DEV_CTL) {              
+		dtype = dev & 0x0f;
+		switch (dtype) {
+		case SND_DEV_DSP:
+		case SND_DEV_DSP16:
+		case SND_DEV_AUDIO:
+			ret = sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
+						 cmd, p);
+			break;			
+		default:
+			ret = sound_mixer_ioctl(dev >> 4, cmd, p);
+			break;
+		}
+		mutex_unlock(&soundcard_mutex);
+		return ret;
+	}
+
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		if (cmd == SOUND_MIXER_GETLEVELS)
+			ret = get_mixer_levels(p);
+		else if (cmd == SOUND_MIXER_SETLEVELS)
+			ret = set_mixer_levels(p);
+		else
+			ret = sound_mixer_ioctl(dev >> 4, cmd, p);
+		break;
+
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		ret = sequencer_ioctl(dev, file, cmd, p);
+		break;
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		ret = audio_ioctl(dev, file, cmd, p);
+		break;
+
+	case SND_DEV_MIDIN:
+		ret = MIDIbuf_ioctl(dev, file, cmd, p);
+		break;
+
+	}
+	mutex_unlock(&soundcard_mutex);
+	return ret;
+}
+
+static unsigned int sound_poll(struct file *file, poll_table * wait)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	int dev = iminor(inode);
+
+	DEB(printk("sound_poll(dev=%d)\n", dev));
+	switch (dev & 0x0f) {
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_poll(dev, file, wait);
+
+	case SND_DEV_MIDIN:
+		return MIDIbuf_poll(dev, file, wait);
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return DMAbuf_poll(file, dev >> 4, wait);
+	}
+	return 0;
+}
+
+static int sound_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	int dev_class;
+	unsigned long size;
+	struct dma_buffparms *dmap = NULL;
+	int dev = iminor(file->f_path.dentry->d_inode);
+
+	dev_class = dev & 0x0f;
+	dev >>= 4;
+
+	if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) {
+		printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
+		return -EINVAL;
+	}
+	mutex_lock(&soundcard_mutex);
+	if (vma->vm_flags & VM_WRITE)	/* Map write and read/write to the output buf */
+		dmap = audio_devs[dev]->dmap_out;
+	else if (vma->vm_flags & VM_READ)
+		dmap = audio_devs[dev]->dmap_in;
+	else {
+		printk(KERN_ERR "Sound: Undefined mmap() access\n");
+		mutex_unlock(&soundcard_mutex);
+		return -EINVAL;
+	}
+
+	if (dmap == NULL) {
+		printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
+		mutex_unlock(&soundcard_mutex);
+		return -EIO;
+	}
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
+		mutex_unlock(&soundcard_mutex);
+		return -EIO;
+	}
+	if (dmap->mapping_flags) {
+		printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
+		mutex_unlock(&soundcard_mutex);
+		return -EIO;
+	}
+	if (vma->vm_pgoff != 0) {
+		printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
+		mutex_unlock(&soundcard_mutex);
+		return -EINVAL;
+	}
+	size = vma->vm_end - vma->vm_start;
+
+	if (size != dmap->bytes_in_use) {
+		printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
+	}
+	if (remap_pfn_range(vma, vma->vm_start,
+			virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT,
+			vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+		mutex_unlock(&soundcard_mutex);
+		return -EAGAIN;
+	}
+
+	dmap->mapping_flags |= DMA_MAP_MAPPED;
+
+	if( audio_devs[dev]->d->mmap)
+		audio_devs[dev]->d->mmap(dev);
+
+	memset(dmap->raw_buf,
+	       dmap->neutral_byte,
+	       dmap->bytes_in_use);
+	mutex_unlock(&soundcard_mutex);
+	return 0;
+}
+
+const struct file_operations oss_sound_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= sound_read,
+	.write		= sound_write,
+	.poll		= sound_poll,
+	.unlocked_ioctl	= sound_ioctl,
+	.mmap		= sound_mmap,
+	.open		= sound_open,
+	.release	= sound_release,
+};
+
+/*
+ *	Create the required special subdevices
+ */
+ 
+static int create_special_devices(void)
+{
+	int seq1,seq2;
+	seq1=register_sound_special(&oss_sound_fops, 1);
+	if(seq1==-1)
+		goto bad;
+	seq2=register_sound_special(&oss_sound_fops, 8);
+	if(seq2!=-1)
+		return 0;
+	unregister_sound_special(1);
+bad:
+	return -1;
+}
+
+
+static int dmabuf;
+static int dmabug;
+
+module_param(dmabuf, int, 0444);
+module_param(dmabug, int, 0444);
+
+/* additional minors for compatibility */
+struct oss_minor_dev {
+	unsigned short minor;
+	unsigned int enabled;
+} dev_list[] = {
+	{ SND_DEV_DSP16 },
+	{ SND_DEV_AUDIO },
+};
+
+static int __init oss_init(void)
+{
+	int             err;
+	int i, j;
+	
+#ifdef CONFIG_PCI
+	if(dmabug)
+		isa_dma_bridge_buggy = dmabug;
+#endif
+
+	err = create_special_devices();
+	if (err) {
+		printk(KERN_ERR "sound: driver already loaded/included in kernel\n");
+		return err;
+	}
+
+	/* Protecting the innocent */
+	sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
+
+	for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
+		j = 0;
+		do {
+			unsigned short minor = dev_list[i].minor + j * 0x10;
+			if (!register_sound_special(&oss_sound_fops, minor))
+				dev_list[i].enabled = (1 << j);
+		} while (++j < num_audiodevs);
+	}
+
+	if (sound_nblocks >= MAX_MEM_BLOCKS - 1)
+		printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
+	
+	return 0;
+}
+
+static void __exit oss_cleanup(void)
+{
+	int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
+		j = 0;
+		do {
+			if (dev_list[i].enabled & (1 << j))
+				unregister_sound_special(dev_list[i].minor);
+		} while (++j < num_audiodevs);
+	}
+	
+	unregister_sound_special(1);
+	unregister_sound_special(8);
+
+	sound_stop_timer();
+
+	sequencer_unload();
+
+	for (i = 0; i < MAX_DMA_CHANNELS; i++)
+		if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) {
+			printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i);
+			sound_free_dma(i);
+		}
+
+	for (i = 0; i < sound_nblocks; i++)
+		vfree(sound_mem_blocks[i]);
+
+}
+
+module_init(oss_init);
+module_exit(oss_cleanup);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OSS Sound subsystem");
+MODULE_AUTHOR("Hannu Savolainen, et al.");
+
+
+int sound_alloc_dma(int chn, char *deviceID)
+{
+	int err;
+
+	if ((err = request_dma(chn, deviceID)) != 0)
+		return err;
+
+	dma_alloc_map[chn] = DMA_MAP_FREE;
+
+	return 0;
+}
+EXPORT_SYMBOL(sound_alloc_dma);
+
+int sound_open_dma(int chn, char *deviceID)
+{
+	if (!valid_dma(chn)) {
+		printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn);
+		return 1;
+	}
+
+	if (dma_alloc_map[chn] != DMA_MAP_FREE) {
+		printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
+		return 1;
+	}
+	dma_alloc_map[chn] = DMA_MAP_BUSY;
+	return 0;
+}
+EXPORT_SYMBOL(sound_open_dma);
+
+void sound_free_dma(int chn)
+{
+	if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) {
+		/* printk( "sound_free_dma: Bad access to DMA channel %d\n",  chn); */
+		return;
+	}
+	free_dma(chn);
+	dma_alloc_map[chn] = DMA_MAP_UNAVAIL;
+}
+EXPORT_SYMBOL(sound_free_dma);
+
+void sound_close_dma(int chn)
+{
+	if (dma_alloc_map[chn] != DMA_MAP_BUSY) {
+		printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn);
+		return;
+	}
+	dma_alloc_map[chn] = DMA_MAP_FREE;
+}
+EXPORT_SYMBOL(sound_close_dma);
+
+static void do_sequencer_timer(unsigned long dummy)
+{
+	sequencer_timer(0);
+}
+
+
+static DEFINE_TIMER(seq_timer, do_sequencer_timer, 0, 0);
+
+void request_sound_timer(int count)
+{
+	extern unsigned long seq_time;
+
+	if (count < 0) {
+		seq_timer.expires = (-count) + jiffies;
+		add_timer(&seq_timer);
+		return;
+	}
+	count += seq_time;
+
+	count -= jiffies;
+
+	if (count < 1)
+		count = 1;
+
+	seq_timer.expires = (count) + jiffies;
+	add_timer(&seq_timer);
+}
+
+void sound_stop_timer(void)
+{
+	del_timer(&seq_timer);
+}
+
+void conf_printf(char *name, struct address_info *hw_config)
+{
+#ifndef CONFIG_SOUND_TRACEINIT
+	return;
+#else
+	printk("<%s> at 0x%03x", name, hw_config->io_base);
+
+	if (hw_config->irq)
+		printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
+
+	if (hw_config->dma != -1 || hw_config->dma2 != -1)
+	{
+		printk(" dma %d", hw_config->dma);
+		if (hw_config->dma2 != -1)
+			printk(",%d", hw_config->dma2);
+	}
+	printk("\n");
+#endif
+}
+EXPORT_SYMBOL(conf_printf);
+
+void conf_printf2(char *name, int base, int irq, int dma, int dma2)
+{
+#ifndef CONFIG_SOUND_TRACEINIT
+	return;
+#else
+	printk("<%s> at 0x%03x", name, base);
+
+	if (irq)
+		printk(" irq %d", (irq > 0) ? irq : -irq);
+
+	if (dma != -1 || dma2 != -1)
+	{
+		  printk(" dma %d", dma);
+		  if (dma2 != -1)
+			  printk(",%d", dma2);
+	}
+	printk("\n");
+#endif
+}
+EXPORT_SYMBOL(conf_printf2);
+
diff --git a/sound/oss/soundvers.h b/sound/oss/soundvers.h
new file mode 100644
index 00000000..e9084d2f
--- /dev/null
+++ b/sound/oss/soundvers.h
@@ -0,0 +1,2 @@
+#define SOUND_VERSION_STRING "3.8s2++-971130"
+#define SOUND_INTERNAL_VERSION 0x030804
diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c
new file mode 100644
index 00000000..09d46484
--- /dev/null
+++ b/sound/oss/swarm_cs4297a.c
@@ -0,0 +1,2768 @@
+/*******************************************************************************
+*
+*      "swarm_cs4297a.c" --  Cirrus Logic-Crystal CS4297a linux audio driver.
+*
+*      Copyright (C) 2001  Broadcom Corporation.
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- adapted from drivers by Thomas Sailer, 
+*            -- but don't bug him; Problems should go to:
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (audio@crystal.cirrus.com).
+*            -- adapted from cs4281 PCI driver for cs4297a on
+*               BCM1250 Synchronous Serial interface
+*               (Kip Walker, Broadcom Corp.)
+*      Copyright (C) 2004  Maciej W. Rozycki
+*      Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org)
+*
+*      This program is free software; you can redistribute it and/or modify
+*      it under the terms of the GNU General Public License as published by
+*      the Free Software Foundation; either version 2 of the License, or
+*      (at your option) any later version.
+*
+*      This program is distributed in the hope that it will be useful,
+*      but WITHOUT ANY WARRANTY; without even the implied warranty of
+*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*      GNU General Public License for more details.
+*
+*      You should have received a copy of the GNU General Public License
+*      along with this program; if not, write to the Free Software
+*      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Module command line parameters:
+*   none
+*
+*  Supported devices:
+*  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+*  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+*  /dev/midi   simple MIDI UART interface, no ioctl
+*
+* Modification History
+* 08/20/00 trw - silence and no stopping DAC until release
+* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop.
+* 09/18/00 trw - added 16bit only record with conversion 
+* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous 
+*                capture/playback rates)
+* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin  
+*                libOSSm.so)
+* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal)
+* 11/03/00 trw - fixed interrupt loss/stutter, added debug.
+* 11/10/00 bkz - added __devinit to cs4297a_hw_init()
+* 11/10/00 trw - fixed SMP and capture spinlock hang.
+* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm.
+* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix.
+* 12/08/00 trw - added PM support. 
+* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 
+*		 (RH/Dell base), 2.2.18, 2.2.12.  cleaned up code mods by ident.
+* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup.
+* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use 
+*		 defaultorder-100 as power of 2 for the buffer size. example:
+*		 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.
+*
+*******************************************************************************/
+
+#include <linux/list.h>
+#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/ac97_codec.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/sb1250_dma.h>
+#include <asm/sibyte/sb1250_scd.h>
+#include <asm/sibyte/sb1250_syncser.h>
+#include <asm/sibyte/sb1250_mac.h>
+#include <asm/sibyte/sb1250.h>
+
+struct cs4297a_state;
+
+static DEFINE_MUTEX(swarm_cs4297a_mutex);
+static void stop_dac(struct cs4297a_state *s);
+static void stop_adc(struct cs4297a_state *s);
+static void start_dac(struct cs4297a_state *s);
+static void start_adc(struct cs4297a_state *s);
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+// --------------------------------------------------------------------- 
+
+#define CS4297a_MAGIC           0xf00beef1
+
+// buffer order determines the size of the dma buffer for the driver.
+// under Linux, a smaller buffer allows more responsiveness from many of the 
+// applications (e.g. games).  A larger buffer allows some of the apps (esound) 
+// to not underrun the dma buffer as easily.  As default, use 32k (order=3)
+// rather than 64k as some of the games work more responsively.
+// log base 2( buff sz = 32k).
+
+//
+// Turn on/off debugging compilation by commenting out "#define CSDEBUG"
+//
+#define CSDEBUG 0
+#if CSDEBUG
+#define CSDEBUG_INTERFACE 1
+#else
+#undef CSDEBUG_INTERFACE
+#endif
+//
+// cs_debugmask areas
+//
+#define CS_INIT	 	0x00000001	// initialization and probe functions
+#define CS_ERROR 	0x00000002	// tmp debugging bit placeholder
+#define CS_INTERRUPT	0x00000004	// interrupt handler (separate from all other)
+#define CS_FUNCTION 	0x00000008	// enter/leave functions
+#define CS_WAVE_WRITE 	0x00000010	// write information for wave
+#define CS_WAVE_READ 	0x00000020	// read information for wave
+#define CS_AC97         0x00000040      // AC97 register access
+#define CS_DESCR        0x00000080      // descriptor management
+#define CS_OPEN		0x00000400	// all open functions in the driver
+#define CS_RELEASE	0x00000800	// all release functions in the driver
+#define CS_PARMS	0x00001000	// functional and operational parameters
+#define CS_IOCTL	0x00002000	// ioctl (non-mixer)
+#define CS_TMP		0x10000000	// tmp debug mask bit
+
+//
+// CSDEBUG is usual mode is set to 1, then use the
+// cs_debuglevel and cs_debugmask to turn on or off debugging.
+// Debug level of 1 has been defined to be kernel errors and info
+// that should be printed on any released driver.
+//
+#if CSDEBUG
+#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;}
+#else
+#define CS_DBGOUT(mask,level,x)
+#endif
+
+#if CSDEBUG
+static unsigned long cs_debuglevel = 4;	// levels range from 1-9
+static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/;
+module_param(cs_debuglevel, int, 0);
+module_param(cs_debugmask, int, 0);
+#endif
+#define CS_TRUE 	1
+#define CS_FALSE 	0
+
+#define CS_TYPE_ADC 0
+#define CS_TYPE_DAC 1
+
+#define SER_BASE    (A_SER_BASE_1 + KSEG1)
+#define SS_CSR(t)   (SER_BASE+t)
+#define SS_TXTBL(t) (SER_BASE+R_SER_TX_TABLE_BASE+(t*8))
+#define SS_RXTBL(t) (SER_BASE+R_SER_RX_TABLE_BASE+(t*8))
+
+#define FRAME_BYTES            32
+#define FRAME_SAMPLE_BYTES      4
+
+/* Should this be variable? */
+#define SAMPLE_BUF_SIZE        (16*1024)
+#define SAMPLE_FRAME_COUNT     (SAMPLE_BUF_SIZE / FRAME_SAMPLE_BYTES)
+/* The driver can explode/shrink the frames to/from a smaller sample
+   buffer */
+#define DMA_BLOAT_FACTOR       1
+#define DMA_DESCR              (SAMPLE_FRAME_COUNT / DMA_BLOAT_FACTOR)
+#define DMA_BUF_SIZE           (DMA_DESCR * FRAME_BYTES)
+
+/* Use the maxmium count (255 == 5.1 ms between interrupts) */
+#define DMA_INT_CNT            ((1 << S_DMA_INT_PKTCNT) - 1)
+
+/* Figure this out: how many TX DMAs ahead to schedule a reg access */
+#define REG_LATENCY            150
+
+#define FRAME_TX_US             20
+
+#define SERDMA_NEXTBUF(d,f) (((d)->f+1) % (d)->ringsz)
+
+static const char invalid_magic[] =
+    KERN_CRIT "cs4297a: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                          \
+({                                                 \
+        if (!(s) || (s)->magic != CS4297a_MAGIC) { \
+                printk(invalid_magic);             \
+                return -ENXIO;                     \
+        }                                          \
+})
+
+struct list_head cs4297a_devs = { &cs4297a_devs, &cs4297a_devs };
+
+typedef struct serdma_descr_s {
+        u64 descr_a;
+        u64 descr_b;
+} serdma_descr_t;
+
+typedef unsigned long paddr_t;
+
+typedef struct serdma_s {
+        unsigned         ringsz;
+        serdma_descr_t  *descrtab;
+        serdma_descr_t  *descrtab_end;
+        paddr_t          descrtab_phys;
+        
+        serdma_descr_t  *descr_add;
+        serdma_descr_t  *descr_rem;
+        
+        u64  *dma_buf;           // buffer for DMA contents (frames)
+        paddr_t          dma_buf_phys;
+        u16  *sample_buf;		// tmp buffer for sample conversions
+        u16  *sb_swptr;
+        u16  *sb_hwptr;
+        u16  *sb_end;
+
+        dma_addr_t dmaaddr;
+//        unsigned buforder;	// Log base 2 of 'dma_buf' size in bytes..
+        unsigned numfrag;	// # of 'fragments' in the buffer.
+        unsigned fragshift;	// Log base 2 of fragment size.
+        unsigned hwptr, swptr;
+        unsigned total_bytes;	// # bytes process since open.
+        unsigned blocks;	// last returned blocks value GETOPTR
+        unsigned wakeup;	// interrupt occurred on block 
+        int count;
+        unsigned underrun;	// underrun flag
+        unsigned error;	// over/underrun 
+        wait_queue_head_t wait;
+        wait_queue_head_t reg_wait;
+        // redundant, but makes calculations easier 
+        unsigned fragsize;	// 2**fragshift..
+        unsigned sbufsz;	// 2**buforder.
+        unsigned fragsamples;
+        // OSS stuff 
+        unsigned mapped:1;	// Buffer mapped in cs4297a_mmap()?
+        unsigned ready:1;	// prog_dmabuf_dac()/adc() successful?
+        unsigned endcleared:1;
+        unsigned type:1;	// adc or dac buffer (CS_TYPE_XXX)
+        unsigned ossfragshift;
+        int ossmaxfrags;
+        unsigned subdivision;
+} serdma_t;
+
+struct cs4297a_state {
+	// magic 
+	unsigned int magic;
+
+	struct list_head list;
+
+	// soundcore stuff 
+	int dev_audio;
+	int dev_mixer;
+
+	// hardware resources 
+	unsigned int irq;
+
+        struct {
+                unsigned int rx_ovrrn; /* FIFO */
+                unsigned int rx_overflow; /* staging buffer */
+                unsigned int tx_underrun;
+                unsigned int rx_bad;
+                unsigned int rx_good;
+        } stats;
+
+	// mixer registers 
+	struct {
+		unsigned short vol[10];
+		unsigned int recsrc;
+		unsigned int modcnt;
+		unsigned short micpreamp;
+	} mix;
+
+	// wave stuff   
+	struct properties {
+		unsigned fmt;
+		unsigned fmt_original;	// original requested format
+		unsigned channels;
+		unsigned rate;
+	} prop_dac, prop_adc;
+	unsigned conversion:1;	// conversion from 16 to 8 bit in progress
+	unsigned ena;
+	spinlock_t lock;
+	struct mutex open_mutex;
+	struct mutex open_sem_adc;
+	struct mutex open_sem_dac;
+	fmode_t open_mode;
+	wait_queue_head_t open_wait;
+	wait_queue_head_t open_wait_adc;
+	wait_queue_head_t open_wait_dac;
+
+	dma_addr_t dmaaddr_sample_buf;
+	unsigned buforder_sample_buf;	// Log base 2 of 'dma_buf' size in bytes..
+
+        serdma_t dma_dac, dma_adc;
+
+        volatile u16 read_value;
+        volatile u16 read_reg;
+        volatile u64 reg_request;
+};
+
+#if 1
+#define prog_codec(a,b)
+#define dealloc_dmabuf(a,b);
+#endif
+
+static int prog_dmabuf_adc(struct cs4297a_state *s)
+{
+	s->dma_adc.ready = 1;
+	return 0;
+}
+
+
+static int prog_dmabuf_dac(struct cs4297a_state *s)
+{
+	s->dma_dac.ready = 1;
+	return 0;
+}
+
+static void clear_advance(void *buf, unsigned bsize, unsigned bptr,
+			  unsigned len, unsigned char c)
+{
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(((char *) buf) + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO
+		"cs4297a: clear_advance(): memset %d at 0x%.8x for %d size \n",
+			(unsigned)c, (unsigned)((char *) buf) + bptr, len));
+	memset(((char *) buf) + bptr, c, len);
+}
+
+#if CSDEBUG
+
+// DEBUG ROUTINES
+
+#define SOUND_MIXER_CS_GETDBGLEVEL 	_SIOWR('M',120, int)
+#define SOUND_MIXER_CS_SETDBGLEVEL 	_SIOWR('M',121, int)
+#define SOUND_MIXER_CS_GETDBGMASK 	_SIOWR('M',122, int)
+#define SOUND_MIXER_CS_SETDBGMASK 	_SIOWR('M',123, int)
+
+static void cs_printioctl(unsigned int x)
+{
+	unsigned int i;
+	unsigned char vidx;
+	// Index of mixtable1[] member is Device ID 
+	// and must be <= SOUND_MIXER_NRDEVICES.
+	// Value of array member is index into s->mix.vol[]
+	static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
+		[SOUND_MIXER_PCM] = 1,	// voice 
+		[SOUND_MIXER_LINE1] = 2,	// AUX
+		[SOUND_MIXER_CD] = 3,	// CD 
+		[SOUND_MIXER_LINE] = 4,	// Line 
+		[SOUND_MIXER_SYNTH] = 5,	// FM
+		[SOUND_MIXER_MIC] = 6,	// Mic 
+		[SOUND_MIXER_SPEAKER] = 7,	// Speaker 
+		[SOUND_MIXER_RECLEV] = 8,	// Recording level 
+		[SOUND_MIXER_VOLUME] = 9	// Master Volume 
+	};
+
+	switch (x) {
+	case SOUND_MIXER_CS_GETDBGMASK:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_GETDBGMASK:\n"));
+		break;
+	case SOUND_MIXER_CS_GETDBGLEVEL:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_GETDBGLEVEL:\n"));
+		break;
+	case SOUND_MIXER_CS_SETDBGMASK:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_SETDBGMASK:\n"));
+		break;
+	case SOUND_MIXER_CS_SETDBGLEVEL:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_SETDBGLEVEL:\n"));
+		break;
+	case OSS_GETVERSION:
+		CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n"));
+		break;
+	case SNDCTL_DSP_SYNC:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n"));
+		break;
+	case SNDCTL_DSP_SETDUPLEX:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n"));
+		break;
+	case SNDCTL_DSP_GETCAPS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n"));
+		break;
+	case SNDCTL_DSP_RESET:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n"));
+		break;
+	case SNDCTL_DSP_SPEED:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n"));
+		break;
+	case SNDCTL_DSP_STEREO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n"));
+		break;
+	case SNDCTL_DSP_CHANNELS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n"));
+		break;
+	case SNDCTL_DSP_GETFMTS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n"));
+		break;
+	case SNDCTL_DSP_SETFMT:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n"));
+		break;
+	case SNDCTL_DSP_POST:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n"));
+		break;
+	case SNDCTL_DSP_GETTRIGGER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n"));
+		break;
+	case SNDCTL_DSP_SETTRIGGER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n"));
+		break;
+	case SNDCTL_DSP_GETOSPACE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n"));
+		break;
+	case SNDCTL_DSP_GETISPACE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n"));
+		break;
+	case SNDCTL_DSP_NONBLOCK:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n"));
+		break;
+	case SNDCTL_DSP_GETODELAY:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n"));
+		break;
+	case SNDCTL_DSP_GETIPTR:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n"));
+		break;
+	case SNDCTL_DSP_GETOPTR:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n"));
+		break;
+	case SNDCTL_DSP_GETBLKSIZE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n"));
+		break;
+	case SNDCTL_DSP_SETFRAGMENT:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SNDCTL_DSP_SETFRAGMENT:\n"));
+		break;
+	case SNDCTL_DSP_SUBDIVIDE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n"));
+		break;
+	case SOUND_PCM_READ_RATE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n"));
+		break;
+	case SOUND_PCM_READ_CHANNELS:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_PCM_READ_CHANNELS:\n"));
+		break;
+	case SOUND_PCM_READ_BITS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n"));
+		break;
+	case SOUND_PCM_WRITE_FILTER:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_PCM_WRITE_FILTER:\n"));
+		break;
+	case SNDCTL_DSP_SETSYNCRO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n"));
+		break;
+	case SOUND_PCM_READ_FILTER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE1:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE2:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE3:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE4:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE5:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n"));
+		break;
+	case SOUND_MIXER_INFO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n"));
+		break;
+	case SOUND_OLD_MIXER_INFO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n"));
+		break;
+
+	default:
+		switch (_IOC_NR(x)) {
+		case SOUND_MIXER_VOLUME:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_VOLUME:\n"));
+			break;
+		case SOUND_MIXER_SPEAKER:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_SPEAKER:\n"));
+			break;
+		case SOUND_MIXER_RECLEV:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_RECLEV:\n"));
+			break;
+		case SOUND_MIXER_MIC:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_MIC:\n"));
+			break;
+		case SOUND_MIXER_SYNTH:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_SYNTH:\n"));
+			break;
+		case SOUND_MIXER_RECSRC:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_RECSRC:\n"));
+			break;
+		case SOUND_MIXER_DEVMASK:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_DEVMASK:\n"));
+			break;
+		case SOUND_MIXER_RECMASK:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_RECMASK:\n"));
+			break;
+		case SOUND_MIXER_STEREODEVS:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_STEREODEVS:\n"));
+			break;
+		case SOUND_MIXER_CAPS:
+			CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n"));
+			break;
+		default:
+			i = _IOC_NR(x);
+			if (i >= SOUND_MIXER_NRDEVICES
+			    || !(vidx = mixtable1[i])) {
+				CS_DBGOUT(CS_IOCTL, 4, printk
+					("UNKNOWN IOCTL: 0x%.8x NR=%d\n",
+						x, i));
+			} else {
+				CS_DBGOUT(CS_IOCTL, 4, printk
+					("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n",
+						x, i));
+			}
+			break;
+		}
+	}
+}
+#endif
+
+
+static int ser_init(struct cs4297a_state *s)
+{
+        int i;
+
+        CS_DBGOUT(CS_INIT, 2, 
+                  printk(KERN_INFO "cs4297a: Setting up serial parameters\n"));
+
+        __raw_writeq(M_SYNCSER_CMD_RX_RESET | M_SYNCSER_CMD_TX_RESET, SS_CSR(R_SER_CMD));
+
+        __raw_writeq(M_SYNCSER_MSB_FIRST, SS_CSR(R_SER_MODE));
+        __raw_writeq(32, SS_CSR(R_SER_MINFRM_SZ));
+        __raw_writeq(32, SS_CSR(R_SER_MAXFRM_SZ));
+
+        __raw_writeq(1, SS_CSR(R_SER_TX_RD_THRSH));
+        __raw_writeq(4, SS_CSR(R_SER_TX_WR_THRSH));
+        __raw_writeq(8, SS_CSR(R_SER_RX_RD_THRSH));
+
+        /* This looks good from experimentation */
+        __raw_writeq((M_SYNCSER_TXSYNC_INT | V_SYNCSER_TXSYNC_DLY(0) | M_SYNCSER_TXCLK_EXT |
+               M_SYNCSER_RXSYNC_INT | V_SYNCSER_RXSYNC_DLY(1) | M_SYNCSER_RXCLK_EXT | M_SYNCSER_RXSYNC_EDGE),
+              SS_CSR(R_SER_LINE_MODE));
+
+        /* This looks good from experimentation */
+        __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE,
+              SS_TXTBL(0));
+        __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
+              SS_TXTBL(1));
+        __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
+              SS_TXTBL(2));
+        __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE |
+              M_SYNCSER_SEQ_STROBE | M_SYNCSER_SEQ_LAST, SS_TXTBL(3));
+
+        __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE,
+              SS_RXTBL(0));
+        __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
+              SS_RXTBL(1));
+        __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
+              SS_RXTBL(2));
+        __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE |
+              M_SYNCSER_SEQ_LAST, SS_RXTBL(3));
+
+        for (i=4; i<16; i++) {
+                /* Just in case... */
+                __raw_writeq(M_SYNCSER_SEQ_LAST, SS_TXTBL(i));
+                __raw_writeq(M_SYNCSER_SEQ_LAST, SS_RXTBL(i));
+        }
+
+        return 0;
+}
+
+static int init_serdma(serdma_t *dma)
+{
+        CS_DBGOUT(CS_INIT, 2,
+                  printk(KERN_ERR "cs4297a: desc - %d sbufsize - %d dbufsize - %d\n",
+                         DMA_DESCR, SAMPLE_BUF_SIZE, DMA_BUF_SIZE));
+
+        /* Descriptors */
+        dma->ringsz = DMA_DESCR;
+        dma->descrtab = kzalloc(dma->ringsz * sizeof(serdma_descr_t), GFP_KERNEL);
+        if (!dma->descrtab) {
+                printk(KERN_ERR "cs4297a: kzalloc descrtab failed\n");
+                return -1;
+        }
+        dma->descrtab_end = dma->descrtab + dma->ringsz;
+	/* XXX bloddy mess, use proper DMA API here ...  */
+	dma->descrtab_phys = CPHYSADDR((long)dma->descrtab);
+        dma->descr_add = dma->descr_rem = dma->descrtab;
+
+        /* Frame buffer area */
+        dma->dma_buf = kzalloc(DMA_BUF_SIZE, GFP_KERNEL);
+        if (!dma->dma_buf) {
+                printk(KERN_ERR "cs4297a: kzalloc dma_buf failed\n");
+                kfree(dma->descrtab);
+                return -1;
+        }
+        dma->dma_buf_phys = CPHYSADDR((long)dma->dma_buf);
+
+        /* Samples buffer area */
+        dma->sbufsz = SAMPLE_BUF_SIZE;
+        dma->sample_buf = kmalloc(dma->sbufsz, GFP_KERNEL);
+        if (!dma->sample_buf) {
+                printk(KERN_ERR "cs4297a: kmalloc sample_buf failed\n");
+                kfree(dma->descrtab);
+                kfree(dma->dma_buf);
+                return -1;
+        }
+        dma->sb_swptr = dma->sb_hwptr = dma->sample_buf;
+        dma->sb_end = (u16 *)((void *)dma->sample_buf + dma->sbufsz);
+        dma->fragsize = dma->sbufsz >> 1;
+
+        CS_DBGOUT(CS_INIT, 4, 
+                  printk(KERN_ERR "cs4297a: descrtab - %08x dma_buf - %x sample_buf - %x\n",
+                         (int)dma->descrtab, (int)dma->dma_buf, 
+                         (int)dma->sample_buf));
+
+        return 0;
+}
+
+static int dma_init(struct cs4297a_state *s)
+{
+        int i;
+
+        CS_DBGOUT(CS_INIT, 2, 
+                  printk(KERN_INFO "cs4297a: Setting up DMA\n"));
+
+        if (init_serdma(&s->dma_adc) ||
+            init_serdma(&s->dma_dac))
+                return -1;
+
+        if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))||
+            __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) {
+                panic("DMA state corrupted?!");
+        }
+
+        /* Initialize now - the descr/buffer pairings will never
+           change... */
+        for (i=0; i<DMA_DESCR; i++) {
+                s->dma_dac.descrtab[i].descr_a = M_DMA_SERRX_SOP | V_DMA_DSCRA_A_SIZE(1) | 
+                        (s->dma_dac.dma_buf_phys + i*FRAME_BYTES);
+                s->dma_dac.descrtab[i].descr_b = V_DMA_DSCRB_PKT_SIZE(FRAME_BYTES);
+                s->dma_adc.descrtab[i].descr_a = V_DMA_DSCRA_A_SIZE(1) |
+                        (s->dma_adc.dma_buf_phys + i*FRAME_BYTES);
+                s->dma_adc.descrtab[i].descr_b = 0;
+        }
+
+        __raw_writeq((M_DMA_EOP_INT_EN | V_DMA_INT_PKTCNT(DMA_INT_CNT) |
+               V_DMA_RINGSZ(DMA_DESCR) | M_DMA_TDX_EN),
+              SS_CSR(R_SER_DMA_CONFIG0_RX));
+        __raw_writeq(M_DMA_L2CA, SS_CSR(R_SER_DMA_CONFIG1_RX));
+        __raw_writeq(s->dma_adc.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_RX));
+
+        __raw_writeq(V_DMA_RINGSZ(DMA_DESCR), SS_CSR(R_SER_DMA_CONFIG0_TX));
+        __raw_writeq(M_DMA_L2CA | M_DMA_NO_DSCR_UPDT, SS_CSR(R_SER_DMA_CONFIG1_TX));
+        __raw_writeq(s->dma_dac.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_TX));
+
+        /* Prep the receive DMA descriptor ring */
+        __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
+
+        __raw_writeq(M_SYNCSER_DMA_RX_EN | M_SYNCSER_DMA_TX_EN, SS_CSR(R_SER_DMA_ENABLE));
+
+        __raw_writeq((M_SYNCSER_RX_SYNC_ERR | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_EOP_COUNT),
+              SS_CSR(R_SER_INT_MASK));
+
+        /* Enable the rx/tx; let the codec warm up to the sync and
+           start sending good frames before the receive FIFO is
+           enabled */
+        __raw_writeq(M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD));
+        udelay(1000);
+        __raw_writeq(M_SYNCSER_CMD_RX_EN | M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD));
+
+        /* XXXKW is this magic? (the "1" part) */
+        while ((__raw_readq(SS_CSR(R_SER_STATUS)) & 0xf1) != 1)
+                ;
+
+        CS_DBGOUT(CS_INIT, 4, 
+                  printk(KERN_INFO "cs4297a: status: %08x\n",
+                         (unsigned int)(__raw_readq(SS_CSR(R_SER_STATUS)) & 0xffffffff)));
+
+        return 0;
+}
+
+static int serdma_reg_access(struct cs4297a_state *s, u64 data)
+{
+        serdma_t *d = &s->dma_dac;
+        u64 *data_p;
+        unsigned swptr;
+        unsigned long flags;
+        serdma_descr_t *descr;
+
+        if (s->reg_request) {
+                printk(KERN_ERR "cs4297a: attempt to issue multiple reg_access\n");
+                return -1;
+        }
+
+        if (s->ena & FMODE_WRITE) {
+                /* Since a writer has the DSP open, we have to mux the
+                   request in */
+                s->reg_request = data;
+                interruptible_sleep_on(&s->dma_dac.reg_wait);
+                /* XXXKW how can I deal with the starvation case where
+                   the opener isn't writing? */
+        } else {
+                /* Be safe when changing ring pointers */
+		spin_lock_irqsave(&s->lock, flags);
+                if (d->hwptr != d->swptr) {
+                        printk(KERN_ERR "cs4297a: reg access found bookkeeping error (hw/sw = %d/%d\n",
+                               d->hwptr, d->swptr);
+                        spin_unlock_irqrestore(&s->lock, flags);
+                        return -1;
+                }
+                swptr = d->swptr;
+                d->hwptr = d->swptr = (d->swptr + 1) % d->ringsz;
+		spin_unlock_irqrestore(&s->lock, flags);
+
+                descr = &d->descrtab[swptr];
+                data_p = &d->dma_buf[swptr * 4];
+		*data_p = cpu_to_be64(data);
+                __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_TX));
+                CS_DBGOUT(CS_DESCR, 4,
+                          printk(KERN_INFO "cs4297a: add_tx  %p (%x -> %x)\n",
+                                 data_p, swptr, d->hwptr));
+        }
+
+        CS_DBGOUT(CS_FUNCTION, 6,
+                  printk(KERN_INFO "cs4297a: serdma_reg_access()-\n"));
+        
+        return 0;
+}
+
+//****************************************************************************
+// "cs4297a_read_ac97" -- Reads an AC97 register
+//****************************************************************************
+static int cs4297a_read_ac97(struct cs4297a_state *s, u32 offset,
+			    u32 * value)
+{
+        CS_DBGOUT(CS_AC97, 1,
+                  printk(KERN_INFO "cs4297a: read reg %2x\n", offset));
+        if (serdma_reg_access(s, (0xCLL << 60) | (1LL << 47) | ((u64)(offset & 0x7F) << 40)))
+                return -1;
+
+        interruptible_sleep_on(&s->dma_adc.reg_wait);
+        *value = s->read_value;
+        CS_DBGOUT(CS_AC97, 2,
+                  printk(KERN_INFO "cs4297a: rdr reg %x -> %x\n", s->read_reg, s->read_value));
+
+        return 0;
+}
+
+
+//****************************************************************************
+// "cs4297a_write_ac97()"-- writes an AC97 register
+//****************************************************************************
+static int cs4297a_write_ac97(struct cs4297a_state *s, u32 offset,
+			     u32 value)
+{
+        CS_DBGOUT(CS_AC97, 1,
+                  printk(KERN_INFO "cs4297a: write reg %2x -> %04x\n", offset, value));
+        return (serdma_reg_access(s, (0xELL << 60) | ((u64)(offset & 0x7F) << 40) | ((value & 0xffff) << 12)));
+}
+
+static void stop_dac(struct cs4297a_state *s)
+{
+	unsigned long flags;
+
+	CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4297a: stop_dac():\n"));
+	spin_lock_irqsave(&s->lock, flags);
+	s->ena &= ~FMODE_WRITE;
+#if 0
+        /* XXXKW what do I really want here?  My theory for now is
+           that I just flip the "ena" bit, and the interrupt handler
+           will stop processing the xmit channel */
+        __raw_writeq((s->ena & FMODE_READ) ? M_SYNCSER_DMA_RX_EN : 0,
+              SS_CSR(R_SER_DMA_ENABLE));
+#endif
+
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void start_dac(struct cs4297a_state *s)
+{
+	unsigned long flags;
+
+	CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4297a: start_dac()+\n"));
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped ||
+					(s->dma_dac.count > 0
+	    				&& s->dma_dac.ready))) {
+		s->ena |= FMODE_WRITE;
+                /* XXXKW what do I really want here?  My theory for
+                   now is that I just flip the "ena" bit, and the
+                   interrupt handler will start processing the xmit
+                   channel */
+
+		CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO
+			"cs4297a: start_dac(): start dma\n"));
+
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 3,
+		  printk(KERN_INFO "cs4297a: start_dac()-\n"));
+}
+
+
+static void stop_adc(struct cs4297a_state *s)
+{
+	unsigned long flags;
+
+	CS_DBGOUT(CS_FUNCTION, 3,
+		  printk(KERN_INFO "cs4297a: stop_adc()+\n"));
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ena &= ~FMODE_READ;
+
+	if (s->conversion == 1) {
+		s->conversion = 0;
+		s->prop_adc.fmt = s->prop_adc.fmt_original;
+	}
+        /* Nothing to do really, I need to keep the DMA going
+           XXXKW when do I get here, and is there more I should do? */
+	spin_unlock_irqrestore(&s->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 3,
+		  printk(KERN_INFO "cs4297a: stop_adc()-\n"));
+}
+
+
+static void start_adc(struct cs4297a_state *s)
+{
+	unsigned long flags;
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4297a: start_adc()+\n"));
+
+	if (!(s->ena & FMODE_READ) &&
+	    (s->dma_adc.mapped || s->dma_adc.count <=
+	     (signed) (s->dma_adc.sbufsz - 2 * s->dma_adc.fragsize))
+	    && s->dma_adc.ready) {
+		if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) {
+			// 
+			// now only use 16 bit capture, due to truncation issue
+			// in the chip, noticeable distortion occurs.
+			// allocate buffer and then convert from 16 bit to 
+			// 8 bit for the user buffer.
+			//
+			s->prop_adc.fmt_original = s->prop_adc.fmt;
+			if (s->prop_adc.fmt & AFMT_S8) {
+				s->prop_adc.fmt &= ~AFMT_S8;
+				s->prop_adc.fmt |= AFMT_S16_LE;
+			}
+			if (s->prop_adc.fmt & AFMT_U8) {
+				s->prop_adc.fmt &= ~AFMT_U8;
+				s->prop_adc.fmt |= AFMT_U16_LE;
+			}
+			//
+			// prog_dmabuf_adc performs a stop_adc() but that is
+			// ok since we really haven't started the DMA yet.
+			//
+			prog_codec(s, CS_TYPE_ADC);
+
+                        prog_dmabuf_adc(s);
+			s->conversion = 1;
+		}
+		spin_lock_irqsave(&s->lock, flags);
+		s->ena |= FMODE_READ;
+                /* Nothing to do really, I am probably already
+                   DMAing...  XXXKW when do I get here, and is there
+                   more I should do? */
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO
+			 "cs4297a: start_adc(): start adc\n"));
+	}
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4297a: start_adc()-\n"));
+
+}
+
+
+// call with spinlock held! 
+static void cs4297a_update_ptr(struct cs4297a_state *s, int intflag)
+{
+	int good_diff, diff, diff2;
+        u64 *data_p, data;
+        u32 *s_ptr;
+	unsigned hwptr;
+        u32 status;
+        serdma_t *d;
+        serdma_descr_t *descr;
+
+	// update ADC pointer 
+        status = intflag ? __raw_readq(SS_CSR(R_SER_STATUS)) : 0;
+
+	if ((s->ena & FMODE_READ) || (status & (M_SYNCSER_RX_EOP_COUNT))) {
+                d = &s->dma_adc;
+                hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
+                                     d->descrtab_phys) / sizeof(serdma_descr_t));
+
+                if (s->ena & FMODE_READ) {
+                        CS_DBGOUT(CS_FUNCTION, 2, 
+                                  printk(KERN_INFO "cs4297a: upd_rcv sw->hw->hw %x/%x/%x (int-%d)n",
+                                         d->swptr, d->hwptr, hwptr, intflag));
+                        /* Number of DMA buffers available for software: */
+                        diff2 = diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz;
+                        d->hwptr = hwptr;
+                        good_diff = 0;
+                        s_ptr = (u32 *)&(d->dma_buf[d->swptr*4]);
+                        descr = &d->descrtab[d->swptr];
+                        while (diff2--) {
+				u64 data = be64_to_cpu(*(u64 *)s_ptr);
+                                u64 descr_a;
+                                u16 left, right;
+                                descr_a = descr->descr_a;
+                                descr->descr_a &= ~M_DMA_SERRX_SOP;
+                                if ((descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)s_ptr)) {
+                                        printk(KERN_ERR "cs4297a: RX Bad address (read)\n");
+                                }
+                                if (((data & 0x9800000000000000) != 0x9800000000000000) ||
+                                    (!(descr_a & M_DMA_SERRX_SOP)) ||
+                                    (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) {
+                                        s->stats.rx_bad++;
+                                        printk(KERN_DEBUG "cs4297a: RX Bad attributes (read)\n");
+                                        continue;
+                                }
+                                s->stats.rx_good++;
+                                if ((data >> 61) == 7) {
+                                        s->read_value = (data >> 12) & 0xffff;
+                                        s->read_reg = (data >> 40) & 0x7f;
+                                        wake_up(&d->reg_wait);
+                                }
+                                if (d->count && (d->sb_hwptr == d->sb_swptr)) {
+                                        s->stats.rx_overflow++;
+                                        printk(KERN_DEBUG "cs4297a: RX overflow\n");
+                                        continue;
+                                }
+                                good_diff++;
+				left = ((be32_to_cpu(s_ptr[1]) & 0xff) << 8) |
+				       ((be32_to_cpu(s_ptr[2]) >> 24) & 0xff);
+				right = (be32_to_cpu(s_ptr[2]) >> 4) & 0xffff;
+				*d->sb_hwptr++ = cpu_to_be16(left);
+				*d->sb_hwptr++ = cpu_to_be16(right);
+                                if (d->sb_hwptr == d->sb_end)
+                                        d->sb_hwptr = d->sample_buf;
+                                descr++;
+                                if (descr == d->descrtab_end) {
+                                        descr = d->descrtab;
+                                        s_ptr = (u32 *)s->dma_adc.dma_buf;
+                                } else {
+                                        s_ptr += 8;
+                                }
+                        }
+                        d->total_bytes += good_diff * FRAME_SAMPLE_BYTES;
+                        d->count += good_diff * FRAME_SAMPLE_BYTES;
+                        if (d->count > d->sbufsz) {
+                                printk(KERN_ERR "cs4297a: bogus receive overflow!!\n");
+                        }
+                        d->swptr = (d->swptr + diff) % d->ringsz;
+                        __raw_writeq(diff, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
+                        if (d->mapped) {
+                                if (d->count >= (signed) d->fragsize)
+                                        wake_up(&d->wait);
+                        } else {
+                                if (d->count > 0) {
+                                        CS_DBGOUT(CS_WAVE_READ, 4,
+                                                  printk(KERN_INFO
+                                                         "cs4297a: update count -> %d\n", d->count));
+                                        wake_up(&d->wait);
+                                }
+                        }
+                } else {
+                        /* Receive is going even if no one is
+                           listening (for register accesses and to
+                           avoid FIFO overrun) */
+                        diff2 = diff = (hwptr + d->ringsz - d->hwptr) % d->ringsz;
+                        if (!diff) {
+                                printk(KERN_ERR "cs4297a: RX full or empty?\n");
+                        }
+                        
+                        descr = &d->descrtab[d->swptr];
+                        data_p = &d->dma_buf[d->swptr*4];
+
+                        /* Force this to happen at least once; I got
+                           here because of an interrupt, so there must
+                           be a buffer to process. */
+                        do {
+				data = be64_to_cpu(*data_p);
+                                if ((descr->descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)data_p)) {
+                                        printk(KERN_ERR "cs4297a: RX Bad address %d (%llx %lx)\n", d->swptr,
+                                               (long long)(descr->descr_a & M_DMA_DSCRA_A_ADDR),
+                                               (long)CPHYSADDR((long)data_p));
+                                }
+                                if (!(data & (1LL << 63)) ||
+                                    !(descr->descr_a & M_DMA_SERRX_SOP) ||
+                                    (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) {
+                                        s->stats.rx_bad++;
+                                        printk(KERN_DEBUG "cs4297a: RX Bad attributes\n");
+                                } else {
+                                        s->stats.rx_good++;
+                                        if ((data >> 61) == 7) {
+                                                s->read_value = (data >> 12) & 0xffff;
+                                                s->read_reg = (data >> 40) & 0x7f;
+                                                wake_up(&d->reg_wait);
+                                        }
+                                }
+                                descr->descr_a &= ~M_DMA_SERRX_SOP;
+                                descr++;
+                                d->swptr++;
+                                data_p += 4;
+                                if (descr == d->descrtab_end) {
+                                        descr = d->descrtab;
+                                        d->swptr = 0;
+                                        data_p = d->dma_buf;
+                                }
+                                __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
+                        } while (--diff);
+                        d->hwptr = hwptr;
+
+                        CS_DBGOUT(CS_DESCR, 6, 
+                                  printk(KERN_INFO "cs4297a: hw/sw %x/%x\n", d->hwptr, d->swptr));
+                }
+
+		CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
+			"cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
+				(unsigned)s, d->hwptr, 
+				d->total_bytes, d->count));
+	}
+
+        /* XXXKW worry about s->reg_request -- there is a starvation
+           case if s->ena has FMODE_WRITE on, but the client isn't
+           doing writes */
+
+	// update DAC pointer 
+	//
+	// check for end of buffer, means that we are going to wait for another interrupt
+	// to allow silence to fill the fifos on the part, to keep pops down to a minimum.
+	//
+	if (s->ena & FMODE_WRITE) {
+                serdma_t *d = &s->dma_dac;
+                hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
+                                     d->descrtab_phys) / sizeof(serdma_descr_t));
+                diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz;
+                CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO
+                                                   "cs4297a: cs4297a_update_ptr(): hw/hw/sw %x/%x/%x diff %d count %d\n",
+                                                   d->hwptr, hwptr, d->swptr, diff, d->count));
+                d->hwptr = hwptr;
+                /* XXXKW stereo? conversion? Just assume 2 16-bit samples for now */
+                d->total_bytes += diff * FRAME_SAMPLE_BYTES;
+		if (d->mapped) {
+			d->count += diff * FRAME_SAMPLE_BYTES;
+			if (d->count >= d->fragsize) {
+				d->wakeup = 1;
+				wake_up(&d->wait);
+				if (d->count > d->sbufsz)
+					d->count &= d->sbufsz - 1;
+			}
+		} else {
+			d->count -= diff * FRAME_SAMPLE_BYTES;
+			if (d->count <= 0) {
+				//
+				// fill with silence, and do not shut down the DAC.
+				// Continue to play silence until the _release.
+				//
+				CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO
+					"cs4297a: cs4297a_update_ptr(): memset %d at 0x%.8x for %d size \n",
+						(unsigned)(s->prop_dac.fmt & 
+						(AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, 
+						(unsigned)d->dma_buf, 
+						d->ringsz));
+				memset(d->dma_buf, 0, d->ringsz * FRAME_BYTES);
+				if (d->count < 0) {
+					d->underrun = 1;
+                                        s->stats.tx_underrun++;
+					d->count = 0;
+					CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO
+					 "cs4297a: cs4297a_update_ptr(): underrun\n"));
+				}
+			} else if (d->count <=
+				   (signed) d->fragsize
+				   && !d->endcleared) {
+                          /* XXXKW what is this for? */
+				clear_advance(d->dma_buf,
+					      d->sbufsz,
+					      d->swptr,
+					      d->fragsize,
+					      0);
+				d->endcleared = 1;
+			}
+			if ( (d->count <= (signed) d->sbufsz/2) || intflag)
+			{
+                                CS_DBGOUT(CS_WAVE_WRITE, 4,
+                                          printk(KERN_INFO
+                                                 "cs4297a: update count -> %d\n", d->count));
+				wake_up(&d->wait);
+			}
+		}
+		CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
+			"cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
+				(unsigned) s, d->hwptr, 
+				d->total_bytes, d->count));
+	}
+}
+
+static int mixer_ioctl(struct cs4297a_state *s, unsigned int cmd,
+		       unsigned long arg)
+{
+	// Index to mixer_src[] is value of AC97 Input Mux Select Reg.
+	// Value of array member is recording source Device ID Mask.
+	static const unsigned int mixer_src[8] = {
+		SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1,
+		SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0
+	};
+
+	// Index of mixtable1[] member is Device ID 
+	// and must be <= SOUND_MIXER_NRDEVICES.
+	// Value of array member is index into s->mix.vol[]
+	static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
+		[SOUND_MIXER_PCM] = 1,	// voice 
+		[SOUND_MIXER_LINE1] = 2,	// AUX
+		[SOUND_MIXER_CD] = 3,	// CD 
+		[SOUND_MIXER_LINE] = 4,	// Line 
+		[SOUND_MIXER_SYNTH] = 5,	// FM
+		[SOUND_MIXER_MIC] = 6,	// Mic 
+		[SOUND_MIXER_SPEAKER] = 7,	// Speaker 
+		[SOUND_MIXER_RECLEV] = 8,	// Recording level 
+		[SOUND_MIXER_VOLUME] = 9	// Master Volume 
+	};
+
+	static const unsigned mixreg[] = {
+		AC97_PCMOUT_VOL,
+		AC97_AUX_VOL,
+		AC97_CD_VOL,
+		AC97_LINEIN_VOL
+	};
+	unsigned char l, r, rl, rr, vidx;
+	unsigned char attentbl[11] =
+	    { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 };
+	unsigned temp1;
+	int i, val;
+
+	VALIDATE_STATE(s);
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
+		 "cs4297a: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n",
+			 (unsigned) s, cmd));
+#if CSDEBUG
+	cs_printioctl(cmd);
+#endif
+#if CSDEBUG_INTERFACE
+
+	if ((cmd == SOUND_MIXER_CS_GETDBGMASK) ||
+	    (cmd == SOUND_MIXER_CS_SETDBGMASK) ||
+	    (cmd == SOUND_MIXER_CS_GETDBGLEVEL) ||
+	    (cmd == SOUND_MIXER_CS_SETDBGLEVEL))
+	{
+		switch (cmd) {
+
+		case SOUND_MIXER_CS_GETDBGMASK:
+			return put_user(cs_debugmask,
+					(unsigned long *) arg);
+
+		case SOUND_MIXER_CS_GETDBGLEVEL:
+			return put_user(cs_debuglevel,
+					(unsigned long *) arg);
+
+		case SOUND_MIXER_CS_SETDBGMASK:
+			if (get_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			cs_debugmask = val;
+			return 0;
+
+		case SOUND_MIXER_CS_SETDBGLEVEL:
+			if (get_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			cs_debuglevel = val;
+			return 0;
+		default:
+			CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
+				"cs4297a: mixer_ioctl(): ERROR unknown debug cmd\n"));
+			return 0;
+		}
+	}
+#endif
+
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+                return -EINVAL;
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+		// enable/disable/query spatializer 
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		if (val != -1) {
+			temp1 = (val & 0x3f) >> 2;
+			cs4297a_write_ac97(s, AC97_3D_CONTROL, temp1);
+			cs4297a_read_ac97(s, AC97_GENERAL_PURPOSE,
+					 &temp1);
+			cs4297a_write_ac97(s, AC97_GENERAL_PURPOSE,
+					  temp1 | 0x2000);
+		}
+		cs4297a_read_ac97(s, AC97_3D_CONTROL, &temp1);
+		return put_user((temp1 << 2) | 3, (int *) arg);
+	}
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		memset(&info, 0, sizeof(info));
+		strlcpy(info.id, "CS4297a", sizeof(info.id));
+		strlcpy(info.name, "Crystal CS4297a", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *) 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, "CS4297a", sizeof(info.id));
+		strlcpy(info.name, "Crystal CS4297a", sizeof(info.name));
+		if (copy_to_user((void *) arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *) arg);
+
+	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+		return -EINVAL;
+
+	// If ioctl has only the SIOC_READ bit(bit 31)
+	// on, process the only-read commands. 
+	if (_SIOC_DIR(cmd) == _SIOC_READ) {
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC:	// Arg contains a bit for each recording source 
+			cs4297a_read_ac97(s, AC97_RECORD_SELECT,
+					 &temp1);
+			return put_user(mixer_src[temp1 & 7], (int *) arg);
+
+		case SOUND_MIXER_DEVMASK:	// Arg contains a bit for each supported device 
+			return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE |
+					SOUND_MASK_VOLUME | SOUND_MASK_RECLEV,
+                                        (int *) arg);
+
+		case SOUND_MIXER_RECMASK:	// Arg contains a bit for each supported recording source 
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_VOLUME,
+                                        (int *) arg);
+
+		case SOUND_MIXER_STEREODEVS:	// Mixer channels supporting stereo 
+			return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE |
+					SOUND_MASK_VOLUME | SOUND_MASK_RECLEV,
+                                        (int *) arg);
+
+		case SOUND_MIXER_CAPS:
+			return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
+
+		default:
+			i = _IOC_NR(cmd);
+			if (i >= SOUND_MIXER_NRDEVICES
+			    || !(vidx = mixtable1[i]))
+				return -EINVAL;
+			return put_user(s->mix.vol[vidx - 1], (int *) arg);
+		}
+	}
+	// If ioctl doesn't have both the SIOC_READ and 
+	// the SIOC_WRITE bit set, return invalid.
+	if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE))
+		return -EINVAL;
+
+	// Increment the count of volume writes.
+	s->mix.modcnt++;
+
+	// Isolate the command; it must be a write.
+	switch (_IOC_NR(cmd)) {
+
+	case SOUND_MIXER_RECSRC:	// Arg contains a bit for each recording source 
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		i = hweight32(val);	// i = # bits on in val.
+		if (i != 1)	// One & only 1 bit must be on.
+			return 0;
+		for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) {
+			if (val == mixer_src[i]) {
+				temp1 = (i << 8) | i;
+				cs4297a_write_ac97(s,
+						  AC97_RECORD_SELECT,
+						  temp1);
+				return 0;
+			}
+		}
+		return 0;
+
+	case SOUND_MIXER_VOLUME:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;	// Max soundcard.h vol is 100.
+		if (l < 6) {
+			rl = 63;
+			l = 0;
+		} else
+			rl = attentbl[(10 * l) / 100];	// Convert 0-100 vol to 63-0 atten.
+
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;	// Max right volume is 100, too
+		if (r < 6) {
+			rr = 63;
+			r = 0;
+		} else
+			rr = attentbl[(10 * r) / 100];	// Convert volume to attenuation.
+
+		if ((rl > 60) && (rr > 60))	// If both l & r are 'low',          
+			temp1 = 0x8000;	//  turn on the mute bit.
+		else
+			temp1 = 0;
+
+		temp1 |= (rl << 8) | rr;
+
+		cs4297a_write_ac97(s, AC97_MASTER_VOL_STEREO, temp1);
+		cs4297a_write_ac97(s, AC97_PHONE_VOL, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[8] = ((unsigned int) r << 8) | l;
+#else
+		s->mix.vol[8] = val;
+#endif
+		return put_user(s->mix.vol[8], (int *) arg);
+
+	case SOUND_MIXER_SPEAKER:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (l < 3) {
+			rl = 0;
+			l = 0;
+		} else {
+			rl = (l * 2 - 5) / 13;	// Convert 0-100 range to 0-15.
+			l = (rl * 13 + 5) / 2;
+		}
+
+		if (rl < 3) {
+			temp1 = 0x8000;
+			rl = 0;
+		} else
+			temp1 = 0;
+		rl = 15 - rl;	// Convert volume to attenuation.
+		temp1 |= rl << 1;
+		cs4297a_write_ac97(s, AC97_PCBEEP_VOL, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[6] = l << 8;
+#else
+		s->mix.vol[6] = val;
+#endif
+		return put_user(s->mix.vol[6], (int *) arg);
+
+	case SOUND_MIXER_RECLEV:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;
+		rl = (l * 2 - 5) / 13;	// Convert 0-100 scale to 0-15.
+		rr = (r * 2 - 5) / 13;
+		if (rl < 3 && rr < 3)
+			temp1 = 0x8000;
+		else
+			temp1 = 0;
+
+		temp1 = temp1 | (rl << 8) | rr;
+		cs4297a_write_ac97(s, AC97_RECORD_GAIN, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[7] = ((unsigned int) r << 8) | l;
+#else
+		s->mix.vol[7] = val;
+#endif
+		return put_user(s->mix.vol[7], (int *) arg);
+
+	case SOUND_MIXER_MIC:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (l < 1) {
+			l = 0;
+			rl = 0;
+		} else {
+			rl = ((unsigned) l * 5 - 4) / 16;	// Convert 0-100 range to 0-31.
+			l = (rl * 16 + 4) / 5;
+		}
+		cs4297a_read_ac97(s, AC97_MIC_VOL, &temp1);
+		temp1 &= 0x40;	// Isolate 20db gain bit.
+		if (rl < 3) {
+			temp1 |= 0x8000;
+			rl = 0;
+		}
+		rl = 31 - rl;	// Convert volume to attenuation.
+		temp1 |= rl;
+		cs4297a_write_ac97(s, AC97_MIC_VOL, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[5] = val << 8;
+#else
+		s->mix.vol[5] = val;
+#endif
+		return put_user(s->mix.vol[5], (int *) arg);
+
+
+	case SOUND_MIXER_SYNTH:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;
+		rl = (l * 2 - 11) / 3;	// Convert 0-100 range to 0-63.
+		rr = (r * 2 - 11) / 3;
+		if (rl < 3)	// If l is low, turn on
+			temp1 = 0x0080;	//  the mute bit.
+		else
+			temp1 = 0;
+
+		rl = 63 - rl;	// Convert vol to attenuation.
+//		writel(temp1 | rl, s->pBA0 + FMLVC);
+		if (rr < 3)	//  If rr is low, turn on
+			temp1 = 0x0080;	//   the mute bit.
+		else
+			temp1 = 0;
+		rr = 63 - rr;	// Convert vol to attenuation.
+//		writel(temp1 | rr, s->pBA0 + FMRVC);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[4] = (r << 8) | l;
+#else
+		s->mix.vol[4] = val;
+#endif
+		return put_user(s->mix.vol[4], (int *) arg);
+
+
+	default:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			"cs4297a: mixer_ioctl(): default\n"));
+
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
+			return -EINVAL;
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (l < 1) {
+			l = 0;
+			rl = 31;
+		} else
+			rl = (attentbl[(l * 10) / 100]) >> 1;
+
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;
+		if (r < 1) {
+			r = 0;
+			rr = 31;
+		} else
+			rr = (attentbl[(r * 10) / 100]) >> 1;
+		if ((rl > 30) && (rr > 30))
+			temp1 = 0x8000;
+		else
+			temp1 = 0;
+		temp1 = temp1 | (rl << 8) | rr;
+		cs4297a_write_ac97(s, mixreg[vidx - 1], temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l;
+#else
+		s->mix.vol[vidx - 1] = val;
+#endif
+		return put_user(s->mix.vol[vidx - 1], (int *) arg);
+	}
+}
+
+
+// --------------------------------------------------------------------- 
+
+static int cs4297a_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	struct cs4297a_state *s=NULL;
+	struct list_head *entry;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n"));
+
+	mutex_lock(&swarm_cs4297a_mutex);
+	list_for_each(entry, &cs4297a_devs)
+	{
+		s = list_entry(entry, struct cs4297a_state, list);
+		if(s->dev_mixer == minor)
+			break;
+	}
+	if (!s)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
+			printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n"));
+
+		mutex_unlock(&swarm_cs4297a_mutex);
+		return -ENODEV;
+	}
+	VALIDATE_STATE(s);
+	file->private_data = s;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n"));
+	mutex_unlock(&swarm_cs4297a_mutex);
+
+	return nonseekable_open(inode, file);
+}
+
+
+static int cs4297a_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct cs4297a_state *s =
+	    (struct cs4297a_state *) file->private_data;
+
+	VALIDATE_STATE(s);
+	return 0;
+}
+
+
+static int cs4297a_ioctl_mixdev(struct file *file,
+			       unsigned int cmd, unsigned long arg)
+{
+	int ret;
+	mutex_lock(&swarm_cs4297a_mutex);
+	ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd,
+			   arg);
+	mutex_unlock(&swarm_cs4297a_mutex);
+	return ret;
+}
+
+
+// ******************************************************************************************
+//   Mixer file operations struct.
+// ******************************************************************************************
+static const struct file_operations cs4297a_mixer_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.unlocked_ioctl	= cs4297a_ioctl_mixdev,
+	.open		= cs4297a_open_mixdev,
+	.release	= cs4297a_release_mixdev,
+};
+
+// --------------------------------------------------------------------- 
+
+
+static int drain_adc(struct cs4297a_state *s, int nonblock)
+{
+        /* This routine serves no purpose currently - any samples
+           sitting in the receive queue will just be processed by the
+           background consumer.  This would be different if DMA
+           actually stopped when there were no clients. */
+	return 0;
+}
+
+static int drain_dac(struct cs4297a_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+        unsigned hwptr;
+	unsigned tmo;
+	int count;
+
+	if (s->dma_dac.mapped)
+		return 0;
+        if (nonblock)
+                return -EBUSY;
+	add_wait_queue(&s->dma_dac.wait, &wait);
+        while ((count = __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) ||
+               (s->dma_dac.count > 0)) {
+                if (!signal_pending(current)) {
+                        set_current_state(TASK_INTERRUPTIBLE);
+                        /* XXXKW is this calculation working? */
+                        tmo = ((count * FRAME_TX_US) * HZ) / 1000000;
+                        schedule_timeout(tmo + 1);
+                } else {
+                        /* XXXKW do I care if there is a signal pending? */
+                }
+        }
+        spin_lock_irqsave(&s->lock, flags);
+        /* Reset the bookkeeping */
+        hwptr = (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
+                       s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t));
+        s->dma_dac.hwptr = s->dma_dac.swptr = hwptr;
+        spin_unlock_irqrestore(&s->lock, flags);
+	remove_wait_queue(&s->dma_dac.wait, &wait);
+	current->state = TASK_RUNNING;
+	return 0;
+}
+
+
+// --------------------------------------------------------------------- 
+
+static ssize_t cs4297a_read(struct file *file, char *buffer, size_t count,
+			   loff_t * ppos)
+{
+	struct cs4297a_state *s =
+	    (struct cs4297a_state *) file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	int cnt, count_fr, cnt_by;
+	unsigned copied = 0;
+
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
+		  printk(KERN_INFO "cs4297a: cs4297a_read()+ %d \n", count));
+
+	VALIDATE_STATE(s);
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+//
+// "count" is the amount of bytes to read (from app), is decremented each loop
+//      by the amount of bytes that have been returned to the user buffer.
+// "cnt" is the running total of each read from the buffer (changes each loop)
+// "buffer" points to the app's buffer
+// "ret" keeps a running total of the amount of bytes that have been copied
+//      to the user buffer.
+// "copied" is the total bytes copied into the user buffer for each loop.
+//
+	while (count > 0) {
+		CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
+			"_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n",
+				count, s->dma_adc.count,
+				s->dma_adc.swptr, s->dma_adc.hwptr));
+		spin_lock_irqsave(&s->lock, flags);
+
+                /* cnt will be the number of available samples (16-bit
+                   stereo); it starts out as the maxmimum consequetive
+                   samples */
+		cnt = (s->dma_adc.sb_end - s->dma_adc.sb_swptr) / 2;
+                count_fr = s->dma_adc.count / FRAME_SAMPLE_BYTES;
+
+		// dma_adc.count is the current total bytes that have not been read.
+		// if the amount of unread bytes from the current sw pointer to the
+		// end of the buffer is greater than the current total bytes that
+		// have not been read, then set the "cnt" (unread bytes) to the
+		// amount of unread bytes.  
+
+		if (count_fr < cnt)
+			cnt = count_fr;
+                cnt_by = cnt * FRAME_SAMPLE_BYTES;
+		spin_unlock_irqrestore(&s->lock, flags);
+		//
+		// if we are converting from 8/16 then we need to copy
+		// twice the number of 16 bit bytes then 8 bit bytes.
+		// 
+		if (s->conversion) {
+			if (cnt_by > (count * 2)) {
+				cnt = (count * 2) / FRAME_SAMPLE_BYTES;
+                                cnt_by = count * 2;
+                        }
+		} else {
+			if (cnt_by > count) {
+				cnt = count / FRAME_SAMPLE_BYTES;
+                                cnt_by = count;
+                        }
+		}
+		//
+		// "cnt" NOW is the smaller of the amount that will be read,
+		// and the amount that is requested in this read (or partial).
+		// if there are no bytes in the buffer to read, then start the
+		// ADC and wait for the interrupt handler to wake us up.
+		//
+		if (cnt <= 0) {
+
+			// start up the dma engine and then continue back to the top of
+			// the loop when wake up occurs.
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->dma_adc.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		// there are bytes in the buffer to read.
+		// copy from the hw buffer over to the user buffer.
+		// user buffer is designated by "buffer"
+		// virtual address to copy from is dma_buf+swptr
+		// the "cnt" is the number of bytes to read.
+
+		CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO
+			"_read() copy_to cnt=%d count=%d ", cnt_by, count));
+		CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
+			 " .sbufsz=%d .count=%d buffer=0x%.8x ret=%d\n",
+				 s->dma_adc.sbufsz, s->dma_adc.count,
+				 (unsigned) buffer, ret));
+
+		if (copy_to_user (buffer, ((void *)s->dma_adc.sb_swptr), cnt_by))
+			return ret ? ret : -EFAULT;
+                copied = cnt_by;
+
+                /* Return the descriptors */
+		spin_lock_irqsave(&s->lock, flags);
+                CS_DBGOUT(CS_FUNCTION, 2, 
+                          printk(KERN_INFO "cs4297a: upd_rcv sw->hw %x/%x\n", s->dma_adc.swptr, s->dma_adc.hwptr));
+		s->dma_adc.count -= cnt_by;
+                s->dma_adc.sb_swptr += cnt * 2;
+                if (s->dma_adc.sb_swptr == s->dma_adc.sb_end)
+                        s->dma_adc.sb_swptr = s->dma_adc.sample_buf;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= copied;
+		buffer += copied;
+		ret += copied;
+		start_adc(s);
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
+		  printk(KERN_INFO "cs4297a: cs4297a_read()- %d\n", ret));
+	return ret;
+}
+
+
+static ssize_t cs4297a_write(struct file *file, const char *buffer,
+			    size_t count, loff_t * ppos)
+{
+	struct cs4297a_state *s =
+	    (struct cs4297a_state *) file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr, hwptr;
+	int cnt;
+
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
+		  printk(KERN_INFO "cs4297a: cs4297a_write()+ count=%d\n",
+			 count));
+	VALIDATE_STATE(s);
+
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+                serdma_t *d = &s->dma_dac;
+                int copy_cnt;
+                u32 *s_tmpl;
+                u32 *t_tmpl;
+                u32 left, right;
+                int swap = (s->prop_dac.fmt == AFMT_S16_LE) || (s->prop_dac.fmt == AFMT_U16_LE);
+                
+                /* XXXXXX this is broken for BLOAT_FACTOR */
+		spin_lock_irqsave(&s->lock, flags);
+		if (d->count < 0) {
+			d->count = 0;
+			d->swptr = d->hwptr;
+		}
+		if (d->underrun) {
+			d->underrun = 0;
+                        hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
+                                             d->descrtab_phys) / sizeof(serdma_descr_t));
+			d->swptr = d->hwptr = hwptr;
+		}
+		swptr = d->swptr;
+		cnt = d->sbufsz - (swptr * FRAME_SAMPLE_BYTES);
+                /* Will this write fill up the buffer? */
+		if (d->count + cnt > d->sbufsz)
+			cnt = d->sbufsz - d->count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&d->wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(d->sample_buf, buffer, cnt))
+			return ret ? ret : -EFAULT;
+
+                copy_cnt = cnt;
+                s_tmpl = (u32 *)d->sample_buf;
+                t_tmpl = (u32 *)(d->dma_buf + (swptr * 4));
+
+                /* XXXKW assuming 16-bit stereo! */
+                do {
+			u32 tmp;
+
+			t_tmpl[0] = cpu_to_be32(0x98000000);
+
+			tmp = be32_to_cpu(s_tmpl[0]);
+			left = tmp & 0xffff;
+			right = tmp >> 16;
+			if (swap) {
+				left = swab16(left);
+				right = swab16(right);
+			}
+			t_tmpl[1] = cpu_to_be32(left >> 8);
+			t_tmpl[2] = cpu_to_be32(((left & 0xff) << 24) |
+						(right << 4));
+
+                        s_tmpl++;
+                        t_tmpl += 8;
+                        copy_cnt -= 4;
+                } while (copy_cnt);
+
+                /* Mux in any pending read/write accesses */
+                if (s->reg_request) {
+			*(u64 *)(d->dma_buf + (swptr * 4)) |=
+				cpu_to_be64(s->reg_request);
+                        s->reg_request = 0;
+                        wake_up(&s->dma_dac.reg_wait);
+                }
+
+                CS_DBGOUT(CS_WAVE_WRITE, 4,
+                          printk(KERN_INFO
+                                 "cs4297a: copy in %d to swptr %x\n", cnt, swptr));
+
+		swptr = (swptr + (cnt/FRAME_SAMPLE_BYTES)) % d->ringsz;
+                __raw_writeq(cnt/FRAME_SAMPLE_BYTES, SS_CSR(R_SER_DMA_DSCR_COUNT_TX));
+		spin_lock_irqsave(&s->lock, flags);
+		d->swptr = swptr;
+		d->count += cnt;
+		d->endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac(s);
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
+		  printk(KERN_INFO "cs4297a: cs4297a_write()- %d\n", ret));
+	return ret;
+}
+
+
+static unsigned int cs4297a_poll(struct file *file,
+				struct poll_table_struct *wait)
+{
+	struct cs4297a_state *s =
+	    (struct cs4297a_state *) file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+		  printk(KERN_INFO "cs4297a: cs4297a_poll()+\n"));
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE) {
+		CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+			  printk(KERN_INFO
+				 "cs4297a: cs4297a_poll() wait on FMODE_WRITE\n"));
+		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
+			return 0;
+		poll_wait(file, &s->dma_dac.wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+			  printk(KERN_INFO
+				 "cs4297a: cs4297a_poll() wait on FMODE_READ\n"));
+		if(!s->dma_dac.ready && prog_dmabuf_adc(s))
+			return 0;
+		poll_wait(file, &s->dma_adc.wait, wait);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	cs4297a_update_ptr(s,CS_FALSE);
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >=
+			    (signed) s->dma_dac.fragsize) {
+				if (s->dma_dac.wakeup)
+					mask |= POLLOUT | POLLWRNORM;
+				else
+					mask = 0;
+				s->dma_dac.wakeup = 0;
+			}
+		} else {
+			if ((signed) (s->dma_dac.sbufsz/2) >= s->dma_dac.count)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	} else if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) 
+				mask |= POLLIN | POLLRDNORM;
+		} else {
+			if (s->dma_adc.count > 0)
+				mask |= POLLIN | POLLRDNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+		  printk(KERN_INFO "cs4297a: cs4297a_poll()- 0x%.8x\n",
+			 mask));
+	return mask;
+}
+
+
+static int cs4297a_mmap(struct file *file, struct vm_area_struct *vma)
+{
+        /* XXXKW currently no mmap support */
+        return -EINVAL;
+	return 0;
+}
+
+
+static int cs4297a_ioctl(struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	struct cs4297a_state *s =
+	    (struct cs4297a_state *) file->private_data;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	int val, mapped, ret;
+
+	CS_DBGOUT(CS_FUNCTION|CS_IOCTL, 4, printk(KERN_INFO
+		 "cs4297a: cs4297a_ioctl(): file=0x%.8x cmd=0x%.8x\n",
+			 (unsigned) file, cmd));
+#if CSDEBUG
+	cs_printioctl(cmd);
+#endif
+	VALIDATE_STATE(s);
+	mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+	    ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			"cs4297a: cs4297a_ioctl(): SOUND_VERSION=0x%.8x\n",
+				 SOUND_VERSION));
+		return put_user(SOUND_VERSION, (int *) arg);
+
+	case SNDCTL_DSP_SYNC:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			 "cs4297a: cs4297a_ioctl(): DSP_SYNC\n"));
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s,
+					 0 /*file->f_flags & O_NONBLOCK */
+					 );
+		return 0;
+
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
+				DSP_CAP_TRIGGER | DSP_CAP_MMAP,
+				(int *) arg);
+
+	case SNDCTL_DSP_RESET:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			 "cs4297a: cs4297a_ioctl(): DSP_RESET\n"));
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq(s->irq);
+                        s->dma_dac.count = s->dma_dac.total_bytes =
+                                s->dma_dac.blocks = s->dma_dac.wakeup = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr =
+                                (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
+                                       s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t));
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq(s->irq);
+                        s->dma_adc.count = s->dma_adc.total_bytes =
+                                s->dma_adc.blocks = s->dma_dac.wakeup = 0;
+			s->dma_adc.swptr = s->dma_adc.hwptr =
+                                (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
+                                       s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t));
+		}
+		return 0;
+
+	case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4297a: cs4297a_ioctl(): DSP_SPEED val=%d -> 48000\n", val));
+                val = 48000;
+                return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4297a: cs4297a_ioctl(): DSP_STEREO val=%d\n", val));
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			s->prop_adc.channels = val ? 2 : 1;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			s->prop_dac.channels = val ? 2 : 1;
+		}
+		return 0;
+
+	case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4297a: cs4297a_ioctl(): DSP_CHANNELS val=%d\n",
+				 val));
+		if (val != 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val >= 2)
+					s->prop_adc.channels = 2;
+				else
+					s->prop_adc.channels = 1;
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					s->prop_dac.channels = 2;
+				else
+					s->prop_dac.channels = 1;
+			}
+		}
+
+		if (file->f_mode & FMODE_WRITE)
+			val = s->prop_dac.channels;
+		else if (file->f_mode & FMODE_READ)
+			val = s->prop_adc.channels;
+
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_GETFMTS:	// Returns a mask 
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			"cs4297a: cs4297a_ioctl(): DSP_GETFMT val=0x%.8x\n",
+				 AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
+				 AFMT_U8));
+		return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
+				AFMT_U8, (int *) arg);
+
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4297a: cs4297a_ioctl(): DSP_SETFMT val=0x%.8x\n",
+				 val));
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val != AFMT_S16_LE
+				    && val != AFMT_U16_LE && val != AFMT_S8
+				    && val != AFMT_U8)
+					val = AFMT_U8;
+				s->prop_adc.fmt = val;
+				s->prop_adc.fmt_original = s->prop_adc.fmt;
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val != AFMT_S16_LE
+				    && val != AFMT_U16_LE && val != AFMT_S8
+				    && val != AFMT_U8)
+					val = AFMT_U8;
+				s->prop_dac.fmt = val;
+				s->prop_dac.fmt_original = s->prop_dac.fmt;
+			}
+		} else {
+			if (file->f_mode & FMODE_WRITE)
+				val = s->prop_dac.fmt_original;
+			else if (file->f_mode & FMODE_READ)
+				val = s->prop_adc.fmt_original;
+		}
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+		  "cs4297a: cs4297a_ioctl(): DSP_SETFMT return val=0x%.8x\n", 
+			val));
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_POST:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			 "cs4297a: cs4297a_ioctl(): DSP_POST\n"));
+		return 0;
+
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & s->ena & FMODE_READ)
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & s->ena & FMODE_WRITE)
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready
+				    && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready
+				    && (ret = prog_dmabuf_dac(s)))
+					return ret;
+				start_dac(s);
+			} else
+				stop_dac(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)))
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4297a_update_ptr(s,CS_FALSE);
+		abinfo.fragsize = s->dma_dac.fragsize;
+		if (s->dma_dac.mapped)
+			abinfo.bytes = s->dma_dac.sbufsz;
+		else
+			abinfo.bytes =
+			    s->dma_dac.sbufsz - s->dma_dac.count;
+		abinfo.fragstotal = s->dma_dac.numfrag;
+		abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
+		CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO
+			"cs4297a: cs4297a_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n",
+				abinfo.fragsize,abinfo.bytes,abinfo.fragstotal,
+				abinfo.fragments));
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &abinfo,
+				    sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)))
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4297a_update_ptr(s,CS_FALSE);
+		if (s->conversion) {
+			abinfo.fragsize = s->dma_adc.fragsize / 2;
+			abinfo.bytes = s->dma_adc.count / 2;
+			abinfo.fragstotal = s->dma_adc.numfrag;
+			abinfo.fragments =
+			    abinfo.bytes >> (s->dma_adc.fragshift - 1);
+		} else {
+			abinfo.fragsize = s->dma_adc.fragsize;
+			abinfo.bytes = s->dma_adc.count;
+			abinfo.fragstotal = s->dma_adc.numfrag;
+			abinfo.fragments =
+			    abinfo.bytes >> s->dma_adc.fragshift;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &abinfo,
+				    sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_NONBLOCK:
+		spin_lock(&file->f_lock);
+		file->f_flags |= O_NONBLOCK;
+		spin_unlock(&file->f_lock);
+		return 0;
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4297a_update_ptr(s,CS_FALSE);
+		val = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if(!s->dma_adc.ready && prog_dmabuf_adc(s))
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4297a_update_ptr(s,CS_FALSE);
+		cinfo.bytes = s->dma_adc.total_bytes;
+		if (s->dma_adc.mapped) {
+			cinfo.blocks =
+			    (cinfo.bytes >> s->dma_adc.fragshift) -
+			    s->dma_adc.blocks;
+			s->dma_adc.blocks =
+			    cinfo.bytes >> s->dma_adc.fragshift;
+		} else {
+			if (s->conversion) {
+				cinfo.blocks =
+				    s->dma_adc.count /
+				    2 >> (s->dma_adc.fragshift - 1);
+			} else
+				cinfo.blocks =
+				    s->dma_adc.count >> s->dma_adc.
+				    fragshift;
+		}
+		if (s->conversion)
+			cinfo.ptr = s->dma_adc.hwptr / 2;
+		else
+			cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize - 1;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4297a_update_ptr(s,CS_FALSE);
+		cinfo.bytes = s->dma_dac.total_bytes;
+		if (s->dma_dac.mapped) {
+			cinfo.blocks =
+			    (cinfo.bytes >> s->dma_dac.fragshift) -
+			    s->dma_dac.blocks;
+			s->dma_dac.blocks =
+			    cinfo.bytes >> s->dma_dac.fragshift;
+		} else {
+			cinfo.blocks =
+			    s->dma_dac.count >> s->dma_dac.fragshift;
+		}
+		cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize - 1;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_dac(s)))
+				return val;
+			return put_user(s->dma_dac.fragsize, (int *) arg);
+		}
+		if ((val = prog_dmabuf_adc(s)))
+			return val;
+		if (s->conversion)
+			return put_user(s->dma_adc.fragsize / 2,
+					(int *) arg);
+		else
+			return put_user(s->dma_adc.fragsize, (int *) arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		return 0;	// Say OK, but do nothing.
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision)
+		    || (file->f_mode & FMODE_WRITE
+			&& s->dma_dac.subdivision)) return -EINVAL;
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		else if (file->f_mode & FMODE_WRITE)
+			s->dma_dac.subdivision = val;
+		return 0;
+
+	case SOUND_PCM_READ_RATE:
+		if (file->f_mode & FMODE_READ)
+			return put_user(s->prop_adc.rate, (int *) arg);
+		else if (file->f_mode & FMODE_WRITE)
+			return put_user(s->prop_dac.rate, (int *) arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		if (file->f_mode & FMODE_READ)
+			return put_user(s->prop_adc.channels, (int *) arg);
+		else if (file->f_mode & FMODE_WRITE)
+			return put_user(s->prop_dac.channels, (int *) arg);
+
+	case SOUND_PCM_READ_BITS:
+		if (file->f_mode & FMODE_READ)
+			return
+			    put_user(
+				     (s->prop_adc.
+				      fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
+				     (int *) arg);
+		else if (file->f_mode & FMODE_WRITE)
+			return
+			    put_user(
+				     (s->prop_dac.
+				      fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
+				     (int *) arg);
+
+	case SOUND_PCM_WRITE_FILTER:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_READ_FILTER:
+		return -EINVAL;
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	mutex_lock(&swarm_cs4297a_mutex);
+	ret = cs4297a_ioctl(file, cmd, arg);
+	mutex_unlock(&swarm_cs4297a_mutex);
+
+	return ret;
+}
+
+static int cs4297a_release(struct inode *inode, struct file *file)
+{
+	struct cs4297a_state *s =
+	    (struct cs4297a_state *) file->private_data;
+
+        CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO
+		 "cs4297a: cs4297a_release(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n",
+			 (unsigned) inode, (unsigned) file, file->f_mode));
+	VALIDATE_STATE(s);
+
+	if (file->f_mode & FMODE_WRITE) {
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+		mutex_lock(&s->open_sem_dac);
+		stop_dac(s);
+		dealloc_dmabuf(s, &s->dma_dac);
+		s->open_mode &= ~FMODE_WRITE;
+		mutex_unlock(&s->open_sem_dac);
+		wake_up(&s->open_wait_dac);
+	}
+	if (file->f_mode & FMODE_READ) {
+		drain_adc(s, file->f_flags & O_NONBLOCK);
+		mutex_lock(&s->open_sem_adc);
+		stop_adc(s);
+		dealloc_dmabuf(s, &s->dma_adc);
+		s->open_mode &= ~FMODE_READ;
+		mutex_unlock(&s->open_sem_adc);
+		wake_up(&s->open_wait_adc);
+	}
+	return 0;
+}
+
+static int cs4297a_locked_open(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	struct cs4297a_state *s=NULL;
+	struct list_head *entry;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+		"cs4297a: cs4297a_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n",
+			(unsigned) inode, (unsigned) file, file->f_mode));
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+                "cs4297a: status = %08x\n", (int)__raw_readq(SS_CSR(R_SER_STATUS_DEBUG))));
+
+	list_for_each(entry, &cs4297a_devs)
+	{
+		s = list_entry(entry, struct cs4297a_state, list);
+
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+	if (entry == &cs4297a_devs)
+		return -ENODEV;
+	if (!s) {
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+			"cs4297a: cs4297a_open(): Error - unable to find audio state struct\n"));
+		return -ENODEV;
+	}
+	VALIDATE_STATE(s);
+	file->private_data = s;
+
+	// wait for device to become free 
+	if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) {
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO
+			 "cs4297a: cs4297a_open(): Error - must open READ and/or WRITE\n"));
+		return -ENODEV;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+                if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)) != 0) {
+                        printk(KERN_ERR "cs4297a: TX pipe needs to drain\n");
+                        while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)))
+                                ;
+                }
+          
+		mutex_lock(&s->open_sem_dac);
+		while (s->open_mode & FMODE_WRITE) {
+			if (file->f_flags & O_NONBLOCK) {
+				mutex_unlock(&s->open_sem_dac);
+				return -EBUSY;
+			}
+			mutex_unlock(&s->open_sem_dac);
+			interruptible_sleep_on(&s->open_wait_dac);
+
+			if (signal_pending(current)) {
+                                printk("open - sig pending\n");
+				return -ERESTARTSYS;
+                        }
+			mutex_lock(&s->open_sem_dac);
+		}
+	}
+	if (file->f_mode & FMODE_READ) {
+		mutex_lock(&s->open_sem_adc);
+		while (s->open_mode & FMODE_READ) {
+			if (file->f_flags & O_NONBLOCK) {
+				mutex_unlock(&s->open_sem_adc);
+				return -EBUSY;
+			}
+			mutex_unlock(&s->open_sem_adc);
+			interruptible_sleep_on(&s->open_wait_adc);
+
+			if (signal_pending(current)) {
+                                printk("open - sig pending\n");
+				return -ERESTARTSYS;
+                        }
+			mutex_lock(&s->open_sem_adc);
+		}
+	}
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	if (file->f_mode & FMODE_READ) {
+		s->prop_adc.fmt = AFMT_S16_BE;
+		s->prop_adc.fmt_original = s->prop_adc.fmt;
+		s->prop_adc.channels = 2;
+		s->prop_adc.rate = 48000;
+		s->conversion = 0;
+		s->ena &= ~FMODE_READ;
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
+		    s->dma_adc.subdivision = 0;
+		mutex_unlock(&s->open_sem_adc);
+
+		if (prog_dmabuf_adc(s)) {
+			CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
+				"cs4297a: adc Program dmabufs failed.\n"));
+			cs4297a_release(inode, file);
+			return -ENOMEM;
+		}
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->prop_dac.fmt = AFMT_S16_BE;
+		s->prop_dac.fmt_original = s->prop_dac.fmt;
+		s->prop_dac.channels = 2;
+		s->prop_dac.rate = 48000;
+		s->conversion = 0;
+		s->ena &= ~FMODE_WRITE;
+		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
+		    s->dma_dac.subdivision = 0;
+		mutex_unlock(&s->open_sem_dac);
+
+		if (prog_dmabuf_dac(s)) {
+			CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
+				"cs4297a: dac Program dmabufs failed.\n"));
+			cs4297a_release(inode, file);
+			return -ENOMEM;
+		}
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2,
+		  printk(KERN_INFO "cs4297a: cs4297a_open()- 0\n"));
+	return nonseekable_open(inode, file);
+}
+
+static int cs4297a_open(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	mutex_lock(&swarm_cs4297a_mutex);
+	ret = cs4297a_open(inode, file);
+	mutex_unlock(&swarm_cs4297a_mutex);
+
+	return ret;
+}
+
+// ******************************************************************************************
+//   Wave (audio) file operations struct.
+// ******************************************************************************************
+static const struct file_operations cs4297a_audio_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= cs4297a_read,
+	.write		= cs4297a_write,
+	.poll		= cs4297a_poll,
+	.unlocked_ioctl	= cs4297a_unlocked_ioctl,
+	.mmap		= cs4297a_mmap,
+	.open		= cs4297a_open,
+	.release	= cs4297a_release,
+};
+
+static void cs4297a_interrupt(int irq, void *dev_id)
+{
+	struct cs4297a_state *s = (struct cs4297a_state *) dev_id;
+        u32 status;
+
+        status = __raw_readq(SS_CSR(R_SER_STATUS_DEBUG));
+
+        CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO
+                 "cs4297a: cs4297a_interrupt() HISR=0x%.8x\n", status));
+
+#if 0
+        /* XXXKW what check *should* be done here? */
+        if (!(status & (M_SYNCSER_RX_EOP_COUNT | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_SYNC_ERR))) {
+                status = __raw_readq(SS_CSR(R_SER_STATUS));
+                printk(KERN_ERR "cs4297a: unexpected interrupt (status %08x)\n", status);
+                return;
+        }
+#endif
+
+        if (status & M_SYNCSER_RX_SYNC_ERR) {
+                status = __raw_readq(SS_CSR(R_SER_STATUS));
+                printk(KERN_ERR "cs4297a: rx sync error (status %08x)\n", status);
+                return;
+        }
+
+        if (status & M_SYNCSER_RX_OVERRUN) {
+                int newptr, i;
+                s->stats.rx_ovrrn++;
+                printk(KERN_ERR "cs4297a: receive FIFO overrun\n");
+
+                /* Fix things up: get the receive descriptor pool
+                   clean and give them back to the hardware */
+                while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX)))
+                        ;
+                newptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
+                                     s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t));
+                for (i=0; i<DMA_DESCR; i++) {
+                        s->dma_adc.descrtab[i].descr_a &= ~M_DMA_SERRX_SOP;
+                }
+                s->dma_adc.swptr = s->dma_adc.hwptr = newptr;
+                s->dma_adc.count = 0;
+                s->dma_adc.sb_swptr = s->dma_adc.sb_hwptr = s->dma_adc.sample_buf;
+                __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
+        }
+
+	spin_lock(&s->lock);
+	cs4297a_update_ptr(s,CS_TRUE);
+	spin_unlock(&s->lock);
+
+	CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO
+		  "cs4297a: cs4297a_interrupt()-\n"));
+}
+
+#if 0
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+
+  	{SOUND_MIXER_WRITE_VOLUME, 0x4040},
+        {SOUND_MIXER_WRITE_PCM, 0x4040},
+        {SOUND_MIXER_WRITE_SYNTH, 0x4040},
+	{SOUND_MIXER_WRITE_CD, 0x4040},
+	{SOUND_MIXER_WRITE_LINE, 0x4040},
+	{SOUND_MIXER_WRITE_LINE1, 0x4040},
+	{SOUND_MIXER_WRITE_RECLEV, 0x0000},
+	{SOUND_MIXER_WRITE_SPEAKER, 0x4040},
+	{SOUND_MIXER_WRITE_MIC, 0x0000}
+};
+#endif
+
+static int __init cs4297a_init(void)
+{
+	struct cs4297a_state *s;
+	u32 pwr, id;
+	mm_segment_t fs;
+	int rval;
+#ifndef CONFIG_BCM_CS4297A_CSWARM
+	u64 cfg;
+	int mdio_val;
+#endif
+
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO 
+		"cs4297a: cs4297a_init_module()+ \n"));
+
+#ifndef CONFIG_BCM_CS4297A_CSWARM
+        mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) &
+                (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT);
+
+        /* Check syscfg for synchronous serial on port 1 */
+        cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG);
+        if (!(cfg & M_SYS_SER1_ENABLE)) {
+                __raw_writeq(cfg | M_SYS_SER1_ENABLE, KSEG1+A_SCD_SYSTEM_CFG);
+                cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG);
+                if (!(cfg & M_SYS_SER1_ENABLE)) {
+                  printk(KERN_INFO "cs4297a: serial port 1 not configured for synchronous operation\n");
+                  return -1;
+                }
+
+                printk(KERN_INFO "cs4297a: serial port 1 switching to synchronous operation\n");
+                
+                /* Force the codec (on SWARM) to reset by clearing
+                   GENO, preserving MDIO (no effect on CSWARM) */
+                __raw_writeq(mdio_val, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
+                udelay(10);
+        }
+
+        /* Now set GENO */
+        __raw_writeq(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
+        /* Give the codec some time to finish resetting (start the bit clock) */
+        udelay(100);
+#endif
+
+	if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+		      "cs4297a: probe() no memory for state struct.\n"));
+		return -1;
+	}
+        s->magic = CS4297a_MAGIC;
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac.wait);
+	init_waitqueue_head(&s->dma_adc.reg_wait);
+	init_waitqueue_head(&s->dma_dac.reg_wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->open_wait_adc);
+	init_waitqueue_head(&s->open_wait_dac);
+	mutex_init(&s->open_sem_adc);
+	mutex_init(&s->open_sem_dac);
+	spin_lock_init(&s->lock);
+
+        s->irq = K_INT_SER_1;
+
+	if (request_irq
+	    (s->irq, cs4297a_interrupt, 0, "Crystal CS4297a", s)) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1,
+			  printk(KERN_ERR "cs4297a: irq %u in use\n", s->irq));
+		goto err_irq;
+	}
+	if ((s->dev_audio = register_sound_dsp(&cs4297a_audio_fops, -1)) <
+	    0) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs4297a: probe() register_sound_dsp() failed.\n"));
+		goto err_dev1;
+	}
+	if ((s->dev_mixer = register_sound_mixer(&cs4297a_mixer_fops, -1)) <
+	    0) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs4297a: probe() register_sound_mixer() failed.\n"));
+		goto err_dev2;
+	}
+
+        if (ser_init(s) || dma_init(s)) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs4297a: ser_init failed.\n"));
+		goto err_dev3;
+        }
+
+        do {
+                udelay(4000);
+                rval = cs4297a_read_ac97(s, AC97_POWER_CONTROL, &pwr);
+        } while (!rval && (pwr != 0xf));
+
+        if (!rval) {
+		char *sb1250_duart_present;
+
+                fs = get_fs();
+                set_fs(KERNEL_DS);
+#if 0
+                val = SOUND_MASK_LINE;
+                mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val);
+                for (i = 0; i < ARRAY_SIZE(initvol); i++) {
+                        val = initvol[i].vol;
+                        mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val);
+                }
+//                cs4297a_write_ac97(s, 0x18, 0x0808);
+#else
+                //                cs4297a_write_ac97(s, 0x5e, 0x180);
+                cs4297a_write_ac97(s, 0x02, 0x0808);
+                cs4297a_write_ac97(s, 0x18, 0x0808);
+#endif
+                set_fs(fs);
+
+                list_add(&s->list, &cs4297a_devs);
+
+                cs4297a_read_ac97(s, AC97_VENDOR_ID1, &id);
+
+		sb1250_duart_present = symbol_get(sb1250_duart_present);
+		if (sb1250_duart_present)
+			sb1250_duart_present[1] = 0;
+
+                printk(KERN_INFO "cs4297a: initialized (vendor id = %x)\n", id);
+
+                CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+                          printk(KERN_INFO "cs4297a: cs4297a_init_module()-\n"));
+                
+                return 0;
+        }
+
+ err_dev3:
+	unregister_sound_mixer(s->dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	free_irq(s->irq, s);
+ err_irq:
+	kfree(s);
+
+        printk(KERN_INFO "cs4297a: initialization failed\n");
+
+        return -1;
+}
+
+static void __exit cs4297a_cleanup(void)
+{
+        /*
+          XXXKW 
+           disable_irq, free_irq
+           drain DMA queue
+           disable DMA
+           disable TX/RX
+           free memory
+        */
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4297a: cleanup_cs4297a() finished\n"));
+}
+
+// --------------------------------------------------------------------- 
+
+MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
+MODULE_DESCRIPTION("Cirrus Logic CS4297a Driver for Broadcom SWARM board");
+
+// --------------------------------------------------------------------- 
+
+module_init(cs4297a_init);
+module_exit(cs4297a_cleanup);
diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c
new file mode 100644
index 00000000..8db6aefe
--- /dev/null
+++ b/sound/oss/sys_timer.c
@@ -0,0 +1,285 @@
+/*
+ * sound/oss/sys_timer.c
+ *
+ * The default timer for the Level 2 sequencer interface
+ * Uses the (1/HZ sec) timer of kernel.
+ */
+/*
+ * 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)
+ * Andrew Veliath  : adapted tmr2ticks from level 1 sequencer (avoid overflow)
+ */
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+static volatile int opened, tmr_running;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+
+static void     poll_def_tmr(unsigned long dummy);
+static DEFINE_SPINLOCK(lock);
+static DEFINE_TIMER(def_tmr, poll_def_tmr, 0, 0);
+
+static unsigned long
+tmr2ticks(int tmr_value)
+{
+	/*
+	 *    Convert timer ticks to MIDI ticks
+	 */
+
+	unsigned long tmp;
+	unsigned long scale;
+
+	/* tmr_value (ticks per sec) *
+	   1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */
+	tmp = tmr_value * (1000000 / HZ);
+	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
+	return (tmp + scale / 2) / scale;
+}
+
+static void
+poll_def_tmr(unsigned long dummy)
+{
+
+	if (opened)
+	  {
+
+		  {
+			  def_tmr.expires = (1) + jiffies;
+			  add_timer(&def_tmr);
+		  };
+
+		  if (tmr_running)
+		    {
+				spin_lock(&lock);
+			    tmr_ctr++;
+			    curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
+
+			    if (curr_ticks >= next_event_time)
+			      {
+				      next_event_time = (unsigned long) -1;
+				      sequencer_timer(0);
+			      }
+				spin_unlock(&lock);
+		    }
+	  }
+}
+
+static void
+tmr_reset(void)
+{
+	unsigned long   flags;
+
+	spin_lock_irqsave(&lock,flags);
+	tmr_offs = 0;
+	ticks_offs = 0;
+	tmr_ctr = 0;
+	next_event_time = (unsigned long) -1;
+	prev_event_time = 0;
+	curr_ticks = 0;
+	spin_unlock_irqrestore(&lock,flags);
+}
+
+static int
+def_tmr_open(int dev, int mode)
+{
+	if (opened)
+		return -EBUSY;
+
+	tmr_reset();
+	curr_tempo = 60;
+	curr_timebase = 100;
+	opened = 1;
+	{
+		def_tmr.expires = (1) + jiffies;
+		add_timer(&def_tmr);
+	};
+
+	return 0;
+}
+
+static void
+def_tmr_close(int dev)
+{
+	opened = tmr_running = 0;
+	del_timer(&def_tmr);
+}
+
+static int
+def_tmr_event(int dev, unsigned char *event)
+{
+	unsigned char   cmd = event[1];
+	unsigned long   parm = *(int *) &event[4];
+
+	switch (cmd)
+	  {
+	  case TMR_WAIT_REL:
+		  parm += prev_event_time;
+	  case TMR_WAIT_ABS:
+		  if (parm > 0)
+		    {
+			    long            time;
+
+			    if (parm <= curr_ticks)	/* It's the time */
+				    return TIMER_NOT_ARMED;
+
+			    time = parm;
+			    next_event_time = prev_event_time = time;
+
+			    return TIMER_ARMED;
+		    }
+		  break;
+
+	  case TMR_START:
+		  tmr_reset();
+		  tmr_running = 1;
+		  break;
+
+	  case TMR_STOP:
+		  tmr_running = 0;
+		  break;
+
+	  case TMR_CONTINUE:
+		  tmr_running = 1;
+		  break;
+
+	  case TMR_TEMPO:
+		  if (parm)
+		    {
+			    if (parm < 8)
+				    parm = 8;
+			    if (parm > 360)
+				    parm = 360;
+			    tmr_offs = tmr_ctr;
+			    ticks_offs += tmr2ticks(tmr_ctr);
+			    tmr_ctr = 0;
+			    curr_tempo = parm;
+		    }
+		  break;
+
+	  case TMR_ECHO:
+		  seq_copy_to_input(event, 8);
+		  break;
+
+	  default:;
+	  }
+
+	return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+def_tmr_get_time(int dev)
+{
+	if (!opened)
+		return 0;
+
+	return curr_ticks;
+}
+
+/* same as sound_timer.c:timer_ioctl!? */
+static int def_tmr_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	int __user *p = arg;
+	int val;
+
+	switch (cmd) {
+	case SNDCTL_TMR_SOURCE:
+		return __put_user(TMR_INTERNAL, p);
+
+	case SNDCTL_TMR_START:
+		tmr_reset();
+		tmr_running = 1;
+		return 0;
+
+	case SNDCTL_TMR_STOP:
+		tmr_running = 0;
+		return 0;
+
+	case SNDCTL_TMR_CONTINUE:
+		tmr_running = 1;
+		return 0;
+
+	case SNDCTL_TMR_TIMEBASE:
+		if (__get_user(val, p))
+			return -EFAULT;
+		if (val) {
+			if (val < 1)
+				val = 1;
+			if (val > 1000)
+				val = 1000;
+			curr_timebase = val;
+		}
+		return __put_user(curr_timebase, p);
+
+	case SNDCTL_TMR_TEMPO:
+		if (__get_user(val, p))
+			return -EFAULT;
+		if (val) {
+			if (val < 8)
+				val = 8;
+			if (val > 250)
+				val = 250;
+			tmr_offs = tmr_ctr;
+			ticks_offs += tmr2ticks(tmr_ctr);
+			tmr_ctr = 0;
+			curr_tempo = val;
+			reprogram_timer();
+		}
+		return __put_user(curr_tempo, p);
+
+	case SNDCTL_SEQ_CTRLRATE:
+		if (__get_user(val, p))
+			return -EFAULT;
+		if (val != 0)	/* Can't change */
+			return -EINVAL;
+		val = ((curr_tempo * curr_timebase) + 30) / 60;
+		return __put_user(val, p);
+		
+	case SNDCTL_SEQ_GETTIME:
+		return __put_user(curr_ticks, p);
+		
+	case SNDCTL_TMR_METRONOME:
+		/* NOP */
+		break;
+		
+	default:;
+	}
+	return -EINVAL;
+}
+
+static void
+def_tmr_arm(int dev, long time)
+{
+	if (time < 0)
+		time = curr_ticks + 1;
+	else if (time <= curr_ticks)	/* It's the time */
+		return;
+
+	next_event_time = prev_event_time = time;
+
+	return;
+}
+
+struct sound_timer_operations default_sound_timer =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"System clock", 0},
+	.priority	= 0,	/* Priority */
+	.devlink	= 0,	/* Local device link */
+	.open		= def_tmr_open,
+	.close		= def_tmr_close,
+	.event		= def_tmr_event,
+	.get_time	= def_tmr_get_time,
+	.ioctl		= def_tmr_ioctl,
+	.arm_timer	= def_tmr_arm
+};
diff --git a/sound/oss/trix.c b/sound/oss/trix.c
new file mode 100644
index 00000000..944e0c01
--- /dev/null
+++ b/sound/oss/trix.c
@@ -0,0 +1,525 @@
+/*
+ * sound/oss/trix.c
+ *
+ * Low level driver for the MediaTrix AudioTrix Pro
+ * (MT-0002-PC Control Chip)
+ *
+ *
+ * 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.
+ *
+ * Changes
+ *	Alan Cox		Modularisation, cleanup.
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *	Arnaldo C. de Melo	Got rid of attach_uart401
+ */
+ 
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+#include "sb.h"
+#include "sound_firmware.h"
+
+#include "ad1848.h"
+#include "mpu401.h"
+
+#include "trix_boot.h"
+
+static int mpu;
+
+static bool joystick;
+
+static unsigned char trix_read(int addr)
+{
+	outb(((unsigned char) addr), 0x390);	/* MT-0002-PC ASIC address */
+	return inb(0x391);	/* MT-0002-PC ASIC data */
+}
+
+static void trix_write(int addr, int data)
+{
+	outb(((unsigned char) addr), 0x390);	/* MT-0002-PC ASIC address */
+	outb(((unsigned char) data), 0x391);	/* MT-0002-PC ASIC data */
+}
+
+static void download_boot(int base)
+{
+	int i = 0, n = trix_boot_len;
+
+	if (trix_boot_len == 0)
+		return;
+
+	trix_write(0xf8, 0x00);	/* ??????? */
+	outb((0x01), base + 6);	/* Clear the internal data pointer */
+	outb((0x00), base + 6);	/* Restart */
+
+	/*
+	   *  Write the boot code to the RAM upload/download register.
+	   *  Each write increments the internal data pointer.
+	 */
+	outb((0x01), base + 6);	/* Clear the internal data pointer */
+	outb((0x1A), 0x390);	/* Select RAM download/upload port */
+
+	for (i = 0; i < n; i++)
+		outb((trix_boot[i]), 0x391);
+	for (i = n; i < 10016; i++)	/* Clear up to first 16 bytes of data RAM */
+		outb((0x00), 0x391);
+	outb((0x00), base + 6);	/* Reset */
+	outb((0x50), 0x390);	/* ?????? */
+
+}
+
+static int trix_set_wss_port(struct address_info *hw_config)
+{
+	unsigned char   addr_bits;
+
+	if (trix_read(0x15) != 0x71)	/* No ASIC signature */
+	{
+		MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n"));
+		return 0;
+	}
+
+	/*
+	 * Reset some registers.
+	 */
+
+	trix_write(0x13, 0);
+	trix_write(0x14, 0);
+
+	/*
+	 * Configure the ASIC to place the codec to the proper I/O location
+	 */
+
+	switch (hw_config->io_base)
+	{
+		case 0x530:
+			addr_bits = 0;
+			break;
+		case 0x604:
+			addr_bits = 1;
+			break;
+		case 0xE80:
+			addr_bits = 2;
+			break;
+		case 0xF40:
+			addr_bits = 3;
+			break;
+		default:
+			return 0;
+	}
+
+	trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits);
+	return 1;
+}
+
+/*
+ *    Probe and attach routines for the Windows Sound System mode of
+ *      AudioTrix Pro
+ */
+
+static int __init init_trix_wss(struct address_info *hw_config)
+{
+	static unsigned char dma_bits[4] = {
+		1, 2, 0, 3
+	};
+	struct resource *ports;
+	int config_port = hw_config->io_base + 0;
+	int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+	int old_num_mixers = num_mixers;
+	u8 config, bits;
+	int ret;
+ 
+	switch(hw_config->irq) {
+	case 7:
+		bits = 8;
+		break;
+	case 9:
+		bits = 0x10;
+		break;
+	case 10:
+		bits = 0x18;
+		break;
+	case 11:
+		bits = 0x20;
+		break;
+	default:
+		printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+
+	switch (dma1) {
+	case 0:
+	case 1:
+	case 3:
+		break;
+	default:
+		printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", dma1);
+		return 0;
+	}
+
+	switch (dma2) {
+	case -1:
+	case 0:
+	case 1:
+	case 3:
+		break;
+	default:
+		printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", dma2);
+		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.
+	 */
+	ports = request_region(hw_config->io_base + 4, 4, "ad1848");
+	if (!ports) {
+		printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
+		return 0;
+	}
+
+	if (!request_region(hw_config->io_base, 4, "MSS config")) {
+		printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
+		release_region(hw_config->io_base + 4, 4);
+		return 0;
+	}
+
+	if (!trix_set_wss_port(hw_config))
+		goto fail;
+
+	config = inb(hw_config->io_base + 3);
+
+	if ((config & 0x3f) != 0x00)
+	{
+		MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base));
+		goto fail;
+	}
+
+	/*
+	 * Check that DMA0 is not in use with a 8 bit board.
+	 */
+
+	if (dma1 == 0 && config & 0x80)
+	{
+		printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n");
+		goto fail;
+	}
+	if (hw_config->irq > 9 && config & 0x80)
+	{
+		printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq);
+		goto fail;
+	}
+
+	ret = ad1848_detect(ports, NULL, hw_config->osp);
+	if (!ret)
+		goto fail;
+
+	if (joystick==1)
+		trix_write(0x15, 0x80);
+
+	/*
+	 * Set the IRQ and DMA addresses.
+	 */
+
+	outb((bits | 0x40), config_port);
+
+	if (dma2 == -1 || dma2 == dma1)
+	{
+		  bits |= dma_bits[dma1];
+		  dma2 = dma1;
+	}
+	else
+	{
+		unsigned char tmp;
+
+		tmp = trix_read(0x13) & ~30;
+		trix_write(0x13, tmp | 0x80 | (dma1 << 4));
+
+		tmp = trix_read(0x14) & ~30;
+		trix_write(0x14, tmp | 0x80 | (dma2 << 4));
+	}
+
+	outb((bits), config_port);	/* Write IRQ+DMA setup */
+
+	hw_config->slots[0] = ad1848_init("AudioTrix Pro", ports,
+					  hw_config->irq,
+					  dma1,
+					  dma2,
+					  0,
+					  hw_config->osp,
+					  THIS_MODULE);
+
+	if (num_mixers > old_num_mixers)	/* Mixer got installed */
+	{
+		AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);	/* Line in */
+		AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+		AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH);		/* OPL4 */
+		AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM);	/* SB */
+	}
+	return 1;
+
+fail:
+	release_region(hw_config->io_base, 4);
+	release_region(hw_config->io_base + 4, 4);
+	return 0;
+}
+
+static int __init probe_trix_sb(struct address_info *hw_config)
+{
+
+	int tmp;
+	unsigned char conf;
+	extern int sb_be_quiet;
+	int old_quiet;
+	static signed char irq_translate[] = {
+		-1, -1, -1, 0, 1, 2, -1, 3
+	};
+
+	if (trix_boot_len == 0)
+		return 0;	/* No boot code -> no fun */
+
+	if ((hw_config->io_base & 0xffffff8f) != 0x200)
+		return 0;
+
+	tmp = hw_config->irq;
+	if (tmp > 7)
+		return 0;
+	if (irq_translate[tmp] == -1)
+		return 0;
+
+	tmp = hw_config->dma;
+	if (tmp != 1 && tmp != 3)
+		return 0;
+
+	if (!request_region(hw_config->io_base, 16, "soundblaster")) {
+		printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base);
+		return 0;
+	}
+
+	conf = 0x84;		/* DMA and IRQ enable */
+	conf |= hw_config->io_base & 0x70;	/* I/O address bits */
+	conf |= irq_translate[hw_config->irq];
+	if (hw_config->dma == 3)
+		conf |= 0x08;
+	trix_write(0x1b, conf);
+
+	download_boot(hw_config->io_base);
+
+	hw_config->name = "AudioTrix SB";
+	if (!sb_dsp_detect(hw_config, 0, 0, NULL)) {
+		release_region(hw_config->io_base, 16);
+		return 0;
+	}
+
+	hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
+
+	/* Prevent false alarms */
+	old_quiet = sb_be_quiet;
+	sb_be_quiet = 1;
+
+	sb_dsp_init(hw_config, THIS_MODULE);
+
+	sb_be_quiet = old_quiet;
+	return 1;
+}
+
+static int __init probe_trix_mpu(struct address_info *hw_config)
+{
+	unsigned char conf;
+	static int irq_bits[] = {
+		-1, -1, -1, 1, 2, 3, -1, 4, -1, 5
+	};
+
+	if (hw_config->irq > 9)
+	{
+		printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	if (irq_bits[hw_config->irq] == -1)
+	{
+		printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	switch (hw_config->io_base)
+	{
+		case 0x330:
+			conf = 0x00;
+			break;
+		case 0x370:
+			conf = 0x04;
+			break;
+		case 0x3b0:
+			conf = 0x08;
+			break;
+		case 0x3f0:
+			conf = 0x0c;
+			break;
+		default:
+			return 0;	/* Invalid port */
+	}
+
+	conf |= irq_bits[hw_config->irq] << 4;
+	trix_write(0x19, (trix_read(0x19) & 0x83) | conf);
+	hw_config->name = "AudioTrix Pro";
+	return probe_uart401(hw_config, THIS_MODULE);
+}
+
+static void __exit unload_trix_wss(struct address_info *hw_config)
+{
+	int dma2 = hw_config->dma2;
+
+	if (dma2 == -1)
+		dma2 = hw_config->dma;
+
+	release_region(0x390, 2);
+	release_region(hw_config->io_base, 4);
+
+	ad1848_unload(hw_config->io_base + 4,
+		      hw_config->irq,
+		      hw_config->dma,
+		      dma2,
+		      0);
+	sound_unload_audiodev(hw_config->slots[0]);
+}
+
+static inline void __exit unload_trix_mpu(struct address_info *hw_config)
+{
+	unload_uart401(hw_config);
+}
+
+static inline void __exit unload_trix_sb(struct address_info *hw_config)
+{
+	sb_dsp_unload(hw_config, mpu);
+}
+
+static struct address_info cfg;
+static struct address_info cfg2;
+static struct address_info cfg_mpu;
+
+static int sb;
+static int fw_load;
+
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma2	= -1;	/* Set this for modules that need it */
+static int __initdata sb_io	= -1;
+static int __initdata sb_dma	= -1;
+static int __initdata sb_irq	= -1;
+static int __initdata mpu_io	= -1;
+static int __initdata mpu_irq	= -1;
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(dma, int, 0);
+module_param(dma2, int, 0);
+module_param(sb_io, int, 0);
+module_param(sb_dma, int, 0);
+module_param(sb_irq, int, 0);
+module_param(mpu_io, int, 0);
+module_param(mpu_irq, int, 0);
+module_param(joystick, bool, 0);
+
+static int __init init_trix(void)
+{
+	printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma2;
+
+	cfg2.io_base = sb_io;
+	cfg2.irq = sb_irq;
+	cfg2.dma = sb_dma;
+
+	cfg_mpu.io_base = mpu_io;
+	cfg_mpu.irq = mpu_irq;
+
+	if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
+		printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
+		return -EINVAL;
+	}
+
+	if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) {
+		printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n");
+		return -EINVAL;
+	}
+	if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) {
+		printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n");
+		return -EINVAL;
+	}
+	if (!trix_boot)
+	{
+		fw_load = 1;
+		trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin",
+						    (char **) &trix_boot);
+	}
+
+	if (!request_region(0x390, 2, "AudioTrix")) {
+		printk(KERN_ERR "AudioTrix: Config port I/O conflict\n");
+		return -ENODEV;
+	}
+
+	if (!init_trix_wss(&cfg)) {
+		release_region(0x390, 2);
+		return -ENODEV;
+	}
+
+	/*
+	 *    We must attach in the right order to get the firmware
+	 *      loaded up in time.
+	 */
+
+	if (cfg2.io_base != -1) {
+		sb = probe_trix_sb(&cfg2);
+	}
+	
+	if (cfg_mpu.io_base != -1)
+		mpu = probe_trix_mpu(&cfg_mpu);
+
+	return 0;
+}
+
+static void __exit cleanup_trix(void)
+{
+	if (fw_load && trix_boot)
+		vfree(trix_boot);
+	if (sb)
+		unload_trix_sb(&cfg2);
+	if (mpu)
+		unload_trix_mpu(&cfg_mpu);
+	unload_trix_wss(&cfg);
+}
+
+module_init(init_trix);
+module_exit(cleanup_trix);
+
+#ifndef MODULE
+static int __init setup_trix (char *str)
+{
+	/* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */
+	int ints[9];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+	sb_io	= ints[5];
+	sb_irq	= ints[6];
+	sb_dma	= ints[6];
+	mpu_io	= ints[7];
+	mpu_irq	= ints[8];
+
+	return 1;
+}
+
+__setup("trix=", setup_trix);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/tuning.h b/sound/oss/tuning.h
new file mode 100644
index 00000000..a73e3dd3
--- /dev/null
+++ b/sound/oss/tuning.h
@@ -0,0 +1,23 @@
+static unsigned short semitone_tuning[24] =
+{
+/*   0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, 
+/*   8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, 
+/*  16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+static unsigned short cent_tuning[100] =
+{
+/*   0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, 
+/*   8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, 
+/*  16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, 
+/*  24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, 
+/*  32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, 
+/*  40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, 
+/*  48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, 
+/*  56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, 
+/*  64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, 
+/*  72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, 
+/*  80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, 
+/*  88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, 
+/*  96 */ 10570, 10576, 10582, 10589
+};
diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c
new file mode 100644
index 00000000..8e514a67
--- /dev/null
+++ b/sound/oss/uart401.c
@@ -0,0 +1,482 @@
+/*
+ * sound/oss/uart401.c
+ *
+ * MPU-401 UART driver (formerly uart401_midi.c)
+ *
+ *
+ * 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.
+ *
+ * Changes:
+ *	Alan Cox		Reformatted, removed sound_mem usage, use normal Linux
+ *				interrupt allocation. Protect against bogus unload
+ *				Fixed to allow IRQ > 15
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *	Arnaldo C. de Melo	got rid of check_region
+ *
+ * Status:
+ *		Untested
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#include "mpu401.h"
+
+typedef struct uart401_devc
+{
+	int             base;
+	int             irq;
+	int            *osp;
+	void            (*midi_input_intr) (int dev, unsigned char data);
+	int             opened, disabled;
+	volatile unsigned char input_byte;
+	int             my_dev;
+	int             share_irq;
+	spinlock_t	lock;
+}
+uart401_devc;
+
+#define	DATAPORT   (devc->base)
+#define	COMDPORT   (devc->base+1)
+#define	STATPORT   (devc->base+1)
+
+static int uart401_status(uart401_devc * devc)
+{
+	return inb(STATPORT);
+}
+
+#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
+#define output_ready(devc)	(!(uart401_status(devc)&OUTPUT_READY))
+
+static void uart401_cmd(uart401_devc * devc, unsigned char cmd)
+{
+	outb((cmd), COMDPORT);
+}
+
+static int uart401_read(uart401_devc * devc)
+{
+	return inb(DATAPORT);
+}
+
+static void uart401_write(uart401_devc * devc, unsigned char byte)
+{
+	outb((byte), DATAPORT);
+}
+
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	MPU_RESET	0xFF
+#define	UART_MODE_ON	0x3F
+
+static int      reset_uart401(uart401_devc * devc);
+static void     enter_uart_mode(uart401_devc * devc);
+
+static void uart401_input_loop(uart401_devc * devc)
+{
+	int work_limit=30000;
+	
+	while (input_avail(devc) && --work_limit)
+	{
+		unsigned char   c = uart401_read(devc);
+
+		if (c == MPU_ACK)
+			devc->input_byte = c;
+		else if (devc->opened & OPEN_READ && devc->midi_input_intr)
+			devc->midi_input_intr(devc->my_dev, c);
+	}
+	if(work_limit==0)
+		printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base);
+}
+
+irqreturn_t uart401intr(int irq, void *dev_id)
+{
+	uart401_devc *devc = dev_id;
+
+	if (devc == NULL)
+	{
+		printk(KERN_ERR "uart401: bad devc\n");
+		return IRQ_NONE;
+	}
+
+	if (input_avail(devc))
+		uart401_input_loop(devc);
+	return IRQ_HANDLED;
+}
+
+static int
+uart401_open(int dev, int mode,
+	     void            (*input) (int dev, unsigned char data),
+	     void            (*output) (int dev)
+)
+{
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+	if (devc->opened)
+		return -EBUSY;
+
+	/* Flush the UART */
+	
+	while (input_avail(devc))
+		uart401_read(devc);
+
+	devc->midi_input_intr = input;
+	devc->opened = mode;
+	enter_uart_mode(devc);
+	devc->disabled = 0;
+
+	return 0;
+}
+
+static void uart401_close(int dev)
+{
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+	reset_uart401(devc);
+	devc->opened = 0;
+}
+
+static int uart401_out(int dev, unsigned char midi_byte)
+{
+	int timeout;
+	unsigned long flags;
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+	if (devc->disabled)
+		return 1;
+	/*
+	 * Test for input since pending input seems to block the output.
+	 */
+
+	spin_lock_irqsave(&devc->lock,flags);	
+	if (input_avail(devc))
+		uart401_input_loop(devc);
+
+	spin_unlock_irqrestore(&devc->lock,flags);
+
+	/*
+	 * Sometimes it takes about 13000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+
+	if (!output_ready(devc))
+	{
+		  printk(KERN_WARNING "uart401: Timeout - Device not responding\n");
+		  devc->disabled = 1;
+		  reset_uart401(devc);
+		  enter_uart_mode(devc);
+		  return 1;
+	}
+	uart401_write(devc, midi_byte);
+	return 1;
+}
+
+static inline int uart401_start_read(int dev)
+{
+	return 0;
+}
+
+static inline int uart401_end_read(int dev)
+{
+	return 0;
+}
+
+static inline void uart401_kick(int dev)
+{
+}
+
+static inline int uart401_buffer_status(int dev)
+{
+	return 0;
+}
+
+#define MIDI_SYNTH_NAME	"MPU-401 UART"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static const struct midi_operations uart401_operations =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
+	.converter	= &std_midi_synth,
+	.in_info	= {0},
+	.open		= uart401_open,
+	.close		= uart401_close,
+	.outputc	= uart401_out,
+	.start_read	= uart401_start_read,
+	.end_read	= uart401_end_read,
+	.kick		= uart401_kick,
+	.buffer_status	= uart401_buffer_status,
+};
+
+static void enter_uart_mode(uart401_devc * devc)
+{
+	int ok, timeout;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock,flags);	
+	for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+
+	devc->input_byte = 0;
+	uart401_cmd(devc, UART_MODE_ON);
+
+	ok = 0;
+	for (timeout = 50000; timeout > 0 && !ok; timeout--)
+		if (devc->input_byte == MPU_ACK)
+			ok = 1;
+		else if (input_avail(devc))
+			if (uart401_read(devc) == MPU_ACK)
+				ok = 1;
+
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int reset_uart401(uart401_devc * devc)
+{
+	int ok, timeout, n;
+
+	/*
+	 * Send the RESET command. Try again if no success at the first time.
+	 */
+
+	ok = 0;
+
+	for (n = 0; n < 2 && !ok; n++)
+	{
+		for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+		devc->input_byte = 0;
+		uart401_cmd(devc, MPU_RESET);
+
+		/*
+		 * Wait at least 25 msec. This method is not accurate so let's make the
+		 * loop bit longer. Cannot sleep since this is called during boot.
+		 */
+
+		for (timeout = 50000; timeout > 0 && !ok; timeout--)
+		{
+			if (devc->input_byte == MPU_ACK)	/* Interrupt */
+				ok = 1;
+			else if (input_avail(devc))
+			{
+				if (uart401_read(devc) == MPU_ACK)
+					ok = 1;
+			}
+		}
+	}
+
+
+	if (ok)
+	{
+		DEB(printk("Reset UART401 OK\n"));
+	}
+	else
+		DDB(printk("Reset UART401 failed - No hardware detected.\n"));
+
+	if (ok)
+		uart401_input_loop(devc);	/*
+						 * Flush input before enabling interrupts
+						 */
+
+	return ok;
+}
+
+int probe_uart401(struct address_info *hw_config, struct module *owner)
+{
+	uart401_devc *devc;
+	char *name = "MPU-401 (UART) MIDI";
+	int ok = 0;
+	unsigned long flags;
+
+	DDB(printk("Entered probe_uart401()\n"));
+
+	/* Default to "not found" */
+	hw_config->slots[4] = -1;
+
+	if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) {
+		printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base);
+		return 0;
+	}
+
+	devc = kmalloc(sizeof(uart401_devc), GFP_KERNEL);
+	if (!devc) {
+		printk(KERN_WARNING "uart401: Can't allocate memory\n");
+		goto cleanup_region;
+	}
+
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->osp = hw_config->osp;
+	devc->midi_input_intr = NULL;
+	devc->opened = 0;
+	devc->input_byte = 0;
+	devc->my_dev = 0;
+	devc->share_irq = 0;
+	spin_lock_init(&devc->lock);
+
+	spin_lock_irqsave(&devc->lock,flags);	
+	ok = reset_uart401(devc);
+	spin_unlock_irqrestore(&devc->lock,flags);
+
+	if (!ok)
+		goto cleanup_devc;
+
+	if (hw_config->name)
+		name = hw_config->name;
+
+	if (devc->irq < 0) {
+		devc->share_irq = 1;
+		devc->irq *= -1;
+	} else
+		devc->share_irq = 0;
+
+	if (!devc->share_irq)
+		if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) {
+			printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
+			devc->share_irq = 1;
+		}
+	devc->my_dev = sound_alloc_mididev();
+	enter_uart_mode(devc);
+
+	if (devc->my_dev == -1) {
+		printk(KERN_INFO "uart401: Too many midi devices detected\n");
+		goto cleanup_irq;
+	}
+	conf_printf(name, hw_config);
+	midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
+	if (!midi_devs[devc->my_dev]) {
+		printk(KERN_ERR "uart401: Failed to allocate memory\n");
+		goto cleanup_unload_mididev;
+	}
+	memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations));
+
+	if (owner)
+		midi_devs[devc->my_dev]->owner = owner;
+	
+	midi_devs[devc->my_dev]->devc = devc;
+	midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
+	if (!midi_devs[devc->my_dev]->converter) {
+		printk(KERN_WARNING "uart401: Failed to allocate memory\n");
+		goto cleanup_midi_devs;
+	}
+	memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations));
+	strcpy(midi_devs[devc->my_dev]->info.name, name);
+	midi_devs[devc->my_dev]->converter->id = "UART401";
+	midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev;
+
+	if (owner)
+		midi_devs[devc->my_dev]->converter->owner = owner;
+
+	hw_config->slots[4] = devc->my_dev;
+	sequencer_init();
+	devc->opened = 0;
+	return 1;
+cleanup_midi_devs:
+	kfree(midi_devs[devc->my_dev]);
+cleanup_unload_mididev:
+	sound_unload_mididev(devc->my_dev);
+cleanup_irq:
+	if (!devc->share_irq)
+		free_irq(devc->irq, devc);
+cleanup_devc:
+	kfree(devc);
+cleanup_region:
+	release_region(hw_config->io_base, 4);
+	return 0;
+}
+
+void unload_uart401(struct address_info *hw_config)
+{
+	uart401_devc *devc;
+	int n=hw_config->slots[4];
+	
+	/* Not set up */
+	if(n==-1 || midi_devs[n]==NULL)
+		return;
+		
+	/* Not allocated (erm ??) */
+	
+	devc = midi_devs[hw_config->slots[4]]->devc;
+	if (devc == NULL)
+		return;
+
+	reset_uart401(devc);
+	release_region(hw_config->io_base, 4);
+
+	if (!devc->share_irq)
+		free_irq(devc->irq, devc);
+	if (devc)
+	{
+		kfree(midi_devs[devc->my_dev]->converter);
+		kfree(midi_devs[devc->my_dev]);
+		kfree(devc);
+		devc = NULL;
+	}
+	/* This kills midi_devs[x] */
+	sound_unload_mididev(hw_config->slots[4]);
+}
+
+EXPORT_SYMBOL(probe_uart401);
+EXPORT_SYMBOL(unload_uart401);
+EXPORT_SYMBOL(uart401intr);
+
+static struct address_info cfg_mpu;
+
+static int io = -1;
+static int irq = -1;
+
+module_param(io, int, 0444);
+module_param(irq, int, 0444);
+
+
+static int __init init_uart401(void)
+{
+	cfg_mpu.irq = irq;
+	cfg_mpu.io_base = io;
+
+	/* Can be loaded either for module use or to provide functions
+	   to others */
+	if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) {
+		printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
+		if (!probe_uart401(&cfg_mpu, THIS_MODULE))
+			return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_uart401(void)
+{
+	if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1)
+		unload_uart401(&cfg_mpu);
+}
+
+module_init(init_uart401);
+module_exit(cleanup_uart401);
+
+#ifndef MODULE
+static int __init setup_uart401(char *str)
+{
+	/* io, irq */
+	int ints[3];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io = ints[1];
+	irq = ints[2];
+	
+	return 1;
+}
+
+__setup("uart401=", setup_uart401);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/uart6850.c b/sound/oss/uart6850.c
new file mode 100644
index 00000000..f3f914aa
--- /dev/null
+++ b/sound/oss/uart6850.c
@@ -0,0 +1,361 @@
+/*
+ * sound/oss/uart6850.c
+ *
+ *
+ * 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.
+ * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver.
+ * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2.
+ *
+ * Alan Cox:		Updated for new modular code. Removed snd_* irq handling. Now
+ *			uses native linux resources
+ * Christoph Hellwig:	Adapted to module_init/module_exit
+ * Jeff Garzik:		Made it work again, in theory
+ *			FIXME: If the request_irq() succeeds, the probe succeeds. Ug.
+ *
+ *	Status: Testing required (no shit -jgarzik)
+ *
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
+ *      added 6850 support, used with COVOX SoundMaster II and custom cards.
+ */
+
+#include "sound_config.h"
+
+static int uart6850_base = 0x330;
+
+static int *uart6850_osp;
+
+#define	DATAPORT   (uart6850_base)
+#define	COMDPORT   (uart6850_base+1)
+#define	STATPORT   (uart6850_base+1)
+
+static int uart6850_status(void)
+{
+	return inb(STATPORT);
+}
+
+#define input_avail()		(uart6850_status()&INPUT_AVAIL)
+#define output_ready()		(uart6850_status()&OUTPUT_READY)
+
+static void uart6850_cmd(unsigned char cmd)
+{
+	outb(cmd, COMDPORT);
+}
+
+static int uart6850_read(void)
+{
+	return inb(DATAPORT);
+}
+
+static void uart6850_write(unsigned char byte)
+{
+	outb(byte, DATAPORT);
+}
+
+#define	OUTPUT_READY	0x02	/* Mask for data ready Bit */
+#define	INPUT_AVAIL	0x01	/* Mask for Data Send Ready Bit */
+
+#define	UART_RESET	0x95
+#define	UART_MODE_ON	0x03
+
+static int uart6850_opened;
+static int uart6850_irq;
+static int uart6850_detected;
+static int my_dev;
+static DEFINE_SPINLOCK(lock);
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+static void poll_uart6850(unsigned long dummy);
+
+
+static DEFINE_TIMER(uart6850_timer, poll_uart6850, 0, 0);
+
+static void uart6850_input_loop(void)
+{
+	int count = 10;
+
+	while (count)
+	{
+		/*
+		 * Not timed out
+		 */
+		if (input_avail())
+		{
+			unsigned char c = uart6850_read();
+			count = 100;
+			if (uart6850_opened & OPEN_READ)
+				midi_input_intr(my_dev, c);
+		}
+		else
+		{
+			while (!input_avail() && count)
+				count--;
+		}
+	}
+}
+
+static irqreturn_t m6850intr(int irq, void *dev_id)
+{
+	if (input_avail())
+		uart6850_input_loop();
+	return IRQ_HANDLED;
+}
+
+/*
+ *	It looks like there is no input interrupts in the UART mode. Let's try
+ *	polling.
+ */
+
+static void poll_uart6850(unsigned long dummy)
+{
+	unsigned long flags;
+
+	if (!(uart6850_opened & OPEN_READ))
+		return;		/* Device has been closed */
+
+	spin_lock_irqsave(&lock,flags);
+	if (input_avail())
+		uart6850_input_loop();
+
+	uart6850_timer.expires = 1 + jiffies;
+	add_timer(&uart6850_timer);
+	
+	/*
+	 *	Come back later
+	 */
+
+	spin_unlock_irqrestore(&lock,flags);
+}
+
+static int uart6850_open(int dev, int mode,
+	      void            (*input) (int dev, unsigned char data),
+	      void            (*output) (int dev)
+)
+{
+	if (uart6850_opened)
+	{
+/*		  printk("Midi6850: Midi busy\n");*/
+		  return -EBUSY;
+	};
+
+	uart6850_cmd(UART_RESET);
+	uart6850_input_loop();
+	midi_input_intr = input;
+	uart6850_opened = mode;
+	poll_uart6850(0);	/*
+				 * Enable input polling
+				 */
+
+	return 0;
+}
+
+static void uart6850_close(int dev)
+{
+	uart6850_cmd(UART_MODE_ON);
+	del_timer(&uart6850_timer);
+	uart6850_opened = 0;
+}
+
+static int uart6850_out(int dev, unsigned char midi_byte)
+{
+	int timeout;
+	unsigned long flags;
+
+	/*
+	 * Test for input since pending input seems to block the output.
+	 */
+
+	spin_lock_irqsave(&lock,flags);
+
+	if (input_avail())
+		uart6850_input_loop();
+
+	spin_unlock_irqrestore(&lock,flags);
+
+	/*
+	 * Sometimes it takes about 13000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	for (timeout = 30000; timeout > 0 && !output_ready(); timeout--);	/*
+										 * Wait
+										 */
+	if (!output_ready())
+	{
+		printk(KERN_WARNING "Midi6850: Timeout\n");
+		return 0;
+	}
+	uart6850_write(midi_byte);
+	return 1;
+}
+
+static inline int uart6850_command(int dev, unsigned char *midi_byte)
+{
+	return 1;
+}
+
+static inline int uart6850_start_read(int dev)
+{
+	return 0;
+}
+
+static inline int uart6850_end_read(int dev)
+{
+	return 0;
+}
+
+static inline void uart6850_kick(int dev)
+{
+}
+
+static inline int uart6850_buffer_status(int dev)
+{
+	return 0;		/*
+				 * No data in buffers
+				 */
+}
+
+#define MIDI_SYNTH_NAME	"6850 UART Midi"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations uart6850_operations =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"6850 UART", 0, 0, SNDCARD_UART6850},
+	.converter	= &std_midi_synth,
+	.in_info	= {0},
+	.open		= uart6850_open,
+	.close		= uart6850_close,
+	.outputc	= uart6850_out,
+	.start_read	= uart6850_start_read,
+	.end_read	= uart6850_end_read,
+	.kick		= uart6850_kick,
+	.command	= uart6850_command,
+	.buffer_status	= uart6850_buffer_status
+};
+
+
+static void __init attach_uart6850(struct address_info *hw_config)
+{
+	int ok, timeout;
+	unsigned long   flags;
+
+	if (!uart6850_detected)
+		return;
+
+	if ((my_dev = sound_alloc_mididev()) == -1)
+	{
+		printk(KERN_INFO "uart6850: Too many midi devices detected\n");
+		return;
+	}
+	uart6850_base = hw_config->io_base;
+	uart6850_osp = hw_config->osp;
+	uart6850_irq = hw_config->irq;
+
+	spin_lock_irqsave(&lock,flags);
+
+	for (timeout = 30000; timeout > 0 && !output_ready(); timeout--);	/*
+										 * Wait
+										 */
+	uart6850_cmd(UART_MODE_ON);
+	ok = 1;
+	spin_unlock_irqrestore(&lock,flags);
+
+	conf_printf("6850 Midi Interface", hw_config);
+
+	std_midi_synth.midi_dev = my_dev;
+	hw_config->slots[4] = my_dev;
+	midi_devs[my_dev] = &uart6850_operations;
+	sequencer_init();
+}
+
+static inline int reset_uart6850(void)
+{
+	uart6850_read();
+	return 1;		/*
+				 * OK
+				 */
+}
+
+static int __init probe_uart6850(struct address_info *hw_config)
+{
+	int ok;
+
+	uart6850_osp = hw_config->osp;
+	uart6850_base = hw_config->io_base;
+	uart6850_irq = hw_config->irq;
+
+	if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0)
+		return 0;
+
+	ok = reset_uart6850();
+	uart6850_detected = ok;
+	return ok;
+}
+
+static void __exit unload_uart6850(struct address_info *hw_config)
+{
+	free_irq(hw_config->irq, NULL);
+	sound_unload_mididev(hw_config->slots[4]);
+}
+
+static struct address_info cfg_mpu;
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+
+static int __init init_uart6850(void)
+{
+	cfg_mpu.io_base = io;
+	cfg_mpu.irq = irq;
+
+	if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) {
+		printk(KERN_INFO "uart6850: irq and io must be set.\n");
+		return -EINVAL;
+	}
+
+	if (probe_uart6850(&cfg_mpu))
+		return -ENODEV;
+	attach_uart6850(&cfg_mpu);
+
+	return 0;
+}
+
+static void __exit cleanup_uart6850(void)
+{
+	unload_uart6850(&cfg_mpu);
+}
+
+module_init(init_uart6850);
+module_exit(cleanup_uart6850);
+
+#ifndef MODULE
+static int __init setup_uart6850(char *str)
+{
+	/* io, irq */
+	int ints[3];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+	irq = ints[2];
+
+	return 1;
+}
+__setup("uart6850=", setup_uart6850);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/ulaw.h b/sound/oss/ulaw.h
new file mode 100644
index 00000000..0ff8c0a3
--- /dev/null
+++ b/sound/oss/ulaw.h
@@ -0,0 +1,69 @@
+static unsigned char ulaw_dsp[] = {
+     3,    7,   11,   15,   19,   23,   27,   31,
+    35,   39,   43,   47,   51,   55,   59,   63,
+    66,   68,   70,   72,   74,   76,   78,   80,
+    82,   84,   86,   88,   90,   92,   94,   96,
+    98,   99,  100,  101,  102,  103,  104,  105,
+   106,  107,  108,  109,  110,  111,  112,  113,
+   113,  114,  114,  115,  115,  116,  116,  117,
+   117,  118,  118,  119,  119,  120,  120,  121,
+   121,  121,  122,  122,  122,  122,  123,  123,
+   123,  123,  124,  124,  124,  124,  125,  125,
+   125,  125,  125,  125,  126,  126,  126,  126,
+   126,  126,  126,  126,  127,  127,  127,  127,
+   127,  127,  127,  127,  127,  127,  127,  127,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   253,  249,  245,  241,  237,  233,  229,  225,
+   221,  217,  213,  209,  205,  201,  197,  193,
+   190,  188,  186,  184,  182,  180,  178,  176,
+   174,  172,  170,  168,  166,  164,  162,  160,
+   158,  157,  156,  155,  154,  153,  152,  151,
+   150,  149,  148,  147,  146,  145,  144,  143,
+   143,  142,  142,  141,  141,  140,  140,  139,
+   139,  138,  138,  137,  137,  136,  136,  135,
+   135,  135,  134,  134,  134,  134,  133,  133,
+   133,  133,  132,  132,  132,  132,  131,  131,
+   131,  131,  131,  131,  130,  130,  130,  130,
+   130,  130,  130,  130,  129,  129,  129,  129,
+   129,  129,  129,  129,  129,  129,  129,  129,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+};
+
+static unsigned char dsp_ulaw[] = {
+     0,    0,    0,    0,    0,    1,    1,    1,
+     1,    2,    2,    2,    2,    3,    3,    3,
+     3,    4,    4,    4,    4,    5,    5,    5,
+     5,    6,    6,    6,    6,    7,    7,    7,
+     7,    8,    8,    8,    8,    9,    9,    9,
+     9,   10,   10,   10,   10,   11,   11,   11,
+    11,   12,   12,   12,   12,   13,   13,   13,
+    13,   14,   14,   14,   14,   15,   15,   15,
+    15,   16,   16,   17,   17,   18,   18,   19,
+    19,   20,   20,   21,   21,   22,   22,   23,
+    23,   24,   24,   25,   25,   26,   26,   27,
+    27,   28,   28,   29,   29,   30,   30,   31,
+    31,   32,   33,   34,   35,   36,   37,   38,
+    39,   40,   41,   42,   43,   44,   45,   46,
+    47,   49,   51,   53,   55,   57,   59,   61,
+    63,   66,   70,   74,   78,   84,   92,  104,
+   254,  231,  219,  211,  205,  201,  197,  193,
+   190,  188,  186,  184,  182,  180,  178,  176,
+   175,  174,  173,  172,  171,  170,  169,  168,
+   167,  166,  165,  164,  163,  162,  161,  160,
+   159,  159,  158,  158,  157,  157,  156,  156,
+   155,  155,  154,  154,  153,  153,  152,  152,
+   151,  151,  150,  150,  149,  149,  148,  148,
+   147,  147,  146,  146,  145,  145,  144,  144,
+   143,  143,  143,  143,  142,  142,  142,  142,
+   141,  141,  141,  141,  140,  140,  140,  140,
+   139,  139,  139,  139,  138,  138,  138,  138,
+   137,  137,  137,  137,  136,  136,  136,  136,
+   135,  135,  135,  135,  134,  134,  134,  134,
+   133,  133,  133,  133,  132,  132,  132,  132,
+   131,  131,  131,  131,  130,  130,  130,  130,
+   129,  129,  129,  129,  128,  128,  128,  128,
+};
diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c
new file mode 100644
index 00000000..f0b4151d
--- /dev/null
+++ b/sound/oss/v_midi.c
@@ -0,0 +1,290 @@
+/*
+ * sound/oss/v_midi.c
+ *
+ * The low level driver for the Sound Blaster DS chips.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1996
+ *
+ * USS/Lite 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.
+ * ??
+ *
+ * Changes
+ *	Alan Cox		Modularisation, changed memory allocations
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *
+ * Status
+ *	Untested
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#include "v_midi.h"
+
+static vmidi_devc *v_devc[2] = { NULL, NULL};
+static int midi1,midi2;
+static void *midi_mem = NULL;
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+
+static int v_midi_open (int dev, int mode,
+	      void            (*input) (int dev, unsigned char data),
+	      void            (*output) (int dev)
+)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return -(ENXIO);
+
+	spin_lock_irqsave(&devc->lock,flags);
+	if (devc->opened)
+	{
+		spin_unlock_irqrestore(&devc->lock,flags);
+		return -(EBUSY);
+	}
+	devc->opened = 1;
+	spin_unlock_irqrestore(&devc->lock,flags);
+
+	devc->intr_active = 1;
+
+	if (mode & OPEN_READ)
+	{
+		devc->input_opened = 1;
+		devc->midi_input_intr = input;
+	}
+
+	return 0;
+}
+
+static void v_midi_close (int dev)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return;
+
+	spin_lock_irqsave(&devc->lock,flags);
+	devc->intr_active = 0;
+	devc->input_opened = 0;
+	devc->opened = 0;
+	spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int v_midi_out (int dev, unsigned char midi_byte)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	vmidi_devc *pdevc;
+
+	if (devc == NULL)
+		return -ENXIO;
+
+	pdevc = midi_devs[devc->pair_mididev]->devc;
+	if (pdevc->input_opened > 0){
+		if (MIDIbuf_avail(pdevc->my_mididev) > 500)
+			return 0;
+		pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
+	}
+	return 1;
+}
+
+static inline int v_midi_start_read (int dev)
+{
+	return 0;
+}
+
+static int v_midi_end_read (int dev)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	if (devc == NULL)
+		return -ENXIO;
+
+	devc->intr_active = 0;
+	return 0;
+}
+
+/* why -EPERM and not -EINVAL?? */
+
+static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg)
+{
+	return -EPERM;
+}
+
+
+#define MIDI_SYNTH_NAME	"Loopback MIDI"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+
+#include "midi_synth.h"
+
+static struct midi_operations v_midi_operations =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
+	.converter	= &std_midi_synth,
+	.in_info	= {0},
+	.open		= v_midi_open,
+	.close		= v_midi_close,
+	.ioctl		= v_midi_ioctl,
+	.outputc	= v_midi_out,
+	.start_read	= v_midi_start_read,
+	.end_read	= v_midi_end_read,
+};
+
+static struct midi_operations v_midi_operations2 =
+{
+	.owner		= THIS_MODULE,
+	.info		= {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
+	.converter	= &std_midi_synth,
+	.in_info	= {0},
+	.open		= v_midi_open,
+	.close		= v_midi_close,
+	.ioctl		= v_midi_ioctl,
+	.outputc	= v_midi_out,
+	.start_read	= v_midi_start_read,
+	.end_read	= v_midi_end_read,
+};
+
+/*
+ *	We kmalloc just one of these - it makes life simpler and the code
+ *	cleaner and the memory handling far more efficient
+ */
+ 
+struct vmidi_memory
+{
+	/* Must be first */
+	struct midi_operations m_ops[2];
+	struct synth_operations s_ops[2];
+	struct vmidi_devc v_ops[2];
+};
+
+static void __init attach_v_midi (struct address_info *hw_config)
+{
+	struct vmidi_memory *m;
+	/* printk("Attaching v_midi device.....\n"); */
+
+	midi1 = sound_alloc_mididev();
+	if (midi1 == -1)
+	{
+		printk(KERN_ERR "v_midi: Too many midi devices detected\n");
+		return;
+	}
+	
+	m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
+	if (m == NULL)
+	{
+		printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
+		sound_unload_mididev(midi1);
+		return;
+	}
+	
+	midi_mem = m;
+	
+	midi_devs[midi1] = &m->m_ops[0];
+	
+
+	midi2 = sound_alloc_mididev();
+	if (midi2 == -1)
+	{
+		printk (KERN_ERR "v_midi: Too many midi devices detected\n");
+		kfree(m);
+		sound_unload_mididev(midi1);
+		return;
+	}
+
+	midi_devs[midi2] = &m->m_ops[1];
+
+	/* printk("VMIDI1: %d   VMIDI2: %d\n",midi1,midi2); */
+
+	/* for MIDI-1 */
+	v_devc[0] = &m->v_ops[0];
+	memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
+		sizeof (struct midi_operations));
+
+	v_devc[0]->my_mididev = midi1;
+	v_devc[0]->pair_mididev = midi2;
+	v_devc[0]->opened = v_devc[0]->input_opened = 0;
+	v_devc[0]->intr_active = 0;
+	v_devc[0]->midi_input_intr = NULL;
+	spin_lock_init(&v_devc[0]->lock);
+
+	midi_devs[midi1]->devc = v_devc[0];
+
+	midi_devs[midi1]->converter = &m->s_ops[0];
+	std_midi_synth.midi_dev = midi1;
+	memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth,
+		sizeof (struct synth_operations));
+	midi_devs[midi1]->converter->id = "V_MIDI 1";
+
+	/* for MIDI-2 */
+	v_devc[1] = &m->v_ops[1];
+
+	memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2,
+		sizeof (struct midi_operations));
+
+	v_devc[1]->my_mididev = midi2;
+	v_devc[1]->pair_mididev = midi1;
+	v_devc[1]->opened = v_devc[1]->input_opened = 0;
+	v_devc[1]->intr_active = 0;
+	v_devc[1]->midi_input_intr = NULL;
+	spin_lock_init(&v_devc[1]->lock);
+
+	midi_devs[midi2]->devc = v_devc[1];
+	midi_devs[midi2]->converter = &m->s_ops[1];
+
+	std_midi_synth.midi_dev = midi2;
+	memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth,
+		sizeof (struct synth_operations));
+	midi_devs[midi2]->converter->id = "V_MIDI 2";
+
+	sequencer_init();
+	/* printk("Attached v_midi device\n"); */
+}
+
+static inline int __init probe_v_midi(struct address_info *hw_config)
+{
+	return(1);	/* always OK */
+}
+
+
+static void __exit unload_v_midi(struct address_info *hw_config)
+{
+	sound_unload_mididev(midi1);
+	sound_unload_mididev(midi2);
+	kfree(midi_mem);
+}
+
+static struct address_info cfg; /* dummy */
+
+static int __init init_vmidi(void)
+{
+	printk("MIDI Loopback device driver\n");
+	if (!probe_v_midi(&cfg))
+		return -ENODEV;
+	attach_v_midi(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_vmidi(void)
+{
+	unload_v_midi(&cfg);
+}
+
+module_init(init_vmidi);
+module_exit(cleanup_vmidi);
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/v_midi.h b/sound/oss/v_midi.h
new file mode 100644
index 00000000..08e2185e
--- /dev/null
+++ b/sound/oss/v_midi.h
@@ -0,0 +1,14 @@
+typedef struct vmidi_devc {
+	   int dev;
+
+	/* State variables */
+	   int opened;
+	   spinlock_t lock;
+
+	/* MIDI fields */
+	   int my_mididev;
+	   int pair_mididev;
+	   int input_opened;
+	   int intr_active;
+	   void (*midi_input_intr) (int dev, unsigned char data);
+	} vmidi_devc;
diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c
new file mode 100644
index 00000000..92ca5bee
--- /dev/null
+++ b/sound/oss/vidc.c
@@ -0,0 +1,557 @@
+/*
+ *  linux/drivers/sound/vidc.c
+ *
+ *  Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  VIDC20 audio driver.
+ *
+ * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
+ * engine.  The DMA transfers fixed-format (16-bit little-endian linear)
+ * samples to the VIDC20, which then transfers this data serially to the
+ * DACs.  The samplerate is controlled by the VIDC.
+ *
+ * We currently support a mixer device, but it is currently non-functional.
+ */
+
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware/iomd.h>
+#include <asm/irq.h>
+
+#include "sound_config.h"
+#include "vidc.h"
+
+#ifndef _SIOC_TYPE
+#define _SIOC_TYPE(x)	_IOC_TYPE(x)
+#endif
+#ifndef _SIOC_NR
+#define _SIOC_NR(x)	_IOC_NR(x)
+#endif
+
+#define VIDC_SOUND_CLOCK	(250000)
+#define VIDC_SOUND_CLOCK_EXT	(176400)
+
+/*
+ * When using SERIAL SOUND mode (external DAC), the number of physical
+ * channels is fixed at 2.
+ */
+static int		vidc_busy;
+static int		vidc_adev;
+static int		vidc_audio_rate;
+static char		vidc_audio_format;
+static char		vidc_audio_channels;
+
+static unsigned char	vidc_level_l[SOUND_MIXER_NRDEVICES] = {
+	85,		/* master	*/
+	50,		/* bass		*/
+	50,		/* treble	*/
+	0,		/* synth	*/
+	75,		/* pcm		*/
+	0,		/* speaker	*/
+	100,		/* ext line	*/
+	0,		/* mic		*/
+	100,		/* CD		*/
+	0,
+};
+
+static unsigned char	vidc_level_r[SOUND_MIXER_NRDEVICES] = {
+	85,		/* master	*/
+	50,		/* bass		*/
+	50,		/* treble	*/
+	0,		/* synth	*/
+	75,		/* pcm		*/
+	0,		/* speaker	*/
+	100,		/* ext line	*/
+	0,		/* mic		*/
+	100,		/* CD		*/
+	0,
+};
+
+static unsigned int	vidc_audio_volume_l;	/* left PCM vol, 0 - 65536 */
+static unsigned int	vidc_audio_volume_r;	/* right PCM vol, 0 - 65536 */
+
+extern void	vidc_update_filler(int bits, int channels);
+extern int	softoss_dev;
+
+static void
+vidc_mixer_set(int mdev, unsigned int level)
+{
+	unsigned int lev_l = level & 0x007f;
+	unsigned int lev_r = (level & 0x7f00) >> 8;
+	unsigned int mlev_l, mlev_r;
+
+	if (lev_l > 100)
+		lev_l = 100;
+	if (lev_r > 100)
+		lev_r = 100;
+
+#define SCALE(lev,master)	((lev) * (master) * 65536 / 10000)
+
+	mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
+	mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
+
+	switch (mdev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_PCM:
+		vidc_level_l[mdev] = lev_l;
+		vidc_level_r[mdev] = lev_r;
+
+		vidc_audio_volume_l = SCALE(lev_l, mlev_l);
+		vidc_audio_volume_r = SCALE(lev_r, mlev_r);
+/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/
+		break;
+	}
+#undef SCALE
+}
+
+static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	unsigned int val;
+	unsigned int mdev;
+
+	if (_SIOC_TYPE(cmd) != 'M')
+		return -EINVAL;
+
+	mdev = _SIOC_NR(cmd);
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		if (get_user(val, (unsigned int __user *)arg))
+			return -EFAULT;
+
+		if (mdev < SOUND_MIXER_NRDEVICES)
+			vidc_mixer_set(mdev, val);
+		else
+			return -EINVAL;
+	}
+
+	/*
+	 * Return parameters
+	 */
+	switch (mdev) {
+	case SOUND_MIXER_RECSRC:
+		val = 0;
+		break;
+
+	case SOUND_MIXER_DEVMASK:
+		val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
+		break;
+
+	case SOUND_MIXER_STEREODEVS:
+		val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
+		break;
+
+	case SOUND_MIXER_RECMASK:
+		val = 0;
+		break;
+
+	case SOUND_MIXER_CAPS:
+		val = 0;
+		break;
+
+	default:
+		if (mdev < SOUND_MIXER_NRDEVICES)
+			val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
+		else
+			return -EINVAL;
+	}
+
+	return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0;
+}
+
+static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
+{
+	switch (fmt) {
+	default:
+		fmt = AFMT_S16_LE;
+	case AFMT_U8:
+	case AFMT_S8:
+	case AFMT_S16_LE:
+		vidc_audio_format = fmt;
+		vidc_update_filler(vidc_audio_format, vidc_audio_channels);
+	case AFMT_QUERY:
+		break;
+	}
+	return vidc_audio_format;
+}
+
+#define my_abs(i) ((i)<0 ? -(i) : (i))
+
+static int vidc_audio_set_speed(int dev, int rate)
+{
+	if (rate) {
+		unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext;
+		unsigned int diff_int, diff_ext;
+		unsigned int newsize, new2size;
+
+		hwctrl = 0x00000003;
+
+		/* Using internal clock */
+		hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
+		if (hwrate < 3)
+			hwrate = 3;
+		if (hwrate > 255)
+			hwrate = 255;
+
+		/* Using exernal clock */
+		hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1;
+		if (hwrate_ext < 3)
+			hwrate_ext = 3;
+		if (hwrate_ext > 255)
+			hwrate_ext = 255;
+
+		rate_int = VIDC_SOUND_CLOCK / hwrate;
+		rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext;
+
+		/* Chose between external and internal clock */
+		diff_int = my_abs(rate_ext-rate);
+		diff_ext = my_abs(rate_int-rate);
+		if (diff_ext < diff_int) {
+			/*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/
+			hwrate=hwrate_ext;
+			hwctrl=0x00000002;
+			/* Allow roughly 0.4% tolerance */
+			if (diff_ext > (rate/256))
+				rate=rate_ext;
+		} else {
+			/*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/
+			hwctrl=0x00000003;
+			/* Allow roughly 0.4% tolerance */
+			if (diff_int > (rate/256))
+				rate=rate_int;
+		}
+
+		vidc_writel(0xb0000000 | (hwrate - 2));
+		vidc_writel(0xb1000000 | hwctrl);
+
+		newsize = (10000 / hwrate) & ~3;
+		if (newsize < 208)
+			newsize = 208;
+		if (newsize > 4096)
+			newsize = 4096;
+		for (new2size = 128; new2size < newsize; new2size <<= 1);
+		if (new2size - newsize > newsize - (new2size >> 1))
+			new2size >>= 1;
+		if (new2size > 4096) {
+			printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
+				newsize, new2size);
+			new2size = 4096;
+		}
+		/*printk("VIDC: dma size %d\n", new2size);*/
+		dma_bufsize = new2size;
+		vidc_audio_rate = rate;
+	}
+	return vidc_audio_rate;
+}
+
+static short vidc_audio_set_channels(int dev, short channels)
+{
+	switch (channels) {
+	default:
+		channels = 2;
+	case 1:
+	case 2:
+		vidc_audio_channels = channels;
+		vidc_update_filler(vidc_audio_format, vidc_audio_channels);
+	case 0:
+		break;
+	}
+	return vidc_audio_channels;
+}
+
+/*
+ * Open the device
+ */
+static int vidc_audio_open(int dev, int mode)
+{
+	/* This audio device does not have recording capability */
+	if (mode == OPEN_READ)
+		return -EPERM;
+
+	if (vidc_busy)
+		return -EBUSY;
+
+	vidc_busy = 1;
+	return 0;
+}
+
+/*
+ * Close the device
+ */
+static void vidc_audio_close(int dev)
+{
+	vidc_busy = 0;
+}
+
+/*
+ * Output a block via DMA to sound device.
+ *
+ * We just set the DMA start and count; the DMA interrupt routine
+ * will take care of formatting the samples (via the appropriate
+ * vidc_filler routine), and flag via vidc_audio_dma_interrupt when
+ * more data is required.
+ */
+static void
+vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
+{
+	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
+	dma_count = total_count;
+	local_irq_restore(flags);
+}
+
+static void
+vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
+{
+}
+
+static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	return -EINVAL;
+}
+
+static irqreturn_t vidc_audio_dma_interrupt(void)
+{
+	DMAbuf_outputintr(vidc_adev, 1);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Prepare for outputting samples.
+ *
+ * Each buffer that will be passed will be `bsize' bytes long,
+ * with a total of `bcount' buffers.
+ */
+static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	dma_interrupt = NULL;
+	adev->dmap_out->flags |= DMA_NODMA;
+
+	return 0;
+}
+
+/*
+ * Stop our current operation.
+ */
+static void vidc_audio_reset(int dev)
+{
+	dma_interrupt = NULL;
+}
+
+static int vidc_audio_local_qlen(int dev)
+{
+	return /*dma_count !=*/ 0;
+}
+
+static void vidc_audio_trigger(int dev, int enable_bits)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (enable_bits & PCM_ENABLE_OUTPUT) {
+		if (!(adev->dmap_out->flags & DMA_ACTIVE)) {
+			unsigned long flags;
+
+			local_irq_save(flags);
+
+			/* prevent recusion */
+			adev->dmap_out->flags |= DMA_ACTIVE;
+
+			dma_interrupt = vidc_audio_dma_interrupt;
+			vidc_sound_dma_irq(0, NULL);
+			iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR);
+
+			local_irq_restore(flags);
+		}
+	}
+}
+
+static struct audio_driver vidc_audio_driver =
+{
+	.owner			= THIS_MODULE,
+	.open			= vidc_audio_open,
+	.close			= vidc_audio_close,
+	.output_block		= vidc_audio_output_block,
+	.start_input		= vidc_audio_start_input,
+	.prepare_for_input	= vidc_audio_prepare_for_input,
+	.prepare_for_output	= vidc_audio_prepare_for_output,
+	.halt_io		= vidc_audio_reset,
+	.local_qlen		= vidc_audio_local_qlen,
+	.trigger		= vidc_audio_trigger,
+	.set_speed		= vidc_audio_set_speed,
+	.set_bits		= vidc_audio_set_format,
+	.set_channels		= vidc_audio_set_channels
+};
+
+static struct mixer_operations vidc_mixer_operations = {
+	.owner		= THIS_MODULE,
+	.id		= "VIDC",
+	.name		= "VIDCsound",
+	.ioctl		= vidc_mixer_ioctl
+};
+
+void vidc_update_filler(int format, int channels)
+{
+#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
+
+	switch (TYPE(format, channels)) {
+	default:
+	case TYPE(AFMT_U8, 1):
+		vidc_filler = vidc_fill_1x8_u;
+		break;
+
+	case TYPE(AFMT_U8, 2):
+		vidc_filler = vidc_fill_2x8_u;
+		break;
+
+	case TYPE(AFMT_S8, 1):
+		vidc_filler = vidc_fill_1x8_s;
+		break;
+
+	case TYPE(AFMT_S8, 2):
+		vidc_filler = vidc_fill_2x8_s;
+		break;
+
+	case TYPE(AFMT_S16_LE, 1):
+		vidc_filler = vidc_fill_1x16_s;
+		break;
+
+	case TYPE(AFMT_S16_LE, 2):
+		vidc_filler = vidc_fill_2x16_s;
+		break;
+	}
+}
+
+static void __init attach_vidc(struct address_info *hw_config)
+{
+	char name[32];
+	int i, adev;
+
+	sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
+	conf_printf(name, hw_config);
+	memset(dma_buf, 0, sizeof(dma_buf));
+
+	adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
+			&vidc_audio_driver, sizeof(vidc_audio_driver),
+			DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
+			NULL, hw_config->dma, hw_config->dma2);
+
+	if (adev < 0)
+		goto audio_failed;
+
+	/*
+	 * 1024 bytes => 64 buffers
+	 */
+	audio_devs[adev]->min_fragment = 10;
+	audio_devs[adev]->mixer_dev = num_mixers;
+
+	audio_devs[adev]->mixer_dev =
+		sound_install_mixer(MIXER_DRIVER_VERSION,
+				name, &vidc_mixer_operations,
+				sizeof(vidc_mixer_operations), NULL);
+
+	if (audio_devs[adev]->mixer_dev < 0)
+		goto mixer_failed;
+
+	for (i = 0; i < 2; i++) {
+		dma_buf[i] = get_zeroed_page(GFP_KERNEL);
+		if (!dma_buf[i]) {
+			printk(KERN_ERR "%s: can't allocate required buffers\n",
+				name);
+			goto mem_failed;
+		}
+		dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]);
+	}
+
+	if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
+		printk(KERN_ERR "%s: DMA %d is in  use\n", name, hw_config->dma);
+		goto dma_failed;
+	}
+
+	if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
+			hw_config->name, &dma_start)) {
+		printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
+		goto irq_failed;
+	}
+	vidc_adev = adev;
+	vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
+
+	return;
+
+irq_failed:
+	sound_free_dma(hw_config->dma);
+dma_failed:
+mem_failed:
+	for (i = 0; i < 2; i++)
+		free_page(dma_buf[i]);
+	sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
+mixer_failed:
+	sound_unload_audiodev(adev);
+audio_failed:
+	return;
+}
+
+static int __init probe_vidc(struct address_info *hw_config)
+{
+	hw_config->irq		= IRQ_DMAS0;
+	hw_config->dma		= DMA_VIRTUAL_SOUND;
+	hw_config->dma2		= -1;
+	hw_config->card_subtype	= 16;
+	hw_config->name		= "VIDC20";
+	return 1;
+}
+
+static void __exit unload_vidc(struct address_info *hw_config)
+{
+	int i, adev = vidc_adev;
+
+	vidc_adev = -1;
+
+	free_irq(hw_config->irq, &dma_start);
+	sound_free_dma(hw_config->dma);
+
+	if (adev >= 0) {
+		sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
+		sound_unload_audiodev(adev);
+		for (i = 0; i < 2; i++)
+			free_page(dma_buf[i]);
+	}
+}
+
+static struct address_info cfg;
+
+static int __init init_vidc(void)
+{
+	if (probe_vidc(&cfg) == 0)
+		return -ENODEV;
+
+	attach_vidc(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_vidc(void)
+{
+	unload_vidc(&cfg);
+}
+
+module_init(init_vidc);
+module_exit(cleanup_vidc);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("VIDC20 audio driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/vidc.h b/sound/oss/vidc.h
new file mode 100644
index 00000000..0d142475
--- /dev/null
+++ b/sound/oss/vidc.h
@@ -0,0 +1,63 @@
+/*
+ *  linux/drivers/sound/vidc.h
+ *
+ *  Copyright (C) 1997 Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  VIDC sound function prototypes
+ */
+
+/* vidc_fill.S */
+
+/*
+ * Filler routines for different channels and sample sizes
+ */
+
+extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend,
+				      unsigned long obuf, int mask);
+extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend,
+				      unsigned long obuf, int mask);
+
+/*
+ * DMA Interrupt handler
+ */
+
+extern irqreturn_t vidc_sound_dma_irq(int irqnr, void *ref);
+
+/*
+ * Filler routine pointer
+ */
+
+extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+
+/*
+ * Virtual DMA buffer exhausted
+ */
+
+extern irqreturn_t (*dma_interrupt) (void);
+
+/*
+ * Virtual DMA buffer addresses
+ */
+
+extern unsigned long dma_start, dma_count, dma_bufsize;
+extern unsigned long dma_buf[2], dma_pbuf[2];
+
+/* vidc_synth.c */
+
+extern void     vidc_synth_init(struct address_info *hw_config);
+extern void	vidc_synth_exit(struct address_info *hw_config);
+extern int      vidc_synth_get_volume(void);
+extern int      vidc_synth_set_volume(int vol);
diff --git a/sound/oss/vidc_fill.S b/sound/oss/vidc_fill.S
new file mode 100644
index 00000000..bed34921
--- /dev/null
+++ b/sound/oss/vidc_fill.S
@@ -0,0 +1,218 @@
+/*
+ *  linux/drivers/sound/vidc_fill.S
+ *
+ *  Copyright (C) 1997 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Filler routines for DMA buffers
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <mach/hardware.h>
+#include <asm/hardware/iomd.h>
+
+		.text
+
+ENTRY(vidc_fill_1x8_u)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	vidc_clear
+		ldrb	r4, [r0], #1
+		eor	r4, r4, #0x80
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_2x8_u)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	vidc_clear
+		ldr	r4, [r0], #2
+		and	r5, r4, ip
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r5, lsl #16
+		orr	r4, r4, r4, lsr #8
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_1x8_s)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	vidc_clear
+		ldrb	r4, [r0], #1
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_2x8_s)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	vidc_clear
+		ldr	r4, [r0], #2
+		and	r5, r4, ip
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r5, lsl #16
+		orr	r4, r4, r4, lsr #8
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_1x16_s)
+		mov	ip, #0xff00
+		orr	ip, ip, ip, lsr #8
+1:		cmp	r0, r1
+		bge	vidc_clear
+		ldr	r5, [r0], #2
+		and	r4, r5, ip
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r2], #4
+		cmp	r0, r1
+		addlt	r0, r0, #2
+		andlt	r4, r5, ip, lsl #16
+		orrlt	r4, r4, r4, lsr #16
+		strlt	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_2x16_s)
+		mov	ip, #0xff00
+		orr	ip, ip, ip, lsr #8
+1:		cmp	r0, r1
+		bge	vidc_clear
+		ldr	r4, [r0], #4
+		str	r4, [r2], #4
+		cmp	r0, r1
+		ldrlt	r4, [r0], #4
+		strlt	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_noaudio)
+		mov	r0, #0
+		mov	r1, #0
+2:		mov	r4, #0
+		mov	r5, #0
+1:		cmp	r2, r3
+		stmltia	r2!, {r0, r1, r4, r5}
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_clear)
+		mov	r0, #0
+		mov	r1, #0
+		tst	r2, #4
+		str	r0, [r2], #4
+		tst	r2, #8
+		stmia	r2!, {r0, r1}
+		b	2b
+
+/*
+ * Call filler routines with:
+ *  r0 = phys address
+ *  r1 = phys end
+ *  r2 = buffer
+ * Returns:
+ *  r0 = new buffer address
+ *  r2 = new buffer finish
+ *  r4 = corrupted
+ *  r5 = corrupted
+ *  ip = corrupted
+ */
+
+ENTRY(vidc_sound_dma_irq)
+		stmfd	sp!, {r4 - r8, lr}
+		ldr	r8, =dma_start
+		ldmia	r8, {r0, r1, r2, r3, r4, r5}
+		teq	r1, #0
+		adreq	r4, vidc_fill_noaudio
+		moveq	r7, #1 << 31
+		movne	r7, #0
+		mov	ip, #IOMD_BASE & 0xff000000
+		orr	ip, ip, #IOMD_BASE & 0x00ff0000
+		ldrb	r6, [ip, #IOMD_SD0ST]
+		tst	r6, #DMA_ST_OFL			@ Check for overrun
+		eorne	r6, r6, #DMA_ST_AB
+		tst	r6, #DMA_ST_AB
+		moveq	r2, r3				@ DMAing A, update B
+		add	r3, r2, r5			@ End of DMA buffer
+		add	r1, r1, r0			@ End of virtual DMA buffer
+		mov	lr, pc
+		mov	pc, r4				@ Call fill routine (uses r4, ip)
+		sub	r1, r1, r0			@ Remaining length
+		stmia	r8, {r0, r1}
+		mov	r0, #0
+		tst	r2, #4				@ Round buffer up to 4 words
+		strne	r0, [r2], #4
+		tst	r2, #8
+		strne	r0, [r2], #4
+		strne	r0, [r2], #4
+		sub	r2, r2, #16
+		mov	r2, r2, lsl #20
+		movs	r2, r2, lsr #20
+		orreq	r2, r2, #1 << 30		@ Set L bit
+		orr	r2, r2, r7
+		ldmdb	r8, {r3, r4, r5}
+		tst	r6, #DMA_ST_AB
+		mov	ip, #IOMD_BASE & 0xff000000
+		orr	ip, ip, #IOMD_BASE & 0x00ff0000
+		streq	r4, [ip, #IOMD_SD0CURB]
+		strne	r5, [ip, #IOMD_SD0CURA]
+		streq	r2, [ip, #IOMD_SD0ENDB]
+		strne	r2, [ip, #IOMD_SD0ENDA]
+		ldr	lr, [ip, #IOMD_SD0ST]
+		tst	lr, #DMA_ST_OFL
+		bne	1f
+		tst	r6, #DMA_ST_AB
+		strne	r4, [ip, #IOMD_SD0CURB]
+		streq	r5, [ip, #IOMD_SD0CURA]
+		strne	r2, [ip, #IOMD_SD0ENDB]
+		streq	r2, [ip, #IOMD_SD0ENDA]
+1:		teq	r7, #0
+		mov	r0, #0x10
+		strneb	r0, [ip, #IOMD_SD0CR]
+		ldmfd	sp!, {r4 - r8, lr}
+		mov	r0, #1				@ IRQ_HANDLED
+		teq	r1, #0				@ If we have no more
+		movne	pc, lr
+		teq	r3, #0
+		movne	pc, r3				@ Call interrupt routine
+		mov	pc, lr
+
+		.data
+		.globl	dma_interrupt
+dma_interrupt:
+		.long	0				@ r3
+		.globl	dma_pbuf
+dma_pbuf:
+		.long	0				@ r4
+		.long	0				@ r5
+		.globl	dma_start
+dma_start:
+		.long	0				@ r0
+		.globl	dma_count
+dma_count:
+		.long	0				@ r1
+		.globl	dma_buf
+dma_buf:
+		.long	0				@ r2
+		.long	0				@ r3
+		.globl	vidc_filler
+vidc_filler:
+		.long	vidc_fill_noaudio		@ r4
+		.globl	dma_bufsize
+dma_bufsize:
+		.long	0x1000				@ r5
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
new file mode 100644
index 00000000..643f1113
--- /dev/null
+++ b/sound/oss/vwsnd.c
@@ -0,0 +1,3498 @@
+/*
+ * Sound driver for Silicon Graphics 320 and 540 Visual Workstations'
+ * onboard audio.  See notes in Documentation/sound/oss/vwsnd .
+ *
+ * Copyright 1999 Silicon Graphics, Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#undef VWSND_DEBUG			/* define for debugging */
+
+/*
+ * XXX to do -
+ *
+ *	External sync.
+ *	Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational.
+ *	Bug - if select() called before read(), pcm_setup() not called.
+ *	Bug - output doesn't stop soon enough if process killed.
+ */
+
+/*
+ * Things to test -
+ *
+ *	Will readv/writev work?  Write a test.
+ *
+ *	insmod/rmmod 100 million times.
+ *
+ *	Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT
+ *	rate).
+ *
+ *	Concurrent threads banging on mixer simultaneously, both UP
+ *	and SMP kernels.  Especially, watch for thread A changing
+ *	OUTSRC while thread B changes gain -- both write to the same
+ *	ad1843 register.
+ *
+ *	What happens if a client opens /dev/audio then forks?
+ *	Do two procs have /dev/audio open?  Test.
+ *
+ *	Pump audio through the CD, MIC and line inputs and verify that
+ *	they mix/mute into the output.
+ *
+ *	Apps:
+ *		amp
+ *		mpg123
+ *		x11amp
+ *		mxv
+ *		kmedia
+ *		esound
+ *		need more input apps
+ *
+ *	Run tests while bombarding with signals.  setitimer(2) will do it...  */
+
+/*
+ * This driver is organized in nine sections.
+ * The nine sections are:
+ *
+ *	debug stuff
+ * 	low level lithium access
+ *	high level lithium access
+ *	AD1843 access
+ *	PCM I/O
+ *	audio driver
+ *	mixer driver
+ *	probe/attach/unload
+ *	initialization and loadable kernel module interface
+ *
+ * That is roughly the order of increasing abstraction, so forward
+ * dependencies are minimal.
+ */
+
+/*
+ * Locking Notes
+ *
+ *	INC_USE_COUNT and DEC_USE_COUNT keep track of the number of
+ *	open descriptors to this driver. They store it in vwsnd_use_count.
+ * 	The global device list, vwsnd_dev_list,	is immutable when the IN_USE
+ *	is true.
+ *
+ *	devc->open_lock is a semaphore that is used to enforce the
+ *	single reader/single writer rule for /dev/audio.  The rule is
+ *	that each device may have at most one reader and one writer.
+ *	Open will block until the previous client has closed the
+ *	device, unless O_NONBLOCK is specified.
+ *
+ *	The semaphore devc->io_mutex serializes PCM I/O syscalls.  This
+ *	is unnecessary in Linux 2.2, because the kernel lock
+ *	serializes read, write, and ioctl globally, but it's there,
+ *	ready for the brave, new post-kernel-lock world.
+ *
+ *	Locking between interrupt and baselevel is handled by the
+ *	"lock" spinlock in vwsnd_port (one lock each for read and
+ *	write).  Each half holds the lock just long enough to see what
+ *	area it owns and update its pointers.  See pcm_output() and
+ *	pcm_input() for most of the gory stuff.
+ *
+ *	devc->mix_mutex serializes all mixer ioctls.  This is also
+ *	redundant because of the kernel lock.
+ *
+ *	The lowest level lock is lith->lithium_lock.  It is a
+ *	spinlock which is held during the two-register tango of
+ *	reading/writing an AD1843 register.  See
+ *	li_{read,write}_ad1843_reg().
+ */
+
+/*
+ * Sample Format Notes
+ *
+ *	Lithium's DMA engine has two formats: 16-bit 2's complement
+ *	and 8-bit unsigned .  16-bit transfers the data unmodified, 2
+ *	bytes per sample.  8-bit unsigned transfers 1 byte per sample
+ *	and XORs each byte with 0x80.  Lithium can input or output
+ *	either mono or stereo in either format.
+ *
+ *	The AD1843 has four formats: 16-bit 2's complement, 8-bit
+ *	unsigned, 8-bit mu-Law and 8-bit A-Law.
+ *
+ *	This driver supports five formats: AFMT_S8, AFMT_U8,
+ *	AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE.
+ *
+ *	For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and
+ *	rely on Lithium's XOR to translate between U8 and S8.
+ *
+ *	For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR
+ *	the 0x80 bit in software to compensate for Lithium's XOR.
+ *	This happens in pcm_copy_{in,out}().
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added some __init/__exit
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <asm/visws/cobalt.h>
+
+#include "sound_config.h"
+
+/*****************************************************************************/
+/* debug stuff */
+
+#ifdef VWSND_DEBUG
+
+static DEFINE_MUTEX(vwsnd_mutex);
+static int shut_up = 1;
+
+/*
+ * dbgassert - called when an assertion fails.
+ */
+
+static void dbgassert(const char *fcn, int line, const char *expr)
+{
+	if (in_interrupt())
+		panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n",
+		      __FILE__, fcn, line, expr);
+	else {
+		int x;
+		printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n",
+		       __FILE__, fcn, line, expr);
+		x = * (volatile int *) 0; /* force proc to exit */
+	}
+}
+
+/*
+ * Bunch of useful debug macros:
+ *
+ *	ASSERT	- print unless e nonzero (panic if in interrupt)
+ *	DBGDO	- include arbitrary code if debugging
+ *	DBGX	- debug print raw (w/o function name)
+ *	DBGP	- debug print w/ function name
+ *	DBGE	- debug print function entry
+ *	DBGC	- debug print function call
+ *	DBGR	- debug print function return
+ *	DBGXV	- debug print raw when verbose
+ *	DBGPV	- debug print when verbose
+ *	DBGEV	- debug print function entry when verbose
+ *	DBGRV	- debug print function return when verbose
+ */
+
+#define ASSERT(e)      ((e) ? (void) 0 : dbgassert(__func__, __LINE__, #e))
+#define DBGDO(x)            x
+#define DBGX(fmt, args...)  (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args))
+#define DBGP(fmt, args...)  (DBGX("%s: " fmt, __func__ , ##args))
+#define DBGE(fmt, args...)  (DBGX("%s" fmt, __func__ , ##args))
+#define DBGC(rtn)           (DBGP("calling %s\n", rtn))
+#define DBGR()              (DBGP("returning\n"))
+#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args))
+#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args))
+#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args))
+#define DBGCV(rtn)          (shut_up ? 0 : DBGC(rtn))
+#define DBGRV()             (shut_up ? 0 : DBGR())
+
+#else /* !VWSND_DEBUG */
+
+#define ASSERT(e)           ((void) 0)
+#define DBGDO(x)            /* don't */
+#define DBGX(fmt, args...)  ((void) 0)
+#define DBGP(fmt, args...)  ((void) 0)
+#define DBGE(fmt, args...)  ((void) 0)
+#define DBGC(rtn)           ((void) 0)
+#define DBGR()              ((void) 0)
+#define DBGPV(fmt, args...) ((void) 0)
+#define DBGXV(fmt, args...) ((void) 0)
+#define DBGEV(fmt, args...) ((void) 0)
+#define DBGCV(rtn)          ((void) 0)
+#define DBGRV()             ((void) 0)
+
+#endif /* !VWSND_DEBUG */
+
+/*****************************************************************************/
+/* low level lithium access */
+
+/*
+ * We need to talk to Lithium registers on three pages.  Here are
+ * the pages' offsets from the base address (0xFF001000).
+ */
+
+enum {
+	LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */
+	LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */
+	LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */
+};
+
+/* low-level lithium data */
+
+typedef struct lithium {
+	void *		page0;		/* virtual addresses */
+	void *		page1;
+	void *		page2;
+	spinlock_t	lock;		/* protects codec and UST/MSC access */
+} lithium_t;
+
+/*
+ * li_destroy destroys the lithium_t structure and vm mappings.
+ */
+
+static void li_destroy(lithium_t *lith)
+{
+	if (lith->page0) {
+		iounmap(lith->page0);
+		lith->page0 = NULL;
+	}
+	if (lith->page1) {
+		iounmap(lith->page1);
+		lith->page1 = NULL;
+	}
+	if (lith->page2) {
+		iounmap(lith->page2);
+		lith->page2 = NULL;
+	}
+}
+
+/*
+ * li_create initializes the lithium_t structure and sets up vm mappings
+ * to access the registers.
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int __init li_create(lithium_t *lith, unsigned long baseaddr)
+{
+	spin_lock_init(&lith->lock);
+	lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE);
+	lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE);
+	lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE);
+	if (!lith->page0 || !lith->page1 || !lith->page2) {
+		li_destroy(lith);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ * basic register accessors - read/write long/byte
+ */
+
+static __inline__ unsigned long li_readl(lithium_t *lith, int off)
+{
+	return * (volatile unsigned long *) (lith->page0 + off);
+}
+
+static __inline__ unsigned char li_readb(lithium_t *lith, int off)
+{
+	return * (volatile unsigned char *) (lith->page0 + off);
+}
+
+static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val)
+{
+	* (volatile unsigned long *) (lith->page0 + off) = val;
+}
+
+static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val)
+{
+	* (volatile unsigned char *) (lith->page0 + off) = val;
+}
+
+/*****************************************************************************/
+/* High Level Lithium Access */
+
+/*
+ * Lithium DMA Notes
+ *
+ * Lithium has two dedicated DMA channels for audio.  They are known
+ * as comm1 and comm2 (communication areas 1 and 2).  Comm1 is for
+ * input, and comm2 is for output.  Each is controlled by three
+ * registers: BASE (base address), CFG (config) and CCTL
+ * (config/control).
+ *
+ * Each DMA channel points to a physically contiguous ring buffer in
+ * main memory of up to 8 Kbytes.  (This driver always uses 8 Kb.)
+ * There are three pointers into the ring buffer: read, write, and
+ * trigger.  The pointers are 8 bits each.  Each pointer points to
+ * 32-byte "chunks" of data.  The DMA engine moves 32 bytes at a time,
+ * so there is no finer-granularity control.
+ *
+ * In comm1, the hardware updates the write ptr, and software updates
+ * the read ptr.  In comm2, it's the opposite: hardware updates the
+ * read ptr, and software updates the write ptr.  I designate the
+ * hardware-updated ptr as the hwptr, and the software-updated ptr as
+ * the swptr.
+ *
+ * The trigger ptr and trigger mask are used to trigger interrupts.
+ * From the Lithium spec, section 5.6.8, revision of 12/15/1998:
+ *
+ *	Trigger Mask Value
+ *
+ *	A three bit wide field that represents a power of two mask
+ *	that is used whenever the trigger pointer is compared to its
+ *	respective read or write pointer.  A value of zero here
+ *	implies a mask of 0xFF and a value of seven implies a mask
+ *	0x01.  This value can be used to sub-divide the ring buffer
+ *	into pie sections so that interrupts monitor the progress of
+ *	hardware from section to section.
+ *
+ * My interpretation of that is, whenever the hw ptr is updated, it is
+ * compared with the trigger ptr, and the result is masked by the
+ * trigger mask.  (Actually, by the complement of the trigger mask.)
+ * If the result is zero, an interrupt is triggered.  I.e., interrupt
+ * if ((hwptr & ~mask) == (trptr & ~mask)).  The mask is formed from
+ * the trigger register value as mask = (1 << (8 - tmreg)) - 1.
+ *
+ * In yet different words, setting tmreg to 0 causes an interrupt after
+ * every 256 DMA chunks (8192 bytes) or once per traversal of the
+ * ring buffer.  Setting it to 7 caues an interrupt every 2 DMA chunks
+ * (64 bytes) or 128 times per traversal of the ring buffer.
+ */
+
+/* Lithium register offsets and bit definitions */
+
+#define LI_HOST_CONTROLLER	0x000
+# define LI_HC_RESET		 0x00008000
+# define LI_HC_LINK_ENABLE	 0x00004000
+# define LI_HC_LINK_FAILURE	 0x00000004
+# define LI_HC_LINK_CODEC	 0x00000002
+# define LI_HC_LINK_READY	 0x00000001
+
+#define LI_INTR_STATUS		0x010
+#define LI_INTR_MASK		0x014
+# define LI_INTR_LINK_ERR	 0x00008000
+# define LI_INTR_COMM2_TRIG	 0x00000008
+# define LI_INTR_COMM2_UNDERFLOW 0x00000004
+# define LI_INTR_COMM1_TRIG	 0x00000002
+# define LI_INTR_COMM1_OVERFLOW  0x00000001
+
+#define LI_CODEC_COMMAND	0x018
+# define LI_CC_BUSY		 0x00008000
+# define LI_CC_DIR		 0x00000080
+#  define LI_CC_DIR_RD		  LI_CC_DIR
+#  define LI_CC_DIR_WR		(!LI_CC_DIR)
+# define LI_CC_ADDR_MASK	 0x0000007F
+
+#define LI_CODEC_DATA		0x01C
+
+#define LI_COMM1_BASE		0x100
+#define LI_COMM1_CTL		0x104
+# define LI_CCTL_RESET		 0x80000000
+# define LI_CCTL_SIZE		 0x70000000
+# define LI_CCTL_DMA_ENABLE	 0x08000000
+# define LI_CCTL_TMASK		 0x07000000 /* trigger mask */
+# define LI_CCTL_TPTR		 0x00FF0000 /* trigger pointer */
+# define LI_CCTL_RPTR		 0x0000FF00
+# define LI_CCTL_WPTR		 0x000000FF
+#define LI_COMM1_CFG		0x108
+# define LI_CCFG_LOCK		 0x00008000
+# define LI_CCFG_SLOT		 0x00000070
+# define LI_CCFG_DIRECTION	 0x00000008
+#  define LI_CCFG_DIR_IN	(!LI_CCFG_DIRECTION)
+#  define LI_CCFG_DIR_OUT	  LI_CCFG_DIRECTION
+# define LI_CCFG_MODE		 0x00000004
+#  define LI_CCFG_MODE_MONO	(!LI_CCFG_MODE)
+#  define LI_CCFG_MODE_STEREO	  LI_CCFG_MODE
+# define LI_CCFG_FORMAT		 0x00000003
+#  define LI_CCFG_FMT_8BIT	  0x00000000
+#  define LI_CCFG_FMT_16BIT	  0x00000001
+#define LI_COMM2_BASE		0x10C
+#define LI_COMM2_CTL		0x110
+ /* bit definitions are the same as LI_COMM1_CTL */
+#define LI_COMM2_CFG		0x114
+ /* bit definitions are the same as LI_COMM1_CFG */
+
+#define LI_UST_LOW		0x200	/* 64-bit Unadjusted System Time is */
+#define LI_UST_HIGH		0x204	/* microseconds since boot */
+
+#define LI_AUDIO1_UST		0x300	/* UST-MSC pairs */
+#define LI_AUDIO1_MSC		0x304	/* MSC (Media Stream Counter) */
+#define LI_AUDIO2_UST		0x308	/* counts samples actually */
+#define LI_AUDIO2_MSC		0x30C	/* processed as of time UST */
+
+/* 
+ * Lithium's DMA engine operates on chunks of 32 bytes.  We call that
+ * a DMACHUNK.
+ */
+
+#define DMACHUNK_SHIFT 5
+#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT)
+#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT)
+#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT)
+
+/*
+ * Two convenient macros to shift bitfields into/out of position.
+ *
+ * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)).
+ * As long as mask is constant, we trust the compiler will change the
+ * multipy and divide into shifts.
+ */
+
+#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask))
+#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask)))
+
+/*
+ * dma_chan_desc is invariant information about a Lithium
+ * DMA channel.  There are two instances, li_comm1 and li_comm2.
+ *
+ * Note that the CCTL register fields are write ptr and read ptr, but what
+ * we care about are which pointer is updated by software and which by
+ * hardware.
+ */
+
+typedef struct dma_chan_desc {
+	int basereg;
+	int cfgreg;
+	int ctlreg;
+	int hwptrreg;
+	int swptrreg;
+	int ustreg;
+	int mscreg;
+	unsigned long swptrmask;
+	int ad1843_slot;
+	int direction;			/* LI_CCTL_DIR_IN/OUT */
+} dma_chan_desc_t;
+
+static const dma_chan_desc_t li_comm1 = {
+	LI_COMM1_BASE,			/* base register offset */
+	LI_COMM1_CFG,			/* config register offset */
+	LI_COMM1_CTL,			/* control register offset */
+	LI_COMM1_CTL + 0,		/* hw ptr reg offset (write ptr) */
+	LI_COMM1_CTL + 1,		/* sw ptr reg offset (read ptr) */
+	LI_AUDIO1_UST,			/* ust reg offset */
+	LI_AUDIO1_MSC,			/* msc reg offset */
+	LI_CCTL_RPTR,			/* sw ptr bitmask in ctlval */
+	2,				/* ad1843 serial slot */
+	LI_CCFG_DIR_IN			/* direction */
+};
+
+static const dma_chan_desc_t li_comm2 = {
+	LI_COMM2_BASE,			/* base register offset */
+	LI_COMM2_CFG,			/* config register offset */
+	LI_COMM2_CTL,			/* control register offset */
+	LI_COMM2_CTL + 1,		/* hw ptr reg offset (read ptr) */
+	LI_COMM2_CTL + 0,		/* sw ptr reg offset (writr ptr) */
+	LI_AUDIO2_UST,			/* ust reg offset */
+	LI_AUDIO2_MSC,			/* msc reg offset */
+	LI_CCTL_WPTR,			/* sw ptr bitmask in ctlval */
+	2,				/* ad1843 serial slot */
+	LI_CCFG_DIR_OUT			/* direction */
+};
+
+/*
+ * dma_chan is variable information about a Lithium DMA channel.
+ *
+ * The desc field points to invariant information.
+ * The lith field points to a lithium_t which is passed
+ * to li_read* and li_write* to access the registers.
+ * The *val fields shadow the lithium registers' contents.
+ */
+
+typedef struct dma_chan {
+	const dma_chan_desc_t *desc;
+	lithium_t      *lith;
+	unsigned long   baseval;
+	unsigned long	cfgval;
+	unsigned long	ctlval;
+} dma_chan_t;
+
+/*
+ * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter).
+ * UST is time in microseconds since the system booted, and MSC is a
+ * counter that increments with every audio sample.
+ */
+
+typedef struct ustmsc {
+	unsigned long long ust;
+	unsigned long msc;
+} ustmsc_t;
+
+/*
+ * li_ad1843_wait waits until lithium says the AD1843 register
+ * exchange is not busy.  Returns 0 on success, -EBUSY on timeout.
+ *
+ * Locking: must be called with lithium_lock held.
+ */
+
+static int li_ad1843_wait(lithium_t *lith)
+{
+	unsigned long later = jiffies + 2;
+	while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY)
+		if (time_after_eq(jiffies, later))
+			return -EBUSY;
+	return 0;
+}
+
+/*
+ * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
+ *
+ * Returns unsigned register value on success, -errno on failure.
+ */
+
+static int li_read_ad1843_reg(lithium_t *lith, int reg)
+{
+	int val;
+
+	ASSERT(!in_interrupt());
+	spin_lock(&lith->lock);
+	{
+		val = li_ad1843_wait(lith);
+		if (val == 0) {
+			li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg);
+			val = li_ad1843_wait(lith);
+		}
+		if (val == 0)
+			val = li_readl(lith, LI_CODEC_DATA);
+	}
+	spin_unlock(&lith->lock);
+
+	DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n",
+	      lith, reg, val);
+
+	return val;
+}
+
+/*
+ * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
+ */
+
+static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval)
+{
+	spin_lock(&lith->lock);
+	{
+		if (li_ad1843_wait(lith) == 0) {
+			li_writel(lith, LI_CODEC_DATA, newval);
+			li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg);
+		}
+	}
+	spin_unlock(&lith->lock);
+}
+
+/*
+ * li_setup_dma calculates all the register settings for DMA in a particular
+ * mode.  It takes too many arguments.
+ */
+
+static void li_setup_dma(dma_chan_t *chan,
+			 const dma_chan_desc_t *desc,
+			 lithium_t *lith,
+			 unsigned long buffer_paddr,
+			 int bufshift,
+			 int fragshift,
+			 int channels,
+			 int sampsize)
+{
+	unsigned long mode, format;
+	unsigned long size, tmask;
+
+	DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, "
+	     "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n",
+	     chan, desc, lith, buffer_paddr,
+	     bufshift, fragshift, channels, sampsize);
+
+	/* Reset the channel first. */
+
+	li_writel(lith, desc->ctlreg, LI_CCTL_RESET);
+
+	ASSERT(channels == 1 || channels == 2);
+	if (channels == 2)
+		mode = LI_CCFG_MODE_STEREO;
+	else
+		mode = LI_CCFG_MODE_MONO;
+	ASSERT(sampsize == 1 || sampsize == 2);
+	if (sampsize == 2)
+		format = LI_CCFG_FMT_16BIT;
+	else
+		format = LI_CCFG_FMT_8BIT;
+	chan->desc = desc;
+	chan->lith = lith;
+
+	/*
+	 * Lithium DMA address register takes a 40-bit physical
+	 * address, right-shifted by 8 so it fits in 32 bits.  Bit 37
+	 * must be set -- it enables cache coherence.
+	 */
+
+	ASSERT(!(buffer_paddr & 0xFF));
+	chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
+
+	chan->cfgval = ((chan->cfgval & ~LI_CCFG_LOCK) |
+			SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
+			desc->direction |
+			mode |
+			format);
+
+	size = bufshift - 6;
+	tmask = 13 - fragshift;		/* See Lithium DMA Notes above. */
+	ASSERT(size >= 2 && size <= 7);
+	ASSERT(tmask >= 1 && tmask <= 7);
+	chan->ctlval = ((chan->ctlval & ~LI_CCTL_RESET) |
+			SHIFT_FIELD(size, LI_CCTL_SIZE) |
+			(chan->ctlval & ~LI_CCTL_DMA_ENABLE) |
+			SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
+			SHIFT_FIELD(0, LI_CCTL_TPTR));
+
+	DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval);
+	DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval);
+	DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval);
+
+	li_writel(lith, desc->basereg, chan->baseval);
+	li_writel(lith, desc->cfgreg, chan->cfgval);
+	li_writel(lith, desc->ctlreg, chan->ctlval);
+
+	DBGRV();
+}
+
+static void li_shutdown_dma(dma_chan_t *chan)
+{
+	lithium_t *lith = chan->lith;
+	void * lith1 = lith->page1;
+
+	DBGEV("(chan=0x%p)\n", chan);
+	
+	chan->ctlval &= ~LI_CCTL_DMA_ENABLE;
+	DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
+	li_writel(lith, chan->desc->ctlreg, chan->ctlval);
+
+	/*
+	 * Offset 0x500 on Lithium page 1 is an undocumented,
+	 * unsupported register that holds the zero sample value.
+	 * Lithium is supposed to output zero samples when DMA is
+	 * inactive, and repeat the last sample when DMA underflows.
+	 * But it has a bug, where, after underflow occurs, the zero
+	 * sample is not reset.
+	 *
+	 * I expect this to break in a future rev of Lithium.
+	 */
+
+	if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT)
+		* (volatile unsigned long *) (lith1 + 0x500) = 0;
+}
+
+/*
+ * li_activate_dma always starts dma at the beginning of the buffer.
+ *
+ * N.B., these may be called from interrupt.
+ */
+
+static __inline__ void li_activate_dma(dma_chan_t *chan)
+{
+	chan->ctlval |= LI_CCTL_DMA_ENABLE;
+	DBGPV("ctlval = 0x%lx\n", chan->ctlval);
+	li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval);
+}
+
+static void li_deactivate_dma(dma_chan_t *chan)
+{
+	lithium_t *lith = chan->lith;
+	void * lith2 = lith->page2;
+
+	chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR);
+	DBGPV("ctlval = 0x%lx\n", chan->ctlval);
+	DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
+	li_writel(lith, chan->desc->ctlreg, chan->ctlval);
+
+	/*
+	 * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented,
+	 * unsupported registers that are internal copies of the DMA
+	 * read and write pointers.  Because of a Lithium bug, these
+	 * registers aren't zeroed correctly when DMA is shut off.  So
+	 * we whack them directly.
+	 *
+	 * I expect this to break in a future rev of Lithium.
+	 */
+
+	if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) {
+		* (volatile unsigned long *) (lith2 + 0x98) = 0;
+		* (volatile unsigned long *) (lith2 + 0x9C) = 0;
+	}
+}
+
+/*
+ * read/write the ring buffer pointers.  These routines' arguments and results
+ * are byte offsets from the beginning of the ring buffer.
+ */
+
+static __inline__ int li_read_swptr(dma_chan_t *chan)
+{
+	const unsigned long mask = chan->desc->swptrmask;
+
+	return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask));
+}
+
+static __inline__ int li_read_hwptr(dma_chan_t *chan)
+{
+	return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg));
+}
+
+static __inline__ void li_write_swptr(dma_chan_t *chan, int val)
+{
+	const unsigned long mask = chan->desc->swptrmask;
+
+	ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF)));
+	val = BYTES_TO_CHUNKS(val);
+	chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask);
+	li_writeb(chan->lith, chan->desc->swptrreg, val);
+}
+
+/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */
+
+static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc)
+{
+	lithium_t *lith = chan->lith;
+	const dma_chan_desc_t *desc = chan->desc;
+	unsigned long now_low, now_high0, now_high1, chan_ust;
+
+	spin_lock(&lith->lock);
+	{
+		/*
+		 * retry until we do all five reads without the
+		 * high word changing.  (High word increments
+		 * every 2^32 microseconds, i.e., not often)
+		 */
+		do {
+			now_high0 = li_readl(lith, LI_UST_HIGH);
+			now_low = li_readl(lith, LI_UST_LOW);
+
+			/*
+			 * Lithium guarantees these two reads will be
+			 * atomic -- ust will not increment after msc
+			 * is read.
+			 */
+
+			ustmsc->msc = li_readl(lith, desc->mscreg);
+			chan_ust = li_readl(lith, desc->ustreg);
+
+			now_high1 = li_readl(lith, LI_UST_HIGH);
+		} while (now_high0 != now_high1);
+	}	
+	spin_unlock(&lith->lock);
+	ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust);
+}
+
+static void li_enable_interrupts(lithium_t *lith, unsigned int mask)
+{
+	DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
+
+	/* clear any already-pending interrupts. */
+
+	li_writel(lith, LI_INTR_STATUS, mask);
+
+	/* enable the interrupts. */
+
+	mask |= li_readl(lith, LI_INTR_MASK);
+	li_writel(lith, LI_INTR_MASK, mask);
+}
+
+static void li_disable_interrupts(lithium_t *lith, unsigned int mask)
+{
+	unsigned int keepmask;
+
+	DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
+
+	/* disable the interrupts */
+
+	keepmask = li_readl(lith, LI_INTR_MASK) & ~mask;
+	li_writel(lith, LI_INTR_MASK, keepmask);
+
+	/* clear any pending interrupts. */
+
+	li_writel(lith, LI_INTR_STATUS, mask);
+}
+
+/* Get the interrupt status and clear all pending interrupts. */
+
+static unsigned int li_get_clear_intr_status(lithium_t *lith)
+{
+	unsigned int status;
+
+	status = li_readl(lith, LI_INTR_STATUS);
+	li_writel(lith, LI_INTR_STATUS, ~0);
+	return status & li_readl(lith, LI_INTR_MASK);
+}
+
+static int li_init(lithium_t *lith)
+{
+	/* 1. System power supplies stabilize. */
+
+	/* 2. Assert the ~RESET signal. */
+
+	li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET);
+	udelay(1);
+
+	/* 3. Deassert the ~RESET signal and enter a wait period to allow
+	   the AD1843 internal clocks and the external crystal oscillator
+	   to stabilize. */
+
+	li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
+	udelay(1);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* AD1843 access */
+
+/*
+ * AD1843 bitfield definitions.  All are named as in the AD1843 data
+ * sheet, with ad1843_ prepended and individual bit numbers removed.
+ *
+ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
+ *
+ * Only the bitfields we need are defined.
+ */
+
+typedef struct ad1843_bitfield {
+	char reg;
+	char lo_bit;
+	char nbits;
+} ad1843_bitfield_t;
+
+static const ad1843_bitfield_t
+	ad1843_PDNO   = {  0, 14,  1 },	/* Converter Power-Down Flag */
+	ad1843_INIT   = {  0, 15,  1 },	/* Clock Initialization Flag */
+	ad1843_RIG    = {  2,  0,  4 },	/* Right ADC Input Gain */
+	ad1843_RMGE   = {  2,  4,  1 },	/* Right ADC Mic Gain Enable */
+	ad1843_RSS    = {  2,  5,  3 },	/* Right ADC Source Select */
+	ad1843_LIG    = {  2,  8,  4 },	/* Left ADC Input Gain */
+	ad1843_LMGE   = {  2, 12,  1 },	/* Left ADC Mic Gain Enable */
+	ad1843_LSS    = {  2, 13,  3 },	/* Left ADC Source Select */
+	ad1843_RX1M   = {  4,  0,  5 },	/* Right Aux 1 Mix Gain/Atten */
+	ad1843_RX1MM  = {  4,  7,  1 },	/* Right Aux 1 Mix Mute */
+	ad1843_LX1M   = {  4,  8,  5 },	/* Left Aux 1 Mix Gain/Atten */
+	ad1843_LX1MM  = {  4, 15,  1 },	/* Left Aux 1 Mix Mute */
+	ad1843_RX2M   = {  5,  0,  5 },	/* Right Aux 2 Mix Gain/Atten */
+	ad1843_RX2MM  = {  5,  7,  1 },	/* Right Aux 2 Mix Mute */
+	ad1843_LX2M   = {  5,  8,  5 },	/* Left Aux 2 Mix Gain/Atten */
+	ad1843_LX2MM  = {  5, 15,  1 },	/* Left Aux 2 Mix Mute */
+	ad1843_RMCM   = {  7,  0,  5 },	/* Right Mic Mix Gain/Atten */
+	ad1843_RMCMM  = {  7,  7,  1 },	/* Right Mic Mix Mute */
+	ad1843_LMCM   = {  7,  8,  5 },	/* Left Mic Mix Gain/Atten */
+	ad1843_LMCMM  = {  7, 15,  1 },	/* Left Mic Mix Mute */
+	ad1843_HPOS   = {  8,  4,  1 },	/* Headphone Output Voltage Swing */
+	ad1843_HPOM   = {  8,  5,  1 },	/* Headphone Output Mute */
+	ad1843_RDA1G  = {  9,  0,  6 },	/* Right DAC1 Analog/Digital Gain */
+	ad1843_RDA1GM = {  9,  7,  1 },	/* Right DAC1 Analog Mute */
+	ad1843_LDA1G  = {  9,  8,  6 },	/* Left DAC1 Analog/Digital Gain */
+	ad1843_LDA1GM = {  9, 15,  1 },	/* Left DAC1 Analog Mute */
+	ad1843_RDA1AM = { 11,  7,  1 },	/* Right DAC1 Digital Mute */
+	ad1843_LDA1AM = { 11, 15,  1 },	/* Left DAC1 Digital Mute */
+	ad1843_ADLC   = { 15,  0,  2 },	/* ADC Left Sample Rate Source */
+	ad1843_ADRC   = { 15,  2,  2 },	/* ADC Right Sample Rate Source */
+	ad1843_DA1C   = { 15,  8,  2 },	/* DAC1 Sample Rate Source */
+	ad1843_C1C    = { 17,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_C2C    = { 20,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_DAADL  = { 25,  4,  2 },	/* Digital ADC Left Source Select */
+	ad1843_DAADR  = { 25,  6,  2 },	/* Digital ADC Right Source Select */
+	ad1843_DRSFLT = { 25, 15,  1 },	/* Digital Reampler Filter Mode */
+	ad1843_ADLF   = { 26,  0,  2 }, /* ADC Left Channel Data Format */
+	ad1843_ADRF   = { 26,  2,  2 }, /* ADC Right Channel Data Format */
+	ad1843_ADTLK  = { 26,  4,  1 },	/* ADC Transmit Lock Mode Select */
+	ad1843_SCF    = { 26,  7,  1 },	/* SCLK Frequency Select */
+	ad1843_DA1F   = { 26,  8,  2 },	/* DAC1 Data Format Select */
+	ad1843_DA1SM  = { 26, 14,  1 },	/* DAC1 Stereo/Mono Mode Select */
+	ad1843_ADLEN  = { 27,  0,  1 },	/* ADC Left Channel Enable */
+	ad1843_ADREN  = { 27,  1,  1 },	/* ADC Right Channel Enable */
+	ad1843_AAMEN  = { 27,  4,  1 },	/* Analog to Analog Mix Enable */
+	ad1843_ANAEN  = { 27,  7,  1 },	/* Analog Channel Enable */
+	ad1843_DA1EN  = { 27,  8,  1 },	/* DAC1 Enable */
+	ad1843_DA2EN  = { 27,  9,  1 },	/* DAC2 Enable */
+	ad1843_C1EN   = { 28, 11,  1 },	/* Clock Generator 1 Enable */
+	ad1843_C2EN   = { 28, 12,  1 },	/* Clock Generator 2 Enable */
+	ad1843_PDNI   = { 28, 15,  1 };	/* Converter Power Down */
+
+/*
+ * The various registers of the AD1843 use three different formats for
+ * specifying gain.  The ad1843_gain structure parameterizes the
+ * formats.
+ */
+
+typedef struct ad1843_gain {
+
+	int	negative;		/* nonzero if gain is negative. */
+	const ad1843_bitfield_t *lfield;
+	const ad1843_bitfield_t *rfield;
+
+} ad1843_gain_t;
+
+static const ad1843_gain_t ad1843_gain_RECLEV
+				= { 0, &ad1843_LIG,   &ad1843_RIG };
+static const ad1843_gain_t ad1843_gain_LINE
+				= { 1, &ad1843_LX1M,  &ad1843_RX1M };
+static const ad1843_gain_t ad1843_gain_CD
+				= { 1, &ad1843_LX2M,  &ad1843_RX2M };
+static const ad1843_gain_t ad1843_gain_MIC
+				= { 1, &ad1843_LMCM,  &ad1843_RMCM };
+static const ad1843_gain_t ad1843_gain_PCM
+				= { 1, &ad1843_LDA1G, &ad1843_RDA1G };
+
+/* read the current value of an AD1843 bitfield. */
+
+static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field)
+{
+	int w = li_read_ad1843_reg(lith, field->reg);
+	int val = w >> field->lo_bit & ((1 << field->nbits) - 1);
+
+	DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n",
+	      lith, field->reg, field->lo_bit, field->nbits, val);
+
+	return val;
+}
+
+/*
+ * write a new value to an AD1843 bitfield and return the old value.
+ */
+
+static int ad1843_write_bits(lithium_t *lith,
+			     const ad1843_bitfield_t *field,
+			     int newval)
+{
+	int w = li_read_ad1843_reg(lith, field->reg);
+	int mask = ((1 << field->nbits) - 1) << field->lo_bit;
+	int oldval = (w & mask) >> field->lo_bit;
+	int newbits = (newval << field->lo_bit) & mask;
+	w = (w & ~mask) | newbits;
+	(void) li_write_ad1843_reg(lith, field->reg, w);
+
+	DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) "
+	      "returns 0x%x\n",
+	      lith, field->reg, field->lo_bit, field->nbits, newval,
+	      oldval);
+
+	return oldval;
+}
+
+/*
+ * ad1843_read_multi reads multiple bitfields from the same AD1843
+ * register.  It uses a single read cycle to do it.  (Reading the
+ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
+ * microseconds.)
+ *
+ * Called ike this.
+ *
+ *  ad1843_read_multi(lith, nfields,
+ *		      &ad1843_FIELD1, &val1,
+ *		      &ad1843_FIELD2, &val2, ...);
+ */
+
+static void ad1843_read_multi(lithium_t *lith, int argcount, ...)
+{
+	va_list ap;
+	const ad1843_bitfield_t *fp;
+	int w = 0, mask, *value, reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const ad1843_bitfield_t *);
+		value = va_arg(ap, int *);
+		if (reg == -1) {
+			reg = fp->reg;
+			w = li_read_ad1843_reg(lith, reg);
+		}
+		ASSERT(reg == fp->reg);
+		mask = (1 << fp->nbits) - 1;
+		*value = w >> fp->lo_bit & mask;
+	}
+	va_end(ap);
+}
+
+/*
+ * ad1843_write_multi stores multiple bitfields into the same AD1843
+ * register.  It uses one read and one write cycle to do it.
+ *
+ * Called like this.
+ *
+ *  ad1843_write_multi(lith, nfields,
+ *		       &ad1843_FIELD1, val1,
+ *		       &ad1843_FIELF2, val2, ...);
+ */
+
+static void ad1843_write_multi(lithium_t *lith, int argcount, ...)
+{
+	va_list ap;
+	int reg;
+	const ad1843_bitfield_t *fp;
+	int value;
+	int w, m, mask, bits;
+
+	mask = 0;
+	bits = 0;
+	reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const ad1843_bitfield_t *);
+		value = va_arg(ap, int);
+		if (reg == -1)
+			reg = fp->reg;
+		ASSERT(fp->reg == reg);
+		m = ((1 << fp->nbits) - 1) << fp->lo_bit;
+		mask |= m;
+		bits |= (value << fp->lo_bit) & m;
+	}
+	va_end(ap);
+	ASSERT(!(bits & ~mask));
+	if (~mask & 0xFFFF)
+		w = li_read_ad1843_reg(lith, reg);
+	else
+		w = 0;
+	w = (w & ~mask) | bits;
+	(void) li_write_ad1843_reg(lith, reg, w);
+}
+
+/*
+ * ad1843_get_gain reads the specified register and extracts the gain value
+ * using the supplied gain type.  It returns the gain in OSS format.
+ */
+
+static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp)
+{
+	int lg, rg;
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg);
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	lg = (lg * 100 + (mask >> 1)) / mask;
+	rg = (rg * 100 + (mask >> 1)) / mask;
+	return lg << 0 | rg << 8;
+}
+
+/*
+ * Set an audio channel's gain. Converts from OSS format to AD1843's
+ * format.
+ *
+ * Returns the new gain, which may be lower than the old gain.
+ */
+
+static int ad1843_set_gain(lithium_t *lith,
+			   const ad1843_gain_t *gp,
+			   int newval)
+{
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	int lg = newval >> 0 & 0xFF;
+	int rg = newval >> 8;
+	if (lg < 0 || lg > 100 || rg < 0 || rg > 100)
+		return -EINVAL;
+	lg = (lg * mask + (mask >> 1)) / 100;
+	rg = (rg * mask + (mask >> 1)) / 100;
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg);
+	return ad1843_get_gain(lith, gp);
+}
+
+/* Returns the current recording source, in OSS format. */
+
+static int ad1843_get_recsrc(lithium_t *lith)
+{
+	int ls = ad1843_read_bits(lith, &ad1843_LSS);
+
+	switch (ls) {
+	case 1:
+		return SOUND_MASK_MIC;
+	case 2:
+		return SOUND_MASK_LINE;
+	case 3:
+		return SOUND_MASK_CD;
+	case 6:
+		return SOUND_MASK_PCM;
+	default:
+		ASSERT(0);
+		return -1;
+	}
+}
+
+/*
+ * Enable/disable digital resample mode in the AD1843.
+ *
+ * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down
+ * while switching modes.  So we save DA1's state (DA2's state is not
+ * interesting), power them down, switch into/out of resample mode,
+ * power them up, and restore state.
+ *
+ * This will cause audible glitches if D/A or A/D is going on, so the
+ * driver disallows that (in mixer_write_ioctl()).
+ *
+ * The open question is, is this worth doing?  I'm leaving it in,
+ * because it's written, but...
+ */
+
+static void ad1843_set_resample_mode(lithium_t *lith, int onoff)
+{
+	/* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */
+	int save_da1 = li_read_ad1843_reg(lith, 9);
+
+	/* Power down A/D and D/A. */
+	ad1843_write_multi(lith, 4,
+			   &ad1843_DA1EN, 0,
+			   &ad1843_DA2EN, 0,
+			   &ad1843_ADLEN, 0,
+			   &ad1843_ADREN, 0);
+
+	/* Switch mode */
+	ASSERT(onoff == 0 || onoff == 1);
+	ad1843_write_bits(lith, &ad1843_DRSFLT, onoff);
+
+ 	/* Power up A/D and D/A. */
+	ad1843_write_multi(lith, 3,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* Restore DA1 mute and gain. */
+	li_write_ad1843_reg(lith, 9, save_da1);
+}
+
+/*
+ * Set recording source.  Arg newsrc specifies an OSS channel mask.
+ *
+ * The complication is that when we switch into/out of loopback mode
+ * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of
+ * digital resampling mode.
+ *
+ * Returns newsrc on success, -errno on failure.
+ */
+
+static int ad1843_set_recsrc(lithium_t *lith, int newsrc)
+{
+	int bits;
+	int oldbits;
+
+	switch (newsrc) {
+	case SOUND_MASK_PCM:
+		bits = 6;
+		break;
+
+	case SOUND_MASK_MIC:
+		bits = 1;
+		break;
+
+	case SOUND_MASK_LINE:
+		bits = 2;
+		break;
+
+	case SOUND_MASK_CD:
+		bits = 3;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	oldbits = ad1843_read_bits(lith, &ad1843_LSS);
+	if (newsrc == SOUND_MASK_PCM && oldbits != 6) {
+		DBGP("enabling digital resample mode\n");
+		ad1843_set_resample_mode(lith, 1);
+		ad1843_write_multi(lith, 2,
+				   &ad1843_DAADL, 2,
+				   &ad1843_DAADR, 2);
+	} else if (newsrc != SOUND_MASK_PCM && oldbits == 6) {
+		DBGP("disabling digital resample mode\n");
+		ad1843_set_resample_mode(lith, 0);
+		ad1843_write_multi(lith, 2,
+				   &ad1843_DAADL, 0,
+				   &ad1843_DAADR, 0);
+	}
+	ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits);
+	return newsrc;
+}
+
+/*
+ * Return current output sources, in OSS format.
+ */
+
+static int ad1843_get_outsrc(lithium_t *lith)
+{
+	int pcm, line, mic, cd;
+
+	pcm  = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM;
+	line = ad1843_read_bits(lith, &ad1843_LX1MM)  ? 0 : SOUND_MASK_LINE;
+	cd   = ad1843_read_bits(lith, &ad1843_LX2MM)  ? 0 : SOUND_MASK_CD;
+	mic  = ad1843_read_bits(lith, &ad1843_LMCMM)  ? 0 : SOUND_MASK_MIC;
+
+	return pcm | line | cd | mic;
+}
+
+/*
+ * Set output sources.  Arg is a mask of active sources in OSS format.
+ *
+ * Returns source mask on success, -errno on failure.
+ */
+
+static int ad1843_set_outsrc(lithium_t *lith, int mask)
+{
+	int pcm, line, mic, cd;
+
+	if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE |
+		     SOUND_MASK_CD | SOUND_MASK_MIC))
+		return -EINVAL;
+	pcm  = (mask & SOUND_MASK_PCM)  ? 0 : 1;
+	line = (mask & SOUND_MASK_LINE) ? 0 : 1;
+	mic  = (mask & SOUND_MASK_MIC)  ? 0 : 1;
+	cd   = (mask & SOUND_MASK_CD)   ? 0 : 1;
+
+	ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm);
+	ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line);
+	ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd,   &ad1843_RX2MM, cd);
+	ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic,  &ad1843_RMCMM, mic);
+
+	return mask;
+}
+
+/* Setup ad1843 for D/A conversion. */
+
+static void ad1843_setup_dac(lithium_t *lith,
+			     int framerate,
+			     int fmt,
+			     int channels)
+{
+	int ad_fmt = 0, ad_mode = 0;
+
+	DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
+	      lith, framerate, fmt, channels);
+
+	switch (fmt) {
+	case AFMT_S8:		ad_fmt = 1; break;
+	case AFMT_U8:		ad_fmt = 1; break;
+	case AFMT_S16_LE:	ad_fmt = 1; break;
+	case AFMT_MU_LAW:	ad_fmt = 2; break;
+	case AFMT_A_LAW:	ad_fmt = 3; break;
+	default:		ASSERT(0);
+	}
+
+	switch (channels) {
+	case 2:			ad_mode = 0; break;
+	case 1:			ad_mode = 1; break;
+	default:		ASSERT(0);
+	}
+		
+	DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt);
+	ASSERT(framerate >= 4000 && framerate <= 49000);
+	ad1843_write_bits(lith, &ad1843_C1C, framerate);
+	ad1843_write_multi(lith, 2,
+			   &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt);
+}
+
+static void ad1843_shutdown_dac(lithium_t *lith)
+{
+	ad1843_write_bits(lith, &ad1843_DA1F, 1);
+}
+
+static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels)
+{
+	int da_fmt = 0;
+
+	DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
+	      lith, framerate, fmt, channels);
+
+	switch (fmt) {
+	case AFMT_S8:		da_fmt = 1; break;
+	case AFMT_U8:		da_fmt = 1; break;
+	case AFMT_S16_LE:	da_fmt = 1; break;
+	case AFMT_MU_LAW:	da_fmt = 2; break;
+	case AFMT_A_LAW:	da_fmt = 3; break;
+	default:		ASSERT(0);
+	}
+
+	DBGPV("da_fmt = %d\n", da_fmt);
+	ASSERT(framerate >= 4000 && framerate <= 49000);
+	ad1843_write_bits(lith, &ad1843_C2C, framerate);
+	ad1843_write_multi(lith, 2,
+			   &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
+}
+
+static void ad1843_shutdown_adc(lithium_t *lith)
+{
+	/* nothing to do */
+}
+
+/*
+ * Fully initialize the ad1843.  As described in the AD1843 data
+ * sheet, section "START-UP SEQUENCE".  The numbered comments are
+ * subsection headings from the data sheet.  See the data sheet, pages
+ * 52-54, for more info.
+ *
+ * return 0 on success, -errno on failure.  */
+
+static int __init ad1843_init(lithium_t *lith)
+{
+	unsigned long later;
+	int err;
+
+	err = li_init(lith);
+	if (err)
+		return err;
+
+	if (ad1843_read_bits(lith, &ad1843_INIT) != 0) {
+		printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n");
+		return -EIO;
+	}
+
+	ad1843_write_bits(lith, &ad1843_SCF, 1);
+
+	/* 4. Put the conversion resources into standby. */
+
+	ad1843_write_bits(lith, &ad1843_PDNI, 0);
+	later = jiffies + HZ / 2;	/* roughly half a second */
+	DBGDO(shut_up++);
+	while (ad1843_read_bits(lith, &ad1843_PDNO)) {
+		if (time_after(jiffies, later)) {
+			printk(KERN_ERR
+			       "vwsnd audio: AD1843 won't power up\n");
+			return -EIO;
+		}
+		schedule();
+	}
+	DBGDO(shut_up--);
+
+	/* 5. Power up the clock generators and enable clock output pins. */
+
+	ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1);
+
+	/* 6. Configure conversion resources while they are in standby. */
+
+ 	/* DAC1 uses clock 1 as source, ADC uses clock 2.  Always. */
+
+	ad1843_write_multi(lith, 3,
+			   &ad1843_DA1C, 1,
+			   &ad1843_ADLC, 2,
+			   &ad1843_ADRC, 2);
+
+	/* 7. Enable conversion resources. */
+
+	ad1843_write_bits(lith, &ad1843_ADTLK, 1);
+	ad1843_write_multi(lith, 5,
+			   &ad1843_ANAEN, 1,
+			   &ad1843_AAMEN, 1,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* 8. Configure conversion resources while they are enabled. */
+
+	ad1843_write_bits(lith, &ad1843_DA1C, 1);
+
+	/* Unmute all channels. */
+
+	ad1843_set_outsrc(lith,
+			  (SOUND_MASK_PCM | SOUND_MASK_LINE |
+			   SOUND_MASK_MIC | SOUND_MASK_CD));
+	ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0);
+
+	/* Set default recording source to Line In and set
+	 * mic gain to +20 dB.
+	 */
+
+	ad1843_set_recsrc(lith, SOUND_MASK_LINE);
+	ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
+
+	/* Set Speaker Out level to +/- 4V and unmute it. */
+
+	ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* PCM I/O */
+
+#define READ_INTR_MASK  (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW)
+#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW)
+
+typedef enum vwsnd_port_swstate {	/* software state */
+	SW_OFF,
+	SW_INITIAL,
+	SW_RUN,
+	SW_DRAIN,
+} vwsnd_port_swstate_t;
+
+typedef enum vwsnd_port_hwstate {	/* hardware state */
+	HW_STOPPED,
+	HW_RUNNING,
+} vwsnd_port_hwstate_t;
+
+/*
+ * These flags are read by ISR, but only written at baseline.
+ */
+
+typedef enum vwsnd_port_flags {
+	DISABLED = 1 << 0,
+	ERFLOWN  = 1 << 1,		/* overflown or underflown */
+	HW_BUSY  = 1 << 2,
+} vwsnd_port_flags_t;
+
+/*
+ * vwsnd_port is the per-port data structure.  Each device has two
+ * ports, one for input and one for output.
+ *
+ * Locking:
+ *
+ *	port->lock protects: hwstate, flags, swb_[iu]_avail.
+ *
+ *	devc->io_mutex protects: swstate, sw_*, swb_[iu]_idx.
+ *
+ *	everything else is only written by open/release or
+ *	pcm_{setup,shutdown}(), which are serialized by a
+ *	combination of devc->open_mutex and devc->io_mutex.
+ */
+
+typedef struct vwsnd_port {
+
+	spinlock_t	lock;
+	wait_queue_head_t queue;
+	vwsnd_port_swstate_t swstate;
+	vwsnd_port_hwstate_t hwstate;
+	vwsnd_port_flags_t flags;
+
+	int		sw_channels;
+	int		sw_samplefmt;
+	int		sw_framerate;
+	int		sample_size;
+	int		frame_size;
+	unsigned int	zero_word;	/* zero for the sample format */
+
+	int		sw_fragshift;
+	int		sw_fragcount;
+	int		sw_subdivshift;
+
+	unsigned int	hw_fragshift;
+	unsigned int	hw_fragsize;
+	unsigned int	hw_fragcount;
+
+	int		hwbuf_size;
+	unsigned long	hwbuf_paddr;
+	unsigned long	hwbuf_vaddr;
+	void *		hwbuf;		/* hwbuf == hwbuf_vaddr */
+	int		hwbuf_max;	/* max bytes to preload */
+
+	void *		swbuf;
+	unsigned int	swbuf_size;	/* size in bytes */
+	unsigned int	swb_u_idx;	/* index of next user byte */
+	unsigned int	swb_i_idx;	/* index of next intr byte */
+	unsigned int	swb_u_avail;	/* # bytes avail to user */
+	unsigned int	swb_i_avail;	/* # bytes avail to intr */
+
+	dma_chan_t	chan;
+
+	/* Accounting */
+
+	int		byte_count;
+	int		frag_count;
+	int		MSC_offset;
+
+} vwsnd_port_t;
+
+/* vwsnd_dev is the per-device data structure. */
+
+typedef struct vwsnd_dev {
+	struct vwsnd_dev *next_dev;
+	int		audio_minor;	/* minor number of audio device */
+	int		mixer_minor;	/* minor number of mixer device */
+
+	struct mutex open_mutex;
+	struct mutex io_mutex;
+	struct mutex mix_mutex;
+	fmode_t		open_mode;
+	wait_queue_head_t open_wait;
+
+	lithium_t	lith;
+
+	vwsnd_port_t	rport;
+	vwsnd_port_t	wport;
+} vwsnd_dev_t;
+
+static vwsnd_dev_t *vwsnd_dev_list;	/* linked list of all devices */
+
+static atomic_t vwsnd_use_count = ATOMIC_INIT(0);
+
+# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count))
+# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count))
+# define IN_USE        (atomic_read(&vwsnd_use_count) != 0)
+
+/*
+ * Lithium can only DMA multiples of 32 bytes.  Its DMA buffer may
+ * be up to 8 Kb.  This driver always uses 8 Kb.
+ *
+ * Memory bug workaround -- I'm not sure what's going on here, but
+ * somehow pcm_copy_out() was triggering segv's going on to the next
+ * page of the hw buffer.  So, I make the hw buffer one size bigger
+ * than we actually use.  That way, the following page is allocated
+ * and mapped, and no error.  I suspect that something is broken
+ * in Cobalt, but haven't really investigated.  HBO is the actual
+ * size of the buffer, and HWBUF_ORDER is what we allocate.
+ */
+
+#define HWBUF_SHIFT 13
+#define HWBUF_SIZE (1 << HWBUF_SHIFT)
+# define HBO         (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0)
+# define HWBUF_ORDER (HBO + 1)		/* next size bigger */
+#define MIN_SPEED 4000
+#define MAX_SPEED 49000
+
+#define MIN_FRAGSHIFT			(DMACHUNK_SHIFT + 1)
+#define MAX_FRAGSHIFT			(PAGE_SHIFT)
+#define MIN_FRAGSIZE			(1 << MIN_FRAGSHIFT)
+#define MAX_FRAGSIZE			(1 << MAX_FRAGSHIFT)
+#define MIN_FRAGCOUNT(fragsize)		3
+#define MAX_FRAGCOUNT(fragsize)		(32 * PAGE_SIZE / (fragsize))
+#define DEFAULT_FRAGSHIFT		12
+#define DEFAULT_FRAGCOUNT		16
+#define DEFAULT_SUBDIVSHIFT		0
+
+/*
+ * The software buffer (swbuf) is a ring buffer shared between user
+ * level and interrupt level.  Each level owns some of the bytes in
+ * the buffer, and may give bytes away by calling swb_inc_{u,i}().
+ * User level calls _u for user, and interrupt level calls _i for
+ * interrupt.
+ *
+ * port->swb_{u,i}_avail is the number of bytes available to that level.
+ *
+ * port->swb_{u,i}_idx is the index of the first available byte in the
+ * buffer.
+ *
+ * Each level calls swb_inc_{u,i}() to atomically increment its index,
+ * recalculate the number of bytes available for both sides, and
+ * return the number of bytes available.  Since each side can only
+ * give away bytes, the other side can only increase the number of
+ * bytes available to this side.  Each side updates its own index
+ * variable, swb_{u,i}_idx, so no lock is needed to read it.
+ *
+ * To query the number of bytes available, call swb_inc_{u,i} with an
+ * increment of zero.
+ */
+
+static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc)
+{
+	if (inc) {
+		port->swb_u_idx += inc;
+		port->swb_u_idx %= port->swbuf_size;
+		port->swb_u_avail -= inc;
+		port->swb_i_avail += inc;
+	}
+	return port->swb_u_avail;
+}
+
+static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	{
+		ret = __swb_inc_u(port, inc);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc)
+{
+	if (inc) {
+		port->swb_i_idx += inc;
+		port->swb_i_idx %= port->swbuf_size;
+		port->swb_i_avail -= inc;
+		port->swb_u_avail += inc;
+	}
+	return port->swb_i_avail;
+}
+
+static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	{
+		ret = __swb_inc_i(port, inc);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+/*
+ * pcm_setup - this routine initializes all port state after
+ * mode-setting ioctls have been done, but before the first I/O is
+ * done.
+ *
+ * Locking: called with devc->io_mutex held.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int pcm_setup(vwsnd_dev_t *devc,
+		     vwsnd_port_t *rport,
+		     vwsnd_port_t *wport)
+{
+	vwsnd_port_t *aport = rport ? rport : wport;
+	int sample_size;
+	unsigned int zero_word;
+
+	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
+
+	ASSERT(aport != NULL);
+	if (aport->swbuf != NULL)
+		return 0;
+	switch (aport->sw_samplefmt) {
+	case AFMT_MU_LAW:
+		sample_size = 1;
+		zero_word = 0xFFFFFFFF ^ 0x80808080;
+		break;
+
+	case AFMT_A_LAW:
+		sample_size = 1;
+		zero_word = 0xD5D5D5D5 ^ 0x80808080;
+		break;
+
+	case AFMT_U8:
+		sample_size = 1;
+		zero_word = 0x80808080;
+		break;
+
+	case AFMT_S8:
+		sample_size = 1;
+		zero_word = 0x00000000;
+		break;
+
+	case AFMT_S16_LE:
+		sample_size = 2;
+		zero_word = 0x00000000;
+		break;
+
+	default:
+		sample_size = 0;	/* prevent compiler warning */
+		zero_word = 0;
+		ASSERT(0);
+	}
+	aport->sample_size  = sample_size;
+	aport->zero_word    = zero_word;
+	aport->frame_size   = aport->sw_channels * aport->sample_size;
+	aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift;
+	aport->hw_fragsize  = 1 << aport->hw_fragshift;
+	aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift;
+	ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE);
+	ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE);
+	ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize));
+	ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize));
+	if (rport) {
+		int hwfrags, swfrags;
+		rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
+		hwfrags = rport->hwbuf_max >> aport->hw_fragshift;
+		swfrags = aport->hw_fragcount - hwfrags;
+		if (swfrags < 2)
+			swfrags = 2;
+		rport->swbuf_size = swfrags * aport->hw_fragsize;
+		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
+		DBGPV("read hwbuf_max = %d, swbuf_size = %d\n",
+		     rport->hwbuf_max, rport->swbuf_size);
+	}
+	if (wport) {
+		int hwfrags, swfrags;
+		int total_bytes = aport->hw_fragcount * aport->hw_fragsize;
+		wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
+		if (wport->hwbuf_max > total_bytes)
+			wport->hwbuf_max = total_bytes;
+		hwfrags = wport->hwbuf_max >> aport->hw_fragshift;
+		DBGPV("hwfrags = %d\n", hwfrags);
+		swfrags = aport->hw_fragcount - hwfrags;
+		if (swfrags < 2)
+			swfrags = 2;
+		wport->swbuf_size = swfrags * aport->hw_fragsize;
+		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
+		DBGPV("write hwbuf_max = %d, swbuf_size = %d\n",
+		     wport->hwbuf_max, wport->swbuf_size);
+	}
+
+	aport->swb_u_idx    = 0;
+	aport->swb_i_idx    = 0;
+	aport->byte_count   = 0;
+
+	/*
+	 * Is this a Cobalt bug?  We need to make this buffer extend
+	 * one page further than we actually use -- somehow memcpy
+	 * causes an exceptoin otherwise.  I suspect there's a bug in
+	 * Cobalt (or somewhere) where it's generating a fault on a
+	 * speculative load or something.  Obviously, I haven't taken
+	 * the time to track it down.
+	 */
+
+	aport->swbuf        = vmalloc(aport->swbuf_size + PAGE_SIZE);
+	if (!aport->swbuf)
+		return -ENOMEM;
+	if (rport && wport) {
+		ASSERT(aport == rport);
+		ASSERT(wport->swbuf == NULL);
+		/* One extra page - see comment above. */
+		wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);
+		if (!wport->swbuf) {
+			vfree(aport->swbuf);
+			aport->swbuf = NULL;
+			return -ENOMEM;
+		}
+		wport->sample_size  = rport->sample_size;
+		wport->zero_word    = rport->zero_word;
+		wport->frame_size   = rport->frame_size;
+		wport->hw_fragshift = rport->hw_fragshift;
+		wport->hw_fragsize  = rport->hw_fragsize;
+		wport->hw_fragcount = rport->hw_fragcount;
+		wport->swbuf_size   = rport->swbuf_size;
+		wport->hwbuf_max    = rport->hwbuf_max;
+		wport->swb_u_idx    = rport->swb_u_idx;
+		wport->swb_i_idx    = rport->swb_i_idx;
+		wport->byte_count   = rport->byte_count;
+	}
+	if (rport) {
+		rport->swb_u_avail = 0;
+		rport->swb_i_avail = rport->swbuf_size;
+		rport->swstate = SW_RUN;
+		li_setup_dma(&rport->chan,
+			     &li_comm1,
+			     &devc->lith,
+			     rport->hwbuf_paddr,
+			     HWBUF_SHIFT,
+			     rport->hw_fragshift,
+			     rport->sw_channels,
+			     rport->sample_size);
+		ad1843_setup_adc(&devc->lith,
+				 rport->sw_framerate,
+				 rport->sw_samplefmt,
+				 rport->sw_channels);
+		li_enable_interrupts(&devc->lith, READ_INTR_MASK);
+		if (!(rport->flags & DISABLED)) {
+			ustmsc_t ustmsc;
+			rport->hwstate = HW_RUNNING;
+			li_activate_dma(&rport->chan);
+			li_read_USTMSC(&rport->chan, &ustmsc);
+			rport->MSC_offset = ustmsc.msc;
+		}
+	}
+	if (wport) {
+		if (wport->hwbuf_max > wport->swbuf_size)
+			wport->hwbuf_max = wport->swbuf_size;
+		wport->flags &= ~ERFLOWN;
+		wport->swb_u_avail = wport->swbuf_size;
+		wport->swb_i_avail = 0;
+		wport->swstate = SW_RUN;
+		li_setup_dma(&wport->chan,
+			     &li_comm2,
+			     &devc->lith,
+			     wport->hwbuf_paddr,
+			     HWBUF_SHIFT,
+			     wport->hw_fragshift,
+			     wport->sw_channels,
+			     wport->sample_size);
+		ad1843_setup_dac(&devc->lith,
+				 wport->sw_framerate,
+				 wport->sw_samplefmt,
+				 wport->sw_channels);
+		li_enable_interrupts(&devc->lith, WRITE_INTR_MASK);
+	}
+	DBGRV();
+	return 0;
+}
+
+/*
+ * pcm_shutdown_port - shut down one port (direction) for PCM I/O.
+ * Only called from pcm_shutdown.
+ */
+
+static void pcm_shutdown_port(vwsnd_dev_t *devc,
+			      vwsnd_port_t *aport,
+			      unsigned int mask)
+{
+	unsigned long flags;
+	vwsnd_port_hwstate_t hwstate;
+	DECLARE_WAITQUEUE(wait, current);
+
+	aport->swstate = SW_INITIAL;
+	add_wait_queue(&aport->queue, &wait);
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		spin_lock_irqsave(&aport->lock, flags);
+		{
+			hwstate = aport->hwstate;
+		}		
+		spin_unlock_irqrestore(&aport->lock, flags);
+		if (hwstate == HW_STOPPED)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&aport->queue, &wait);
+	li_disable_interrupts(&devc->lith, mask);
+	if (aport == &devc->rport)
+		ad1843_shutdown_adc(&devc->lith);
+	else /* aport == &devc->wport) */
+		ad1843_shutdown_dac(&devc->lith);
+	li_shutdown_dma(&aport->chan);
+	vfree(aport->swbuf);
+	aport->swbuf = NULL;
+	aport->byte_count = 0;
+}
+
+/*
+ * pcm_shutdown undoes what pcm_setup did.
+ * Also sets the ports' swstate to newstate.
+ */
+
+static void pcm_shutdown(vwsnd_dev_t *devc,
+			 vwsnd_port_t *rport,
+			 vwsnd_port_t *wport)
+{
+	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
+
+	if (rport && rport->swbuf) {
+		DBGPV("shutting down rport\n");
+		pcm_shutdown_port(devc, rport, READ_INTR_MASK);
+	}
+	if (wport && wport->swbuf) {
+		DBGPV("shutting down wport\n");
+		pcm_shutdown_port(devc, wport, WRITE_INTR_MASK);
+	}
+	DBGRV();
+}
+
+static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb)
+{
+	char *src = rport->hwbuf + hwidx;
+	char *dst = rport->swbuf + swidx;
+	int fmt = rport->sw_samplefmt;
+
+	DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx);
+	ASSERT(rport->hwbuf != NULL);
+	ASSERT(rport->swbuf != NULL);
+	ASSERT(nb > 0 && (nb % 32) == 0);
+	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
+	ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size);
+	ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size);
+
+	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
+
+		/* See Sample Format Notes above. */
+
+		char *end = src + nb;
+		while (src < end)
+			*dst++ = *src++ ^ 0x80;
+	} else
+		memcpy(dst, src, nb);
+}
+
+static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb)
+{
+	char *src = wport->swbuf + swidx;
+	char *dst = wport->hwbuf + hwidx;
+	int fmt = wport->sw_samplefmt;
+
+	ASSERT(nb > 0 && (nb % 32) == 0);
+	ASSERT(wport->hwbuf != NULL);
+	ASSERT(wport->swbuf != NULL);
+	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
+	ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size);
+	ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size);
+	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
+
+		/* See Sample Format Notes above. */
+
+		char *end = src + nb;
+		while (src < end)
+			*dst++ = *src++ ^ 0x80;
+	} else
+		memcpy(dst, src, nb);
+}
+
+/*
+ * pcm_output() is called both from baselevel and from interrupt level.
+ * This is where audio frames are copied into the hardware-accessible
+ * ring buffer.
+ *
+ * Locking note: The part of this routine that figures out what to do
+ * holds wport->lock.  The longer part releases wport->lock, but sets
+ * wport->flags & HW_BUSY.  Afterward, it reacquires wport->lock, and
+ * checks for more work to do.
+ *
+ * If another thread calls pcm_output() while HW_BUSY is set, it
+ * returns immediately, knowing that the thread that set HW_BUSY will
+ * look for more work to do before returning.
+ *
+ * This has the advantage that port->lock is held for several short
+ * periods instead of one long period.  Also, when pcm_output is
+ * called from base level, it reenables interrupts.
+ */
+
+static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb)
+{
+	vwsnd_port_t *wport = &devc->wport;
+	const int hwmax  = wport->hwbuf_max;
+	const int hwsize = wport->hwbuf_size;
+	const int swsize = wport->swbuf_size;
+	const int fragsize = wport->hw_fragsize;
+	unsigned long iflags;
+
+	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
+	spin_lock_irqsave(&wport->lock, iflags);
+	if (erflown)
+		wport->flags |= ERFLOWN;
+	(void) __swb_inc_u(wport, nb);
+	if (wport->flags & HW_BUSY) {
+		spin_unlock_irqrestore(&wport->lock, iflags);
+		DBGPV("returning: HW BUSY\n");
+		return;
+	}
+	if (wport->flags & DISABLED) {
+		spin_unlock_irqrestore(&wport->lock, iflags);
+		DBGPV("returning: DISABLED\n");
+		return;
+	}
+	wport->flags |= HW_BUSY;
+	while (1) {
+		int swptr, hwptr, hw_avail, sw_avail, swidx;
+		vwsnd_port_hwstate_t hwstate = wport->hwstate;
+		vwsnd_port_swstate_t swstate = wport->swstate;
+		int hw_unavail;
+		ustmsc_t ustmsc;
+
+		hwptr = li_read_hwptr(&wport->chan);
+		swptr = li_read_swptr(&wport->chan);
+		hw_unavail = (swptr - hwptr + hwsize) % hwsize;
+		hw_avail = (hwmax - hw_unavail) & -fragsize;
+		sw_avail = wport->swb_i_avail & -fragsize;
+		if (sw_avail && swstate == SW_RUN) {
+			if (wport->flags & ERFLOWN) {
+				wport->flags &= ~ERFLOWN;
+			}
+		} else if (swstate == SW_INITIAL ||
+			 swstate == SW_OFF ||
+			 (swstate == SW_DRAIN &&
+			  !sw_avail &&
+			  (wport->flags & ERFLOWN))) {
+			DBGP("stopping.  hwstate = %d\n", hwstate);
+			if (hwstate != HW_STOPPED) {
+				li_deactivate_dma(&wport->chan);
+				wport->hwstate = HW_STOPPED;
+			}
+			wake_up(&wport->queue);
+			break;
+		}
+		if (!sw_avail || !hw_avail)
+			break;
+		spin_unlock_irqrestore(&wport->lock, iflags);
+
+		/*
+		 * We gave up the port lock, but we have the HW_BUSY flag.
+		 * Proceed without accessing any nonlocal state.
+		 * Do not exit the loop -- must check for more work.
+		 */
+
+		swidx = wport->swb_i_idx;
+		nb = hw_avail;
+		if (nb > sw_avail)
+			nb = sw_avail;
+		if (nb > hwsize - swptr)
+			nb = hwsize - swptr; /* don't overflow hwbuf */
+		if (nb > swsize - swidx)
+			nb = swsize - swidx; /* don't overflow swbuf */
+		ASSERT(nb > 0);
+		if (nb % fragsize) {
+			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
+			DBGP("hw_avail = %d\n", hw_avail);
+			DBGP("sw_avail = %d\n", sw_avail);
+			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
+			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
+		}
+		ASSERT(!(nb % fragsize));
+		DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n",
+		      swidx, swidx + nb, swptr, swptr + nb);
+		pcm_copy_out(wport, swidx, swptr, nb);
+		li_write_swptr(&wport->chan, (swptr + nb) % hwsize);
+		spin_lock_irqsave(&wport->lock, iflags);
+		if (hwstate == HW_STOPPED) {
+			DBGPV("starting\n");
+			li_activate_dma(&wport->chan);
+			wport->hwstate = HW_RUNNING;
+			li_read_USTMSC(&wport->chan, &ustmsc);
+			ASSERT(wport->byte_count % wport->frame_size == 0);
+			wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size;
+		}
+		__swb_inc_i(wport, nb);
+		wport->byte_count += nb;
+		wport->frag_count += nb / fragsize;
+		ASSERT(nb % fragsize == 0);
+		wake_up(&wport->queue);
+	}
+	wport->flags &= ~HW_BUSY;
+	spin_unlock_irqrestore(&wport->lock, iflags);
+	DBGRV();
+}
+
+/*
+ * pcm_input() is called both from baselevel and from interrupt level.
+ * This is where audio frames are copied out of the hardware-accessible
+ * ring buffer.
+ *
+ * Locking note: The part of this routine that figures out what to do
+ * holds rport->lock.  The longer part releases rport->lock, but sets
+ * rport->flags & HW_BUSY.  Afterward, it reacquires rport->lock, and
+ * checks for more work to do.
+ *
+ * If another thread calls pcm_input() while HW_BUSY is set, it
+ * returns immediately, knowing that the thread that set HW_BUSY will
+ * look for more work to do before returning.
+ *
+ * This has the advantage that port->lock is held for several short
+ * periods instead of one long period.  Also, when pcm_input is
+ * called from base level, it reenables interrupts.
+ */
+
+static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb)
+{
+	vwsnd_port_t *rport = &devc->rport;
+	const int hwmax  = rport->hwbuf_max;
+	const int hwsize = rport->hwbuf_size;
+	const int swsize = rport->swbuf_size;
+	const int fragsize = rport->hw_fragsize;
+	unsigned long iflags;
+
+	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
+
+	spin_lock_irqsave(&rport->lock, iflags);
+	if (erflown)
+		rport->flags |= ERFLOWN;
+	(void) __swb_inc_u(rport, nb);
+	if (rport->flags & HW_BUSY || !rport->swbuf) {
+		spin_unlock_irqrestore(&rport->lock, iflags);
+		DBGPV("returning: HW BUSY or !swbuf\n");
+		return;
+	}
+	if (rport->flags & DISABLED) {
+		spin_unlock_irqrestore(&rport->lock, iflags);
+		DBGPV("returning: DISABLED\n");
+		return;
+	}
+	rport->flags |= HW_BUSY;
+	while (1) {
+		int swptr, hwptr, hw_avail, sw_avail, swidx;
+		vwsnd_port_hwstate_t hwstate = rport->hwstate;
+		vwsnd_port_swstate_t swstate = rport->swstate;
+
+		hwptr = li_read_hwptr(&rport->chan);
+		swptr = li_read_swptr(&rport->chan);
+		hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize;
+		if (hw_avail > hwmax)
+			hw_avail = hwmax;
+		sw_avail = rport->swb_i_avail & -fragsize;
+		if (swstate != SW_RUN) {
+			DBGP("stopping.  hwstate = %d\n", hwstate);
+			if (hwstate != HW_STOPPED) {
+				li_deactivate_dma(&rport->chan);
+				rport->hwstate = HW_STOPPED;
+			}
+			wake_up(&rport->queue);
+			break;
+		}
+		if (!sw_avail || !hw_avail)
+			break;
+		spin_unlock_irqrestore(&rport->lock, iflags);
+
+		/*
+		 * We gave up the port lock, but we have the HW_BUSY flag.
+		 * Proceed without accessing any nonlocal state.
+		 * Do not exit the loop -- must check for more work.
+		 */
+
+		swidx = rport->swb_i_idx;
+		nb = hw_avail;
+		if (nb > sw_avail)
+			nb = sw_avail;
+		if (nb > hwsize - swptr)
+			nb = hwsize - swptr; /* don't overflow hwbuf */
+		if (nb > swsize - swidx)
+			nb = swsize - swidx; /* don't overflow swbuf */
+		ASSERT(nb > 0);
+		if (nb % fragsize) {
+			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
+			DBGP("hw_avail = %d\n", hw_avail);
+			DBGP("sw_avail = %d\n", sw_avail);
+			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
+			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
+		}
+		ASSERT(!(nb % fragsize));
+		DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n",
+		      swptr, swptr + nb, swidx, swidx + nb);
+		pcm_copy_in(rport, swidx, swptr, nb);
+		li_write_swptr(&rport->chan, (swptr + nb) % hwsize);
+		spin_lock_irqsave(&rport->lock, iflags);
+		__swb_inc_i(rport, nb);
+		rport->byte_count += nb;
+		rport->frag_count += nb / fragsize;
+		ASSERT(nb % fragsize == 0);
+		wake_up(&rport->queue);
+	}
+	rport->flags &= ~HW_BUSY;
+	spin_unlock_irqrestore(&rport->lock, iflags);
+	DBGRV();
+}
+
+/*
+ * pcm_flush_frag() writes zero samples to fill the current fragment,
+ * then flushes it to the hardware.
+ *
+ * It is only meaningful to flush output, not input.
+ */
+
+static void pcm_flush_frag(vwsnd_dev_t *devc)
+{
+	vwsnd_port_t *wport = &devc->wport;
+
+	DBGPV("swstate = %d\n", wport->swstate);
+	if (wport->swstate == SW_RUN) {
+		int idx = wport->swb_u_idx;
+		int end = (idx + wport->hw_fragsize - 1)
+			>> wport->hw_fragshift
+			<< wport->hw_fragshift;
+		int nb = end - idx;
+		DBGPV("clearing %d bytes\n", nb);
+		if (nb)
+			memset(wport->swbuf + idx,
+			       (char) wport->zero_word,
+			       nb);
+		wport->swstate = SW_DRAIN;
+		pcm_output(devc, 0, nb);
+	}
+	DBGRV();
+}
+
+/*
+ * Wait for output to drain.  This sleeps uninterruptibly because
+ * there is nothing intelligent we can do if interrupted.  This
+ * means the process will be delayed in responding to the signal.
+ */
+
+static void pcm_write_sync(vwsnd_dev_t *devc)
+{
+	vwsnd_port_t *wport = &devc->wport;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	vwsnd_port_hwstate_t hwstate;
+
+	DBGEV("(devc=0x%p)\n", devc);
+	add_wait_queue(&wport->queue, &wait);
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			hwstate = wport->hwstate;
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		if (hwstate == HW_STOPPED)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&wport->queue, &wait);
+	DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate);
+	DBGRV();
+}
+
+/*****************************************************************************/
+/* audio driver */
+
+/*
+ * seek on an audio device always fails.
+ */
+
+static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status)
+{
+	int overflown = status & LI_INTR_COMM1_OVERFLOW;
+
+	if (status & READ_INTR_MASK)
+		pcm_input(devc, overflown, 0);
+}
+
+static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status)
+{
+	int underflown = status & LI_INTR_COMM2_UNDERFLOW;
+
+	if (status & WRITE_INTR_MASK)
+		pcm_output(devc, underflown, 0);
+}
+
+static irqreturn_t vwsnd_audio_intr(int irq, void *dev_id)
+{
+	vwsnd_dev_t *devc = dev_id;
+	unsigned int status;
+
+	DBGEV("(irq=%d, dev_id=0x%p)\n", irq, dev_id);
+
+	status = li_get_clear_intr_status(&devc->lith);
+	vwsnd_audio_read_intr(devc, status);
+	vwsnd_audio_write_intr(devc, status);
+	return IRQ_HANDLED;
+}
+
+static ssize_t vwsnd_audio_do_read(struct file *file,
+				   char *buffer,
+				   size_t count,
+				   loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ?
+			       &devc->rport : NULL);
+	int ret, nb;
+
+	DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
+	     file, buffer, count, ppos);
+
+	if (!rport)
+		return -EINVAL;
+
+	if (rport->swbuf == NULL) {
+		vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+			&devc->wport : NULL;
+		ret = pcm_setup(devc, rport, wport);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count) {
+		DECLARE_WAITQUEUE(wait, current);
+		add_wait_queue(&rport->queue, &wait);
+		while ((nb = swb_inc_u(rport, 0)) == 0) {
+			DBGPV("blocking\n");
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (rport->flags & DISABLED ||
+			    file->f_flags & O_NONBLOCK) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&rport->queue, &wait);
+				return ret ? ret : -EAGAIN;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&rport->queue, &wait);
+				return ret ? ret : -ERESTARTSYS;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&rport->queue, &wait);
+		pcm_input(devc, 0, 0);
+		/* nb bytes are available in userbuf. */
+		if (nb > count)
+			nb = count;
+		DBGPV("nb = %d\n", nb);
+		if (copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb))
+			return -EFAULT;
+		(void) swb_inc_u(rport, nb);
+		buffer += nb;
+		count -= nb;
+		ret += nb;
+	}
+	DBGPV("returning %d\n", ret);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_read(struct file *file,
+				char *buffer,
+				size_t count,
+				loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	ssize_t ret;
+
+	mutex_lock(&devc->io_mutex);
+	ret = vwsnd_audio_do_read(file, buffer, count, ppos);
+	mutex_unlock(&devc->io_mutex);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_do_write(struct file *file,
+				    const char *buffer,
+				    size_t count,
+				    loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ?
+			       &devc->wport : NULL);
+	int ret, nb;
+
+	DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
+	      file, buffer, count, ppos);
+
+	if (!wport)
+		return -EINVAL;
+
+	if (wport->swbuf == NULL) {
+		vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+			&devc->rport : NULL;
+		ret = pcm_setup(devc, rport, wport);
+		if (ret < 0)
+			return ret;
+	}
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count) {
+		DECLARE_WAITQUEUE(wait, current);
+		add_wait_queue(&wport->queue, &wait);
+		while ((nb = swb_inc_u(wport, 0)) == 0) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (wport->flags & DISABLED ||
+			    file->f_flags & O_NONBLOCK) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&wport->queue, &wait);
+				return ret ? ret : -EAGAIN;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&wport->queue, &wait);
+				return ret ? ret : -ERESTARTSYS;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&wport->queue, &wait);
+		/* nb bytes are available in userbuf. */
+		if (nb > count)
+			nb = count;
+		DBGPV("nb = %d\n", nb);
+		if (copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb))
+			return -EFAULT;
+		pcm_output(devc, 0, nb);
+		buffer += nb;
+		count -= nb;
+		ret += nb;
+	}
+	DBGPV("returning %d\n", ret);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_write(struct file *file,
+				 const char *buffer,
+				 size_t count,
+				 loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	ssize_t ret;
+
+	mutex_lock(&devc->io_mutex);
+	ret = vwsnd_audio_do_write(file, buffer, count, ppos);
+	mutex_unlock(&devc->io_mutex);
+	return ret;
+}
+
+/* No kernel lock - fine */
+static unsigned int vwsnd_audio_poll(struct file *file,
+				     struct poll_table_struct *wait)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+		&devc->rport : NULL;
+	vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+		&devc->wport : NULL;
+	unsigned int mask = 0;
+
+	DBGEV("(file=0x%p, wait=0x%p)\n", file, wait);
+
+	ASSERT(rport || wport);
+	if (rport) {
+		poll_wait(file, &rport->queue, wait);
+		if (swb_inc_u(rport, 0))
+			mask |= (POLLIN | POLLRDNORM);
+	}
+	if (wport) {
+		poll_wait(file, &wport->queue, wait);
+		if (wport->swbuf == NULL || swb_inc_u(wport, 0))
+			mask |= (POLLOUT | POLLWRNORM);
+	}
+
+	DBGPV("returning 0x%x\n", mask);
+	return mask;
+}
+
+static int vwsnd_audio_do_ioctl(struct file *file,
+				unsigned int cmd,
+				unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+		&devc->rport : NULL;
+	vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+		&devc->wport : NULL;
+	vwsnd_port_t *aport = rport ? rport : wport;
+	struct audio_buf_info buf_info;
+	struct count_info info;
+	unsigned long flags;
+	int ival;
+
+	
+	DBGEV("(file=0x%p, cmd=0x%x, arg=0x%lx)\n",
+	      file, cmd, arg);
+	switch (cmd) {
+	case OSS_GETVERSION:		/* _SIOR ('M', 118, int) */
+		DBGX("OSS_GETVERSION\n");
+		ival = SOUND_VERSION;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETCAPS:	/* _SIOR ('P',15, int) */
+		DBGX("SNDCTL_DSP_GETCAPS\n");
+		ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETFMTS:	/* _SIOR ('P',11, int) */
+		DBGX("SNDCTL_DSP_GETFMTS\n");
+		ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW |
+			AFMT_U8 | AFMT_S8);
+		return put_user(ival, (int *) arg);
+		break;
+
+	case SOUND_PCM_READ_RATE:	/* _SIOR ('P', 2, int) */
+		DBGX("SOUND_PCM_READ_RATE\n");
+		ival = aport->sw_framerate;
+		return put_user(ival, (int *) arg);
+
+	case SOUND_PCM_READ_CHANNELS:	/* _SIOR ('P', 6, int) */
+		DBGX("SOUND_PCM_READ_CHANNELS\n");
+		ival = aport->sw_channels;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SPEED:		/* _SIOWR('P', 2, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SPEED %d\n", ival);
+		if (ival) {
+			if (aport->swstate != SW_INITIAL) {
+				DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n",
+				     aport->swstate);
+				return -EINVAL;
+			}
+			if (ival < MIN_SPEED)
+				ival = MIN_SPEED;
+			if (ival > MAX_SPEED)
+				ival = MAX_SPEED;
+			if (rport)
+				rport->sw_framerate = ival;
+			if (wport)
+				wport->sw_framerate = ival;
+		} else
+			ival = aport->sw_framerate;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_STEREO:		/* _SIOWR('P', 3, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_STEREO %d\n", ival);
+		if (ival != 0 && ival != 1)
+			return -EINVAL;
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		if (rport)
+			rport->sw_channels = ival + 1;
+		if (wport)
+			wport->sw_channels = ival + 1;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_CHANNELS:	/* _SIOWR('P', 6, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_CHANNELS %d\n", ival);
+		if (ival != 1 && ival != 2)
+			return -EINVAL;
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		if (rport)
+			rport->sw_channels = ival;
+		if (wport)
+			wport->sw_channels = ival;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETBLKSIZE:	/* _SIOWR('P', 4, int) */
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0) {
+			DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival);
+			return ival;
+		}
+		ival = 1 << aport->sw_fragshift;
+		DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:	/* _SIOWR('P',10, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n",
+		     ival >> 16, ival & 0xFFFF);
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		{
+			int sw_fragshift = ival & 0xFFFF;
+			int sw_subdivshift = aport->sw_subdivshift;
+			int hw_fragshift = sw_fragshift - sw_subdivshift;
+			int sw_fragcount = (ival >> 16) & 0xFFFF;
+			int hw_fragsize;
+			if (hw_fragshift < MIN_FRAGSHIFT)
+				hw_fragshift = MIN_FRAGSHIFT;
+			if (hw_fragshift > MAX_FRAGSHIFT)
+				hw_fragshift = MAX_FRAGSHIFT;
+			sw_fragshift = hw_fragshift + aport->sw_subdivshift;
+			hw_fragsize = 1 << hw_fragshift;
+			if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize))
+				sw_fragcount = MIN_FRAGCOUNT(hw_fragsize);
+			if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
+				sw_fragcount = MAX_FRAGCOUNT(hw_fragsize);
+			DBGPV("sw_fragshift = %d\n", sw_fragshift);
+			DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport);
+			if (rport) {
+				rport->sw_fragshift = sw_fragshift;
+				rport->sw_fragcount = sw_fragcount;
+			}
+			if (wport) {
+				wport->sw_fragshift = sw_fragshift;
+				wport->sw_fragcount = sw_fragcount;
+			}
+			ival = sw_fragcount << 16 | sw_fragshift;
+		}
+		DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n",
+		      ival >> 16, ival & 0xFFFF);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SUBDIVIDE:	/* _SIOWR('P', 9, int) */
+                if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival);
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		{
+			int subdivshift;
+			int hw_fragshift, hw_fragsize, hw_fragcount;
+			switch (ival) {
+			case 1: subdivshift = 0; break;
+			case 2: subdivshift = 1; break;
+			case 4: subdivshift = 2; break;
+			default: return -EINVAL;
+			}
+			hw_fragshift = aport->sw_fragshift - subdivshift;
+			if (hw_fragshift < MIN_FRAGSHIFT ||
+			    hw_fragshift > MAX_FRAGSHIFT)
+				return -EINVAL;
+			hw_fragsize = 1 << hw_fragshift;
+			hw_fragcount = aport->sw_fragcount >> subdivshift;
+			if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) ||
+			    hw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
+				return -EINVAL;
+			if (rport)
+				rport->sw_subdivshift = subdivshift;
+			if (wport)
+				wport->sw_subdivshift = subdivshift;
+		}
+		return 0;
+
+	case SNDCTL_DSP_SETFMT:		/* _SIOWR('P',5, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SETFMT %d\n", ival);
+		if (ival != AFMT_QUERY) {
+			if (aport->swstate != SW_INITIAL) {
+				DBGP("SETFMT failed, swstate = %d\n",
+				     aport->swstate);
+				return -EINVAL;
+			}
+			switch (ival) {
+			case AFMT_MU_LAW:
+			case AFMT_A_LAW:
+			case AFMT_U8:
+			case AFMT_S8:
+			case AFMT_S16_LE:
+				if (rport)
+					rport->sw_samplefmt = ival;
+				if (wport)
+					wport->sw_samplefmt = ival;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+		ival = aport->sw_samplefmt;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETOSPACE:	/* _SIOR ('P',12, audio_buf_info) */
+		DBGXV("SNDCTL_DSP_GETOSPACE\n");
+		if (!wport)
+			return -EINVAL;
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0)
+			return ival;
+		ival = swb_inc_u(wport, 0);
+		buf_info.fragments = ival >> wport->sw_fragshift;
+		buf_info.fragstotal = wport->sw_fragcount;
+		buf_info.fragsize = 1 << wport->sw_fragshift;
+		buf_info.bytes = ival;
+		DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n",
+		     buf_info.fragments, buf_info.fragstotal,
+		     buf_info.fragsize, buf_info.bytes);
+		if (copy_to_user((void *) arg, &buf_info, sizeof buf_info))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_GETISPACE:	/* _SIOR ('P',13, audio_buf_info) */
+		DBGX("SNDCTL_DSP_GETISPACE\n");
+		if (!rport)
+			return -EINVAL;
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0)
+			return ival;
+		ival = swb_inc_u(rport, 0);
+		buf_info.fragments = ival >> rport->sw_fragshift;
+		buf_info.fragstotal = rport->sw_fragcount;
+		buf_info.fragsize = 1 << rport->sw_fragshift;
+		buf_info.bytes = ival;
+		DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n",
+		     buf_info.fragments, buf_info.fragstotal,
+		     buf_info.fragsize, buf_info.bytes);
+		if (copy_to_user((void *) arg, &buf_info, sizeof buf_info))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_NONBLOCK:	/* _SIO  ('P',14) */
+		DBGX("SNDCTL_DSP_NONBLOCK\n");
+		spin_lock(&file->f_lock);
+		file->f_flags |= O_NONBLOCK;
+		spin_unlock(&file->f_lock);
+		return 0;
+
+	case SNDCTL_DSP_RESET:		/* _SIO  ('P', 0) */
+		DBGX("SNDCTL_DSP_RESET\n");
+		/*
+		 * Nothing special needs to be done for input.  Input
+		 * samples sit in swbuf, but it will be reinitialized
+		 * to empty when pcm_setup() is called.
+		 */
+		if (wport && wport->swbuf) {
+			wport->swstate = SW_INITIAL;
+			pcm_output(devc, 0, 0);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		return 0;
+
+	case SNDCTL_DSP_SYNC:		/* _SIO  ('P', 1) */
+		DBGX("SNDCTL_DSP_SYNC\n");
+		if (wport) {
+			pcm_flush_frag(devc);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		return 0;
+
+	case SNDCTL_DSP_POST:		/* _SIO  ('P', 8) */
+		DBGX("SNDCTL_DSP_POST\n");
+		if (!wport)
+			return -EINVAL;
+		pcm_flush_frag(devc);
+		return 0;
+
+	case SNDCTL_DSP_GETIPTR:	/* _SIOR ('P', 17, count_info) */
+		DBGX("SNDCTL_DSP_GETIPTR\n");
+		if (!rport)
+			return -EINVAL;
+		spin_lock_irqsave(&rport->lock, flags);
+		{
+			ustmsc_t ustmsc;
+			if (rport->hwstate == HW_RUNNING) {
+				ASSERT(rport->swstate == SW_RUN);
+				li_read_USTMSC(&rport->chan, &ustmsc);
+				info.bytes = ustmsc.msc - rport->MSC_offset;
+				info.bytes *= rport->frame_size;
+			} else {
+				info.bytes = rport->byte_count;
+			}
+			info.blocks = rport->frag_count;
+			info.ptr = 0;	/* not implemented */
+			rport->frag_count = 0;
+		}
+		spin_unlock_irqrestore(&rport->lock, flags);
+		if (copy_to_user((void *) arg, &info, sizeof info))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_GETOPTR:	/* _SIOR ('P',18, count_info) */
+		DBGX("SNDCTL_DSP_GETOPTR\n");
+		if (!wport)
+			return -EINVAL;
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			ustmsc_t ustmsc;
+			if (wport->hwstate == HW_RUNNING) {
+				ASSERT(wport->swstate == SW_RUN);
+				li_read_USTMSC(&wport->chan, &ustmsc);
+				info.bytes = ustmsc.msc - wport->MSC_offset;
+				info.bytes *= wport->frame_size;
+			} else {
+				info.bytes = wport->byte_count;
+			}
+			info.blocks = wport->frag_count;
+			info.ptr = 0;	/* not implemented */
+			wport->frag_count = 0;
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		if (copy_to_user((void *) arg, &info, sizeof info))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_GETODELAY:	/* _SIOR ('P', 23, int) */
+		DBGX("SNDCTL_DSP_GETODELAY\n");
+		if (!wport)
+			return -EINVAL;
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			int fsize = wport->frame_size;
+			ival = wport->swb_i_avail / fsize;
+			if (wport->hwstate == HW_RUNNING) {
+				int swptr, hwptr, hwframes, hwbytes, hwsize;
+				int totalhwbytes;
+				ustmsc_t ustmsc;
+
+				hwsize = wport->hwbuf_size;
+				swptr = li_read_swptr(&wport->chan);
+				li_read_USTMSC(&wport->chan, &ustmsc);
+				hwframes = ustmsc.msc - wport->MSC_offset;
+				totalhwbytes = hwframes * fsize;
+				hwptr = totalhwbytes % hwsize;
+				hwbytes = (swptr - hwptr + hwsize) % hwsize;
+				ival += hwbytes / fsize;
+			}
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_PROFILE:	/* _SIOW ('P', 23, int) */
+		DBGX("SNDCTL_DSP_PROFILE\n");
+
+		/*
+		 * Thomas Sailer explains SNDCTL_DSP_PROFILE
+		 * (private email, March 24, 1999):
+		 *
+		 *     This gives the sound driver a hint on what it
+		 *     should do with partial fragments
+		 *     (i.e. fragments partially filled with write).
+		 *     This can direct the driver to zero them or
+		 *     leave them alone.  But don't ask me what this
+		 *     is good for, my driver just zeroes the last
+		 *     fragment before the receiver stops, no idea
+		 *     what good for any other behaviour could
+		 *     be. Implementing it as NOP seems safe.
+		 */
+
+		break;
+
+	case SNDCTL_DSP_GETTRIGGER:	/* _SIOR ('P',16, int) */
+		DBGX("SNDCTL_DSP_GETTRIGGER\n");
+		ival = 0;
+		if (rport) {
+			spin_lock_irqsave(&rport->lock, flags);
+			{
+				if (!(rport->flags & DISABLED))
+					ival |= PCM_ENABLE_INPUT;
+			}
+			spin_unlock_irqrestore(&rport->lock, flags);
+		}
+		if (wport) {
+			spin_lock_irqsave(&wport->lock, flags);
+			{
+				if (!(wport->flags & DISABLED))
+					ival |= PCM_ENABLE_OUTPUT;
+			}
+			spin_unlock_irqrestore(&wport->lock, flags);
+		}
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SETTRIGGER:	/* _SIOW ('P',16, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival);
+
+		/*
+		 * If user is disabling I/O and port is not in initial
+		 * state, fail with EINVAL.
+		 */
+
+		if (((rport && !(ival & PCM_ENABLE_INPUT)) ||
+		     (wport && !(ival & PCM_ENABLE_OUTPUT))) &&
+		    aport->swstate != SW_INITIAL)
+			return -EINVAL;
+
+		if (rport) {
+			vwsnd_port_hwstate_t hwstate;
+			spin_lock_irqsave(&rport->lock, flags);
+			{
+				hwstate = rport->hwstate;
+				if (ival & PCM_ENABLE_INPUT)
+					rport->flags &= ~DISABLED;
+				else
+					rport->flags |= DISABLED;
+			}
+			spin_unlock_irqrestore(&rport->lock, flags);
+			if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) {
+
+				if (rport->swstate == SW_INITIAL)
+					pcm_setup(devc, rport, wport);
+				else
+					li_activate_dma(&rport->chan);
+			}
+		}
+		if (wport) {
+			vwsnd_port_flags_t pflags;
+			spin_lock_irqsave(&wport->lock, flags);
+			{
+				pflags = wport->flags;
+				if (ival & PCM_ENABLE_OUTPUT)
+					wport->flags &= ~DISABLED;
+				else
+					wport->flags |= DISABLED;
+			}
+			spin_unlock_irqrestore(&wport->lock, flags);
+			if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) {
+				if (wport->swstate == SW_RUN)
+					pcm_output(devc, 0, 0);
+			}
+		}
+		return 0;
+
+	default:
+		DBGP("unknown ioctl 0x%x\n", cmd);
+		return -EINVAL;
+	}
+	DBGP("unimplemented ioctl 0x%x\n", cmd);
+	return -EINVAL;
+}
+
+static long vwsnd_audio_ioctl(struct file *file,
+				unsigned int cmd,
+				unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	int ret;
+
+	mutex_lock(&vwsnd_mutex);
+	mutex_lock(&devc->io_mutex);
+	ret = vwsnd_audio_do_ioctl(file, cmd, arg);
+	mutex_unlock(&devc->io_mutex);
+	mutex_unlock(&vwsnd_mutex);
+
+	return ret;
+}
+
+/* No mmap. */
+
+static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	DBGE("(file=0x%p, vma=0x%p)\n", file, vma);
+	return -ENODEV;
+}
+
+/*
+ * Open the audio device for read and/or write.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int vwsnd_audio_open(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc;
+	int minor = iminor(inode);
+	int sw_samplefmt;
+
+	DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
+
+	mutex_lock(&vwsnd_mutex);
+	INC_USE_COUNT;
+	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
+		if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
+			break;
+
+	if (devc == NULL) {
+		DEC_USE_COUNT;
+		mutex_unlock(&vwsnd_mutex);
+		return -ENODEV;
+	}
+
+	mutex_lock(&devc->open_mutex);
+	while (devc->open_mode & file->f_mode) {
+		mutex_unlock(&devc->open_mutex);
+		if (file->f_flags & O_NONBLOCK) {
+			DEC_USE_COUNT;
+			mutex_unlock(&vwsnd_mutex);
+			return -EBUSY;
+		}
+		interruptible_sleep_on(&devc->open_wait);
+		if (signal_pending(current)) {
+			DEC_USE_COUNT;
+			mutex_unlock(&vwsnd_mutex);
+			return -ERESTARTSYS;
+		}
+		mutex_lock(&devc->open_mutex);
+	}
+	devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	mutex_unlock(&devc->open_mutex);
+
+	/* get default sample format from minor number. */
+
+	sw_samplefmt = 0;
+	if ((minor & 0xF) == SND_DEV_DSP)
+		sw_samplefmt = AFMT_U8;
+	else if ((minor & 0xF) == SND_DEV_AUDIO)
+		sw_samplefmt = AFMT_MU_LAW;
+	else if ((minor & 0xF) == SND_DEV_DSP16)
+		sw_samplefmt = AFMT_S16_LE;
+	else
+		ASSERT(0);
+
+	/* Initialize vwsnd_ports. */
+
+	mutex_lock(&devc->io_mutex);
+	{
+		if (file->f_mode & FMODE_READ) {
+			devc->rport.swstate        = SW_INITIAL;
+			devc->rport.flags          = 0;
+			devc->rport.sw_channels    = 1;
+			devc->rport.sw_samplefmt   = sw_samplefmt;
+			devc->rport.sw_framerate   = 8000;
+			devc->rport.sw_fragshift   = DEFAULT_FRAGSHIFT;
+			devc->rport.sw_fragcount   = DEFAULT_FRAGCOUNT;
+			devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
+			devc->rport.byte_count     = 0;
+			devc->rport.frag_count     = 0;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			devc->wport.swstate        = SW_INITIAL;
+			devc->wport.flags          = 0;
+			devc->wport.sw_channels    = 1;
+			devc->wport.sw_samplefmt   = sw_samplefmt;
+			devc->wport.sw_framerate   = 8000;
+			devc->wport.sw_fragshift   = DEFAULT_FRAGSHIFT;
+			devc->wport.sw_fragcount   = DEFAULT_FRAGCOUNT;
+			devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
+			devc->wport.byte_count     = 0;
+			devc->wport.frag_count     = 0;
+		}
+	}
+	mutex_unlock(&devc->io_mutex);
+
+	file->private_data = devc;
+	DBGRV();
+	mutex_unlock(&vwsnd_mutex);
+	return 0;
+}
+
+/*
+ * Release (close) the audio device.
+ */
+
+static int vwsnd_audio_release(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *wport = NULL, *rport = NULL;
+	int err = 0;
+
+	mutex_lock(&vwsnd_mutex);
+	mutex_lock(&devc->io_mutex);
+	{
+		DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+
+		if (file->f_mode & FMODE_READ)
+			rport = &devc->rport;
+		if (file->f_mode & FMODE_WRITE) {
+			wport = &devc->wport;
+			pcm_flush_frag(devc);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		if (rport)
+			rport->swstate = SW_OFF;
+		if (wport)
+			wport->swstate = SW_OFF;
+	}
+	mutex_unlock(&devc->io_mutex);
+
+	mutex_lock(&devc->open_mutex);
+	{
+		devc->open_mode &= ~file->f_mode;
+	}
+	mutex_unlock(&devc->open_mutex);
+	wake_up(&devc->open_wait);
+	DEC_USE_COUNT;
+	DBGR();
+	mutex_unlock(&vwsnd_mutex);
+	return err;
+}
+
+static const struct file_operations vwsnd_audio_fops = {
+	.owner =	THIS_MODULE,
+	.llseek =	no_llseek,
+	.read =		vwsnd_audio_read,
+	.write =	vwsnd_audio_write,
+	.poll =		vwsnd_audio_poll,
+	.unlocked_ioctl = vwsnd_audio_ioctl,
+	.mmap =		vwsnd_audio_mmap,
+	.open =		vwsnd_audio_open,
+	.release =	vwsnd_audio_release,
+};
+
+/*****************************************************************************/
+/* mixer driver */
+
+/* open the mixer device. */
+
+static int vwsnd_mixer_open(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc;
+
+	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+
+	INC_USE_COUNT;
+	mutex_lock(&vwsnd_mutex);
+	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
+		if (devc->mixer_minor == iminor(inode))
+			break;
+
+	if (devc == NULL) {
+		DEC_USE_COUNT;
+		mutex_unlock(&vwsnd_mutex);
+		return -ENODEV;
+	}
+	file->private_data = devc;
+	mutex_unlock(&vwsnd_mutex);
+	return 0;
+}
+
+/* release (close) the mixer device. */
+
+static int vwsnd_mixer_release(struct inode *inode, struct file *file)
+{
+	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+	DEC_USE_COUNT;
+	return 0;
+}
+
+/* mixer_read_ioctl handles all read ioctls on the mixer device. */
+
+static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, void __user *arg)
+{
+	int val = -1;
+
+	DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
+
+	switch (nr) {
+	case SOUND_MIXER_CAPS:
+		val = SOUND_CAP_EXCL_INPUT;
+		break;
+
+	case SOUND_MIXER_DEVMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
+		break;
+
+	case SOUND_MIXER_STEREODEVS:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
+		break;
+
+	case SOUND_MIXER_OUTMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD);
+		break;
+
+	case SOUND_MIXER_RECMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD);
+		break;
+
+	case SOUND_MIXER_PCM:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM);
+		break;
+
+	case SOUND_MIXER_LINE:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE);
+		break;
+
+	case SOUND_MIXER_MIC:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC);
+		break;
+
+	case SOUND_MIXER_CD:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD);
+		break;
+
+	case SOUND_MIXER_RECLEV:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV);
+		break;
+
+	case SOUND_MIXER_RECSRC:
+		val = ad1843_get_recsrc(&devc->lith);
+		break;
+
+	case SOUND_MIXER_OUTSRC:
+		val = ad1843_get_outsrc(&devc->lith);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return put_user(val, (int __user *) arg);
+}
+
+/* mixer_write_ioctl handles all write ioctls on the mixer device. */
+
+static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, void __user *arg)
+{
+	int val;
+	int err;
+
+	DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
+
+	err = get_user(val, (int __user *) arg);
+	if (err)
+		return -EFAULT;
+	switch (nr) {
+	case SOUND_MIXER_PCM:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val);
+		break;
+
+	case SOUND_MIXER_LINE:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val);
+		break;
+
+	case SOUND_MIXER_MIC:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val);
+		break;
+
+	case SOUND_MIXER_CD:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val);
+		break;
+
+	case SOUND_MIXER_RECLEV:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val);
+		break;
+
+	case SOUND_MIXER_RECSRC:
+		if (devc->rport.swbuf || devc->wport.swbuf)
+			return -EBUSY;	/* can't change recsrc while running */
+		val = ad1843_set_recsrc(&devc->lith, val);
+		break;
+
+	case SOUND_MIXER_OUTSRC:
+		val = ad1843_set_outsrc(&devc->lith, val);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (val < 0)
+		return val;
+	return put_user(val, (int __user *) arg);
+}
+
+/* This is the ioctl entry to the mixer driver. */
+
+static long vwsnd_mixer_ioctl(struct file *file,
+			      unsigned int cmd,
+			      unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT;
+	const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT;
+	int retval;
+
+	DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
+
+	mutex_lock(&vwsnd_mutex);
+	mutex_lock(&devc->mix_mutex);
+	{
+		if ((cmd & ~nrmask) == MIXER_READ(0))
+			retval = mixer_read_ioctl(devc, nr, (void __user *) arg);
+		else if ((cmd & ~nrmask) == MIXER_WRITE(0))
+			retval = mixer_write_ioctl(devc, nr, (void __user *) arg);
+		else
+			retval = -EINVAL;
+	}
+	mutex_unlock(&devc->mix_mutex);
+	mutex_unlock(&vwsnd_mutex);
+	return retval;
+}
+
+static const struct file_operations vwsnd_mixer_fops = {
+	.owner =	THIS_MODULE,
+	.llseek =	no_llseek,
+	.unlocked_ioctl = vwsnd_mixer_ioctl,
+	.open =		vwsnd_mixer_open,
+	.release =	vwsnd_mixer_release,
+};
+
+/*****************************************************************************/
+/* probe/attach/unload */
+
+/* driver probe routine.  Return nonzero if hardware is found. */
+
+static int __init probe_vwsnd(struct address_info *hw_config)
+{
+	lithium_t lith;
+	int w;
+	unsigned long later;
+
+	DBGEV("(hw_config=0x%p)\n", hw_config);
+
+	/* XXX verify lithium present (to prevent crash on non-vw) */
+
+	if (li_create(&lith, hw_config->io_base) != 0) {
+		printk(KERN_WARNING "probe_vwsnd: can't map lithium\n");
+		return 0;
+	}
+	later = jiffies + 2;
+	li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
+	do {
+		w = li_readl(&lith, LI_HOST_CONTROLLER);
+	} while (w == LI_HC_LINK_ENABLE && time_before(jiffies, later));
+	
+	li_destroy(&lith);
+
+	DBGPV("HC = 0x%04x\n", w);
+
+	if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) {
+
+		/* This may indicate a beta machine with no audio,
+		 * or a future machine with different audio.
+		 * On beta-release 320 w/ no audio, HC == 0x4000 */
+
+		printk(KERN_WARNING "probe_vwsnd: audio codec not found\n");
+		return 0;
+	}
+
+	if (w & LI_HC_LINK_FAILURE) {
+		printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n");
+		return 0;
+	}
+
+	printk(KERN_INFO "vwsnd: lithium audio at mmio %#x irq %d\n",
+		hw_config->io_base, hw_config->irq);
+
+	return 1;
+}
+
+/*
+ * driver attach routine.  Initialize driver data structures and
+ * initialize hardware.  A new vwsnd_dev_t is allocated and put
+ * onto the global list, vwsnd_dev_list.
+ *
+ * Return +minor_dev on success, -errno on failure.
+ */
+
+static int __init attach_vwsnd(struct address_info *hw_config)
+{
+	vwsnd_dev_t *devc = NULL;
+	int err = -ENOMEM;
+
+	DBGEV("(hw_config=0x%p)\n", hw_config);
+
+	devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL);
+	if (devc == NULL)
+		goto fail0;
+
+	err = li_create(&devc->lith, hw_config->io_base);
+	if (err)
+		goto fail1;
+
+	init_waitqueue_head(&devc->open_wait);
+
+	devc->rport.hwbuf_size = HWBUF_SIZE;
+	devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
+	if (!devc->rport.hwbuf_vaddr)
+		goto fail2;
+	devc->rport.hwbuf = (void *) devc->rport.hwbuf_vaddr;
+	devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf);
+
+	/*
+	 * Quote from the NT driver:
+	 *
+	 * // WARNING!!! HACK to setup output dma!!!
+	 * // This is required because even on output there is some data
+	 * // trickling into the input DMA channel.  This is a bug in the
+	 * // Lithium microcode.
+	 * // --sde
+	 *
+	 * We set the input side's DMA base address here.  It will remain
+	 * valid until the driver is unloaded.
+	 */
+
+	li_writel(&devc->lith, LI_COMM1_BASE,
+		  devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8));
+
+	devc->wport.hwbuf_size = HWBUF_SIZE;
+	devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
+	if (!devc->wport.hwbuf_vaddr)
+		goto fail3;
+	devc->wport.hwbuf = (void *) devc->wport.hwbuf_vaddr;
+	devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf);
+	DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf);
+
+	DBGDO(shut_up++);
+	err = ad1843_init(&devc->lith);
+	DBGDO(shut_up--);
+	if (err)
+		goto fail4;
+
+	/* install interrupt handler */
+
+	err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc);
+	if (err)
+		goto fail5;
+
+	/* register this device's drivers. */
+
+	devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1);
+	if ((err = devc->audio_minor) < 0) {
+		DBGDO(printk(KERN_WARNING
+			     "attach_vwsnd: register_sound_dsp error %d\n",
+			     err));
+		goto fail6;
+	}
+	devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops,
+						 devc->audio_minor >> 4);
+	if ((err = devc->mixer_minor) < 0) {
+		DBGDO(printk(KERN_WARNING
+			     "attach_vwsnd: register_sound_mixer error %d\n",
+			     err));
+		goto fail7;
+	}
+
+	/* Squirrel away device indices for unload routine. */
+
+	hw_config->slots[0] = devc->audio_minor;
+
+	/* Initialize as much of *devc as possible */
+
+	mutex_init(&devc->open_mutex);
+	mutex_init(&devc->io_mutex);
+	mutex_init(&devc->mix_mutex);
+	devc->open_mode = 0;
+	spin_lock_init(&devc->rport.lock);
+	init_waitqueue_head(&devc->rport.queue);
+	devc->rport.swstate = SW_OFF;
+	devc->rport.hwstate = HW_STOPPED;
+	devc->rport.flags = 0;
+	devc->rport.swbuf = NULL;
+	spin_lock_init(&devc->wport.lock);
+	init_waitqueue_head(&devc->wport.queue);
+	devc->wport.swstate = SW_OFF;
+	devc->wport.hwstate = HW_STOPPED;
+	devc->wport.flags = 0;
+	devc->wport.swbuf = NULL;
+
+	/* Success.  Link us onto the local device list. */
+
+	devc->next_dev = vwsnd_dev_list;
+	vwsnd_dev_list = devc;
+	return devc->audio_minor;
+
+	/* So many ways to fail.  Undo what we did. */
+
+ fail7:
+	unregister_sound_dsp(devc->audio_minor);
+ fail6:
+	free_irq(hw_config->irq, devc);
+ fail5:
+ fail4:
+	free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
+ fail3:
+	free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
+ fail2:
+	li_destroy(&devc->lith);
+ fail1:
+	kfree(devc);
+ fail0:
+	return err;
+}
+
+static int __exit unload_vwsnd(struct address_info *hw_config)
+{
+	vwsnd_dev_t *devc, **devcp;
+
+	DBGE("()\n");
+
+	devcp = &vwsnd_dev_list;
+	while ((devc = *devcp)) {
+		if (devc->audio_minor == hw_config->slots[0]) {
+			*devcp = devc->next_dev;
+			break;
+		}
+		devcp = &devc->next_dev;
+	}
+
+	if (!devc)
+		return -ENODEV;
+
+	unregister_sound_mixer(devc->mixer_minor);
+	unregister_sound_dsp(devc->audio_minor);
+	free_irq(hw_config->irq, devc);
+	free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
+	free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
+	li_destroy(&devc->lith);
+	kfree(devc);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* initialization and loadable kernel module interface */
+
+static struct address_info the_hw_config = {
+	0xFF001000,			/* lithium phys addr  */
+	CO_IRQ(CO_APIC_LI_AUDIO)	/* irq */
+};
+
+MODULE_DESCRIPTION("SGI Visual Workstation sound module");
+MODULE_AUTHOR("Bob Miller <kbob@sgi.com>");
+MODULE_LICENSE("GPL");
+
+static int __init init_vwsnd(void)
+{
+	int err;
+
+	DBGXV("\n");
+	DBGXV("sound::vwsnd::init_module()\n");
+
+	if (!probe_vwsnd(&the_hw_config))
+		return -ENODEV;
+
+	err = attach_vwsnd(&the_hw_config);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static void __exit cleanup_vwsnd(void)
+{
+	DBGX("sound::vwsnd::cleanup_module()\n");
+
+	unload_vwsnd(&the_hw_config);
+}
+
+module_init(init_vwsnd);
+module_exit(cleanup_vwsnd);
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
new file mode 100644
index 00000000..24c430f7
--- /dev/null
+++ b/sound/oss/waveartist.c
@@ -0,0 +1,2024 @@
+/*
+ * linux/sound/oss/waveartist.c
+ *
+ * The low level driver for the RWA010 Rockwell Wave Artist
+ * codec chip used in the Rebel.com NetWinder.
+ *
+ * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
+ * and Pat Beirne (patb@corel.ca)
+ *
+ *
+ * Copyright (C) by Rebel.com 1998-1999
+ *
+ * RWA010 specs received under NDA from Rockwell
+ *
+ * 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.
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added __init to waveartist_init()
+ */
+
+/* Debugging */
+#define DEBUG_CMD	1
+#define DEBUG_OUT	2
+#define DEBUG_IN	4
+#define DEBUG_INTR	8
+#define DEBUG_MIXER	16
+#define DEBUG_TRIGGER	32
+
+#define debug_flg (0)
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+
+
+#include "sound_config.h"
+#include "waveartist.h"
+
+#ifdef CONFIG_ARM
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#endif
+
+#ifndef NO_DMA
+#define NO_DMA	255
+#endif
+
+#define SUPPORTED_MIXER_DEVICES		(SOUND_MASK_SYNTH      |\
+					 SOUND_MASK_PCM        |\
+					 SOUND_MASK_LINE       |\
+					 SOUND_MASK_MIC        |\
+					 SOUND_MASK_LINE1      |\
+					 SOUND_MASK_RECLEV     |\
+					 SOUND_MASK_VOLUME     |\
+					 SOUND_MASK_IMIX)
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] = {
+	0x5555,		/* Master Volume	 */
+	0x0000,		/* Bass			 */
+	0x0000,		/* Treble		 */
+	0x2323,		/* Synth (FM)		 */
+	0x4b4b,		/* PCM			 */
+	0x6464,		/* PC Speaker		 */
+	0x0000,		/* Ext Line		 */
+	0x0000,		/* Mic			 */
+	0x0000,		/* CD			 */
+	0x6464,		/* Recording monitor	 */
+	0x0000,		/* SB PCM (ALT PCM)	 */
+	0x0000,		/* Recording level	 */
+	0x6464,		/* Input gain		 */
+	0x6464,		/* Output gain		 */
+	0x0000,		/* Line1 (Aux1)		 */
+	0x0000,		/* Line2 (Aux2)		 */
+	0x0000,		/* Line3 (Aux3)		 */
+	0x0000,		/* Digital1		 */
+	0x0000,		/* Digital2		 */
+	0x0000,		/* Digital3		 */
+	0x0000,		/* Phone In		 */
+	0x6464,		/* Phone Out		 */
+	0x0000,		/* Video		 */
+	0x0000,		/* Radio		 */
+	0x0000		/* Monitor		 */
+};
+
+typedef struct {
+	struct address_info  hw;	/* hardware */
+	char		*chip_name;
+
+	int		xfer_count;
+	int		audio_mode;
+	int		open_mode;
+	int		audio_flags;
+	int		record_dev;
+	int		playback_dev;
+	int		dev_no;
+
+	/* Mixer parameters */
+	const struct waveartist_mixer_info *mix;
+
+	unsigned short	*levels;	   /* cache of volume settings   */
+	int		recmask;	   /* currently enabled recording device! */
+
+#ifdef CONFIG_ARCH_NETWINDER
+	signed int	slider_vol;	   /* hardware slider volume     */
+	unsigned int	handset_detect	:1;
+	unsigned int	telephone_detect:1;
+	unsigned int	no_autoselect	:1;/* handset/telephone autoselects a path */
+	unsigned int	spkr_mute_state	:1;/* set by ioctl or autoselect */
+	unsigned int	line_mute_state	:1;/* set by ioctl or autoselect */
+	unsigned int	use_slider	:1;/* use slider setting for o/p vol */
+#endif
+} wavnc_info;
+
+/*
+ * This is the implementation specific mixer information.
+ */
+struct waveartist_mixer_info {
+	unsigned int	supported_devs;	   /* Supported devices */
+	unsigned int	recording_devs;	   /* Recordable devies */
+	unsigned int	stereo_devs;	   /* Stereo devices	*/
+
+	unsigned int	(*select_input)(wavnc_info *, unsigned int,
+					unsigned char *, unsigned char *);
+	int		(*decode_mixer)(wavnc_info *, int,
+					unsigned char, unsigned char);
+	int		(*get_mixer)(wavnc_info *, int);
+};
+
+typedef struct wavnc_port_info {
+	int		open_mode;
+	int		speed;
+	int		channels;
+	int		audio_format;
+} wavnc_port_info;
+
+static int		nr_waveartist_devs;
+static wavnc_info	adev_info[MAX_AUDIO_DEV];
+static DEFINE_SPINLOCK(waveartist_lock);
+
+#ifndef CONFIG_ARCH_NETWINDER
+#define machine_is_netwinder() 0
+#else
+static struct timer_list vnc_timer;
+static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask);
+static int vnc_private_ioctl(int dev, unsigned int cmd, int __user *arg);
+static void vnc_slider_tick(unsigned long data);
+#endif
+
+static inline void
+waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
+{
+	unsigned int ctlr_port = hw->io_base + CTLR;
+
+	clear = ~clear & inb(ctlr_port);
+
+	outb(clear | set, ctlr_port);
+}
+
+/* Toggle IRQ acknowledge line
+ */
+static inline void
+waveartist_iack(wavnc_info *devc)
+{
+	unsigned int ctlr_port = devc->hw.io_base + CTLR;
+	int old_ctlr;
+
+	old_ctlr = inb(ctlr_port) & ~IRQ_ACK;
+
+	outb(old_ctlr | IRQ_ACK, ctlr_port);
+	outb(old_ctlr, ctlr_port);
+}
+
+static inline int
+waveartist_sleep(int timeout_ms)
+{
+	unsigned int timeout = msecs_to_jiffies(timeout_ms*100);
+	return schedule_timeout_interruptible(timeout);
+}
+
+static int
+waveartist_reset(wavnc_info *devc)
+{
+	struct address_info *hw = &devc->hw;
+	unsigned int timeout, res = -1;
+
+	waveartist_set_ctlr(hw, -1, RESET);
+	waveartist_sleep(2);
+	waveartist_set_ctlr(hw, RESET, 0);
+
+	timeout = 500;
+	do {
+		mdelay(2);
+
+		if (inb(hw->io_base + STATR) & CMD_RF) {
+			res = inw(hw->io_base + CMDR);
+			if (res == 0x55aa)
+				break;
+		}
+	} while (--timeout);
+
+	if (timeout == 0) {
+		printk(KERN_WARNING "WaveArtist: reset timeout ");
+		if (res != (unsigned int)-1)
+			printk("(res=%04X)", res);
+		printk("\n");
+		return 1;
+	}
+	return 0;
+}
+
+/* Helper function to send and receive words
+ * from WaveArtist.  It handles all the handshaking
+ * and can send or receive multiple words.
+ */
+static int
+waveartist_cmd(wavnc_info *devc,
+		int nr_cmd, unsigned int *cmd,
+		int nr_resp, unsigned int *resp)
+{
+	unsigned int io_base = devc->hw.io_base;
+	unsigned int timed_out = 0;
+	unsigned int i;
+
+	if (debug_flg & DEBUG_CMD) {
+		printk("waveartist_cmd: cmd=");
+
+		for (i = 0; i < nr_cmd; i++)
+			printk("%04X ", cmd[i]);
+
+		printk("\n");
+	}
+
+	if (inb(io_base + STATR) & CMD_RF) {
+		int old_data;
+
+		/* flush the port
+		 */
+
+		old_data = inw(io_base + CMDR);
+
+		if (debug_flg & DEBUG_CMD)
+			printk("flushed %04X...", old_data);
+
+		udelay(10);
+	}
+
+	for (i = 0; !timed_out && i < nr_cmd; i++) {
+		int count;
+
+		for (count = 5000; count; count--)
+			if (inb(io_base + STATR) & CMD_WE)
+				break;
+
+		if (!count)
+			timed_out = 1;
+		else
+			outw(cmd[i], io_base + CMDR);
+	}
+
+	for (i = 0; !timed_out && i < nr_resp; i++) {
+		int count;
+
+		for (count = 5000; count; count--)
+			if (inb(io_base + STATR) & CMD_RF)
+				break;
+
+		if (!count)
+			timed_out = 1;
+		else
+			resp[i] = inw(io_base + CMDR);
+	}
+
+	if (debug_flg & DEBUG_CMD) {
+		if (!timed_out) {
+			printk("waveartist_cmd: resp=");
+
+			for (i = 0; i < nr_resp; i++)
+				printk("%04X ", resp[i]);
+
+			printk("\n");
+		} else
+			printk("waveartist_cmd: timed out\n");
+	}
+
+	return timed_out ? 1 : 0;
+}
+
+/*
+ * Send one command word
+ */
+static inline int
+waveartist_cmd1(wavnc_info *devc, unsigned int cmd)
+{
+	return waveartist_cmd(devc, 1, &cmd, 0, NULL);
+}
+
+/*
+ * Send one command, receive one word
+ */
+static inline unsigned int
+waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd)
+{
+	unsigned int ret;
+
+	waveartist_cmd(devc, 1, &cmd, 1, &ret);
+
+	return ret;
+}
+
+/*
+ * Send a double command, receive one
+ * word (and throw it away)
+ */
+static inline int
+waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg)
+{
+	unsigned int vals[2];
+
+	vals[0] = cmd;
+	vals[1] = arg;
+
+	return waveartist_cmd(devc, 2, vals, 1, vals);
+}
+
+/*
+ * Send a triple command
+ */
+static inline int
+waveartist_cmd3(wavnc_info *devc, unsigned int cmd,
+		unsigned int arg1, unsigned int arg2)
+{
+	unsigned int vals[3];
+
+	vals[0] = cmd;
+	vals[1] = arg1;
+	vals[2] = arg2;
+
+	return waveartist_cmd(devc, 3, vals, 0, NULL);
+}
+
+static int
+waveartist_getrev(wavnc_info *devc, char *rev)
+{
+	unsigned int temp[2];
+	unsigned int cmd = WACMD_GETREV;
+
+	waveartist_cmd(devc, 1, &cmd, 2, temp);
+
+	rev[0] = temp[0] >> 8;
+	rev[1] = temp[0] & 255;
+	rev[2] = '\0';
+
+	return temp[0];
+}
+
+static void waveartist_halt_output(int dev);
+static void waveartist_halt_input(int dev);
+static void waveartist_halt(int dev);
+static void waveartist_trigger(int dev, int state);
+
+static int
+waveartist_open(int dev, int mode)
+{
+	wavnc_info	*devc;
+	wavnc_port_info	*portc;
+	unsigned long	flags;
+
+	if (dev < 0 || dev >= num_audiodevs)
+		return -ENXIO;
+
+	devc  = (wavnc_info *) audio_devs[dev]->devc;
+	portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+	if (portc->open_mode || (devc->open_mode & mode)) {
+		spin_unlock_irqrestore(&waveartist_lock, flags);
+		return -EBUSY;
+	}
+
+	devc->audio_mode  = 0;
+	devc->open_mode  |= mode;
+	portc->open_mode  = mode;
+	waveartist_trigger(dev, 0);
+
+	if (mode & OPEN_READ)
+		devc->record_dev = dev;
+	if (mode & OPEN_WRITE)
+		devc->playback_dev = dev;
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+
+	return 0;
+}
+
+static void
+waveartist_close(int dev)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	waveartist_halt(dev);
+
+	devc->audio_mode = 0;
+	devc->open_mode &= ~portc->open_mode;
+	portc->open_mode = 0;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag)
+{
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+	unsigned int	count = __count; 
+
+	if (debug_flg & DEBUG_OUT)
+		printk("waveartist: output block, buf=0x%lx, count=0x%x...\n",
+			buf, count);
+	/*
+	 * 16 bit data
+	 */
+	if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))
+		count >>= 1;
+
+	if (portc->channels > 1)
+		count >>= 1;
+
+	count -= 1;
+
+	if (devc->audio_mode & PCM_ENABLE_OUTPUT &&
+	    audio_devs[dev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    count == devc->xfer_count) {
+		devc->audio_mode |= PCM_ENABLE_OUTPUT;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	/*
+	 * set sample count
+	 */
+	waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count);
+
+	devc->xfer_count = count;
+	devc->audio_mode |= PCM_ENABLE_OUTPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+	unsigned int	count = __count;
+
+	if (debug_flg & DEBUG_IN)
+		printk("waveartist: start input, buf=0x%lx, count=0x%x...\n",
+			buf, count);
+
+	if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+		count >>= 1;
+
+	if (portc->channels > 1)
+		count >>= 1;
+
+	count -= 1;
+
+	if (devc->audio_mode & PCM_ENABLE_INPUT &&
+	    audio_devs[dev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    count == devc->xfer_count) {
+		devc->audio_mode |= PCM_ENABLE_INPUT;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	/*
+	 * set sample count
+	 */
+	waveartist_cmd2(devc, WACMD_INPUTSIZE, count);
+
+	devc->xfer_count = count;
+	devc->audio_mode |= PCM_ENABLE_INPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static int
+waveartist_ioctl(int dev, unsigned int cmd, void __user * arg)
+{
+	return -EINVAL;
+}
+
+static unsigned int
+waveartist_get_speed(wavnc_port_info *portc)
+{
+	unsigned int speed;
+
+	/*
+	 * program the speed, channels, bits
+	 */
+	if (portc->speed == 8000)
+		speed = 0x2E71;
+	else if (portc->speed == 11025)
+		speed = 0x4000;
+	else if (portc->speed == 22050)
+		speed = 0x8000;
+	else if (portc->speed == 44100)
+		speed = 0x0;
+	else {
+		/*
+		 * non-standard - just calculate
+		 */
+		speed = portc->speed << 16;
+
+		speed = (speed / 44100) & 65535;
+	}
+
+	return speed;
+}
+
+static unsigned int
+waveartist_get_bits(wavnc_port_info *portc)
+{
+	unsigned int bits;
+
+	if (portc->audio_format == AFMT_S16_LE)
+		bits = 1;
+	else if (portc->audio_format == AFMT_S8)
+		bits = 0;
+	else
+		bits = 2;	//default AFMT_U8
+
+	return bits;
+}
+
+static int
+waveartist_prepare_for_input(int dev, int bsize, int bcount)
+{
+	unsigned long	flags;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned int	speed, bits;
+
+	if (devc->audio_mode)
+		return 0;
+
+	speed = waveartist_get_speed(portc);
+	bits  = waveartist_get_bits(portc);
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
+		printk(KERN_WARNING "waveartist: error setting the "
+		       "record format to %d\n", portc->audio_format);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels))
+		printk(KERN_WARNING "waveartist: error setting record "
+		       "to %d channels\n", portc->channels);
+
+	/*
+	 * write cmd SetSampleSpeedTimeConstant
+	 */
+	if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed))
+		printk(KERN_WARNING "waveartist: error setting the record "
+		       "speed to %dHz.\n", portc->speed);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1))
+		printk(KERN_WARNING "waveartist: error setting the record "
+		       "data path to 0x%X\n", 1);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
+		printk(KERN_WARNING "waveartist: error setting the record "
+		       "format to %d\n", portc->audio_format);
+
+	devc->xfer_count = 0;
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+	waveartist_halt_input(dev);
+
+	if (debug_flg & DEBUG_INTR) {
+		printk("WA CTLR reg: 0x%02X.\n",
+		       inb(devc->hw.io_base + CTLR));
+		printk("WA STAT reg: 0x%02X.\n",
+		       inb(devc->hw.io_base + STATR));
+		printk("WA IRQS reg: 0x%02X.\n",
+		       inb(devc->hw.io_base + IRQSTAT));
+	}
+
+	return 0;
+}
+
+static int
+waveartist_prepare_for_output(int dev, int bsize, int bcount)
+{
+	unsigned long	flags;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned int	speed, bits;
+
+	/*
+	 * program the speed, channels, bits
+	 */
+	speed = waveartist_get_speed(portc);
+	bits  = waveartist_get_bits(portc);
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) &&
+	    waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "speed to %dHz.\n", portc->speed);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "to %d channels\n", portc->channels);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "data path to 0x%X\n", 0);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "format to %d\n", portc->audio_format);
+
+	devc->xfer_count = 0;
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+	waveartist_halt_output(dev);
+
+	if (debug_flg & DEBUG_INTR) {
+		printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR));
+		printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR));
+		printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT));
+	}
+
+	return 0;
+}
+
+static void
+waveartist_halt(int dev)
+{
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	wavnc_info	*devc;
+
+	if (portc->open_mode & OPEN_WRITE)
+		waveartist_halt_output(dev);
+
+	if (portc->open_mode & OPEN_READ)
+		waveartist_halt_input(dev);
+
+	devc = (wavnc_info *) audio_devs[dev]->devc;
+	devc->audio_mode = 0;
+}
+
+static void
+waveartist_halt_input(int dev)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	/*
+	 * Stop capture
+	 */
+	waveartist_cmd1(devc, WACMD_INPUTSTOP);
+
+	devc->audio_mode &= ~PCM_ENABLE_INPUT;
+
+	/*
+	 * Clear interrupt by toggling
+	 * the IRQ_ACK bit in CTRL
+	 */
+	if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
+		waveartist_iack(devc);
+
+//	devc->audio_mode &= ~PCM_ENABLE_INPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_halt_output(int dev)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	waveartist_cmd1(devc, WACMD_OUTPUTSTOP);
+
+	devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+
+	/*
+	 * Clear interrupt by toggling
+	 * the IRQ_ACK bit in CTRL
+	 */
+	if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
+		waveartist_iack(devc);
+
+//	devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_trigger(int dev, int state)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned long	flags;
+
+	if (debug_flg & DEBUG_TRIGGER) {
+		printk("wavnc: audio trigger ");
+		if (state & PCM_ENABLE_INPUT)
+			printk("in ");
+		if (state & PCM_ENABLE_OUTPUT)
+			printk("out");
+		printk("\n");
+	}
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	state &= devc->audio_mode;
+
+	if (portc->open_mode & OPEN_READ &&
+	    state & PCM_ENABLE_INPUT)
+		/*
+		 * enable ADC Data Transfer to PC
+		 */
+		waveartist_cmd1(devc, WACMD_INPUTSTART);
+
+	if (portc->open_mode & OPEN_WRITE &&
+	    state & PCM_ENABLE_OUTPUT)
+		/*
+		 * enable DAC data transfer from PC
+		 */
+		waveartist_cmd1(devc, WACMD_OUTPUTSTART);
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static int
+waveartist_set_speed(int dev, int arg)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	if (arg <= 0)
+		return portc->speed;
+
+	if (arg < 5000)
+		arg = 5000;
+	if (arg > 44100)
+		arg = 44100;
+
+	portc->speed = arg;
+	return portc->speed;
+
+}
+
+static short
+waveartist_set_channels(int dev, short arg)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	if (arg != 1 && arg != 2)
+		return portc->channels;
+
+	portc->channels = arg;
+	return arg;
+}
+
+static unsigned int
+waveartist_set_bits(int dev, unsigned int arg)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	if (arg == 0)
+		return portc->audio_format;
+
+	if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8))
+		arg = AFMT_U8;
+
+	portc->audio_format = arg;
+
+	return arg;
+}
+
+static struct audio_driver waveartist_audio_driver = {
+	.owner			= THIS_MODULE,
+	.open			= waveartist_open,
+	.close			= waveartist_close,
+	.output_block		= waveartist_output_block,
+	.start_input		= waveartist_start_input,
+	.ioctl			= waveartist_ioctl,
+	.prepare_for_input	= waveartist_prepare_for_input,
+	.prepare_for_output	= waveartist_prepare_for_output,
+	.halt_io		= waveartist_halt,
+	.halt_input		= waveartist_halt_input,
+	.halt_output		= waveartist_halt_output,
+	.trigger		= waveartist_trigger,
+	.set_speed		= waveartist_set_speed,
+	.set_bits		= waveartist_set_bits,
+	.set_channels		= waveartist_set_channels
+};
+
+
+static irqreturn_t
+waveartist_intr(int irq, void *dev_id)
+{
+	wavnc_info *devc = dev_id;
+	int	   irqstatus, status;
+
+	spin_lock(&waveartist_lock);
+	irqstatus = inb(devc->hw.io_base + IRQSTAT);
+	status    = inb(devc->hw.io_base + STATR);
+
+	if (debug_flg & DEBUG_INTR)
+		printk("waveartist_intr: stat=%02x, irqstat=%02x\n",
+		       status, irqstatus);
+
+	if (status & IRQ_REQ)	/* Clear interrupt */
+		waveartist_iack(devc);
+	else
+		printk(KERN_WARNING "waveartist: unexpected interrupt\n");
+
+	if (irqstatus & 0x01) {
+		int temp = 1;
+
+		/* PCM buffer done
+		 */
+		if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) {
+			DMAbuf_outputintr(devc->playback_dev, 1);
+			temp = 0;
+		}
+		if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) {
+			DMAbuf_inputintr(devc->record_dev);
+			temp = 0;
+		}
+		if (temp)	//default:
+			printk(KERN_WARNING "waveartist: Unknown interrupt\n");
+	}
+	if (irqstatus & 0x2)
+		// We do not use SB mode natively...
+		printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n");
+	spin_unlock(&waveartist_lock);
+	return IRQ_HANDLED;
+}
+
+/* -------------------------------------------------------------------------
+ * Mixer stuff
+ */
+struct mix_ent {
+	unsigned char	reg_l;
+	unsigned char	reg_r;
+	unsigned char	shift;
+	unsigned char	max;
+};
+
+static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = {
+	{ 2, 6, 1,  7 }, /* SOUND_MIXER_VOLUME   */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_BASS     */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_TREBLE   */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_SYNTH    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_PCM      */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_SPEAKER  */
+	{ 0, 4, 6, 31 }, /* SOUND_MIXER_LINE     */
+	{ 2, 6, 4,  3 }, /* SOUND_MIXER_MIC      */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_CD       */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_IMIX     */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_ALTPCM   */
+#if 0
+	{ 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV   */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_IGAIN    */
+#else
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_RECLEV   */
+	{ 3, 7, 0,  7 }, /* SOUND_MIXER_IGAIN    */
+#endif
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_OGAIN    */
+	{ 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1    */
+	{ 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_LINE3    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_DIGITAL1 */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_DIGITAL2 */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_DIGITAL3 */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_PHONEIN  */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_PHONEOUT */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_VIDEO    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_RADIO    */
+	{ 0, 0, 0,  0 }  /* SOUND_MIXER_MONITOR  */
+};
+
+static void
+waveartist_mixer_update(wavnc_info *devc, int whichDev)
+{
+	unsigned int lev_left, lev_right;
+
+	lev_left  = devc->levels[whichDev] & 0xff;
+	lev_right = devc->levels[whichDev] >> 8;
+
+	if (lev_left > 100)
+		lev_left = 100;
+	if (lev_right > 100)
+		lev_right = 100;
+
+#define SCALE(lev,max)	((lev) * (max) / 100)
+
+	if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT)
+		whichDev = SOUND_MIXER_VOLUME;
+
+	if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) {
+		const struct mix_ent *mix = mix_devs + whichDev;
+		unsigned int mask, left, right;
+
+		mask = mix->max << mix->shift;
+		lev_left  = SCALE(lev_left,  mix->max) << mix->shift;
+		lev_right = SCALE(lev_right, mix->max) << mix->shift;
+
+		/* read left setting */
+		left  = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
+					       mix->reg_l << 8);
+
+		/* read right setting */
+		right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
+						mix->reg_r << 8);
+
+		left  = (left  & ~mask) | (lev_left  & mask);
+		right = (right & ~mask) | (lev_right & mask);
+
+		/* write left,right back */
+		waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
+	} else {
+		switch(whichDev) {
+		case SOUND_MIXER_PCM:
+			waveartist_cmd3(devc, WACMD_SET_LEVEL,
+					SCALE(lev_left,  32767),
+					SCALE(lev_right, 32767));
+			break;
+
+		case SOUND_MIXER_SYNTH:
+			waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL,
+					SCALE(lev_left,  32767),
+					SCALE(lev_right, 32767));
+			break;
+		}
+	}
+}
+
+/*
+ * Set the ADC MUX to the specified values.  We do NOT do any
+ * checking of the values passed, since we assume that the
+ * relevant *_select_input function has done that for us.
+ */
+static void
+waveartist_set_adc_mux(wavnc_info *devc, char left_dev, char right_dev)
+{
+	unsigned int reg_08, reg_09;
+
+	reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800);
+	reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900);
+
+	reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev;
+
+	waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09);
+}
+
+/*
+ * Decode a recording mask into a mixer selection as follows:
+ *
+ *     OSS Source	WA Source	Actual source
+ *  SOUND_MASK_IMIX	Mixer		Mixer output (same as AD1848)
+ *  SOUND_MASK_LINE	Line		Line in
+ *  SOUND_MASK_LINE1	Aux 1		Aux 1 in
+ *  SOUND_MASK_LINE2	Aux 2		Aux 2 in
+ *  SOUND_MASK_MIC	Mic		Microphone
+ */
+static unsigned int
+waveartist_select_input(wavnc_info *devc, unsigned int recmask,
+			unsigned char *dev_l, unsigned char *dev_r)
+{
+	unsigned int recdev = ADC_MUX_NONE;
+
+	if (recmask & SOUND_MASK_IMIX) {
+		recmask = SOUND_MASK_IMIX;
+		recdev = ADC_MUX_MIXER;
+	} else if (recmask & SOUND_MASK_LINE2) {
+		recmask = SOUND_MASK_LINE2;
+		recdev = ADC_MUX_AUX2;
+	} else if (recmask & SOUND_MASK_LINE1) {
+		recmask = SOUND_MASK_LINE1;
+		recdev = ADC_MUX_AUX1;
+	} else if (recmask & SOUND_MASK_LINE) {
+		recmask = SOUND_MASK_LINE;
+		recdev = ADC_MUX_LINE;
+	} else if (recmask & SOUND_MASK_MIC) {
+		recmask = SOUND_MASK_MIC;
+		recdev = ADC_MUX_MIC;
+	}
+
+	*dev_l = *dev_r = recdev;
+
+	return recmask;
+}
+
+static int
+waveartist_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
+			unsigned char lev_r)
+{
+	switch (dev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_SYNTH:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_MIC:
+	case SOUND_MIXER_IGAIN:
+	case SOUND_MIXER_LINE1:
+	case SOUND_MIXER_LINE2:
+		devc->levels[dev] = lev_l | lev_r << 8;
+		break;
+
+	case SOUND_MIXER_IMIX:
+		break;
+
+	default:
+		dev = -EINVAL;
+		break;
+	}
+
+	return dev;
+}
+
+static int waveartist_get_mixer(wavnc_info *devc, int dev)
+{
+	return devc->levels[dev];
+}
+
+static const struct waveartist_mixer_info waveartist_mixer = {
+	.supported_devs	= SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN,
+	.recording_devs	= SOUND_MASK_LINE  | SOUND_MASK_MIC   |
+			SOUND_MASK_LINE1 | SOUND_MASK_LINE2 |
+			SOUND_MASK_IMIX,
+	.stereo_devs	= (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~
+			(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX),
+	.select_input	= waveartist_select_input,
+	.decode_mixer	= waveartist_decode_mixer,
+	.get_mixer	= waveartist_get_mixer,
+};
+
+static void
+waveartist_set_recmask(wavnc_info *devc, unsigned int recmask)
+{
+	unsigned char dev_l, dev_r;
+
+	recmask &= devc->mix->recording_devs;
+
+	/*
+	 * If more than one recording device selected,
+	 * disable the device that is currently in use.
+	 */
+	if (hweight32(recmask) > 1)
+		recmask &= ~devc->recmask;
+
+	/*
+	 * Translate the recording device mask into
+	 * the ADC multiplexer settings.
+	 */
+	devc->recmask = devc->mix->select_input(devc, recmask,
+						&dev_l, &dev_r);
+
+	waveartist_set_adc_mux(devc, dev_l, dev_r);
+}
+
+static int
+waveartist_set_mixer(wavnc_info *devc, int dev, unsigned int level)
+{
+	unsigned int lev_left  = level & 0x00ff;
+	unsigned int lev_right = (level & 0xff00) >> 8;
+
+	if (lev_left > 100)
+		lev_left = 100;
+	if (lev_right > 100)
+		lev_right = 100;
+
+	/*
+	 * Mono devices have their right volume forced to their
+	 * left volume.  (from ALSA driver OSS emulation).
+	 */
+	if (!(devc->mix->stereo_devs & (1 << dev)))
+		lev_right = lev_left;
+
+	dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right);
+
+	if (dev >= 0)
+		waveartist_mixer_update(devc, dev);
+
+	return dev < 0 ? dev : 0;
+}
+
+static int
+waveartist_mixer_ioctl(int dev, unsigned int cmd, void __user * arg)
+{
+	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+	int ret = 0, val, nr;
+
+	/*
+	 * All SOUND_MIXER_* ioctls use type 'M'
+	 */
+	if (((cmd >> 8) & 255) != 'M')
+		return -ENOIOCTLCMD;
+
+#ifdef CONFIG_ARCH_NETWINDER
+	if (machine_is_netwinder()) {
+		ret = vnc_private_ioctl(dev, cmd, arg);
+		if (ret != -ENOIOCTLCMD)
+			return ret;
+		else
+			ret = 0;
+	}
+#endif
+
+	nr = cmd & 0xff;
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		if (get_user(val, (int __user *)arg))
+			return -EFAULT;
+
+		switch (nr) {
+		case SOUND_MIXER_RECSRC:
+			waveartist_set_recmask(devc, val);
+			break;
+
+		default:
+			ret = -EINVAL;
+			if (nr < SOUND_MIXER_NRDEVICES &&
+			    devc->mix->supported_devs & (1 << nr))
+				ret = waveartist_set_mixer(devc, nr, val);
+		}
+	}
+
+	if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) {
+		ret = -EINVAL;
+
+		switch (nr) {
+		case SOUND_MIXER_RECSRC:
+			ret = devc->recmask;
+			break;
+
+		case SOUND_MIXER_DEVMASK:
+			ret = devc->mix->supported_devs;
+			break;
+
+		case SOUND_MIXER_STEREODEVS:
+			ret = devc->mix->stereo_devs;
+			break;
+
+		case SOUND_MIXER_RECMASK:
+			ret = devc->mix->recording_devs;
+			break;
+
+		case SOUND_MIXER_CAPS:
+			ret = SOUND_CAP_EXCL_INPUT;
+			break;
+
+		default:
+			if (nr < SOUND_MIXER_NRDEVICES)
+				ret = devc->mix->get_mixer(devc, nr);
+			break;
+		}
+
+		if (ret >= 0)
+			ret = put_user(ret, (int __user *)arg) ? -EFAULT : 0;
+	}
+
+	return ret;
+}
+
+static struct mixer_operations waveartist_mixer_operations =
+{
+	.owner	= THIS_MODULE,
+	.id	= "WaveArtist",
+	.name	= "WaveArtist",
+	.ioctl	= waveartist_mixer_ioctl
+};
+
+static void
+waveartist_mixer_reset(wavnc_info *devc)
+{
+	int i;
+
+	if (debug_flg & DEBUG_MIXER)
+		printk("%s: mixer_reset\n", devc->hw.name);
+
+	/*
+	 * reset mixer cmd
+	 */
+	waveartist_cmd1(devc, WACMD_RST_MIXER);
+
+	/*
+	 * set input for ADC to come from 'quiet'
+	 * turn on default modes
+	 */
+	waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836);
+
+	/*
+	 * set mixer input select to none, RX filter gains 0 dB
+	 */
+	waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00);
+
+	/*
+	 * set bit 0 reg 2 to 1 - unmute MonoOut
+	 */
+	waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800);
+
+	/* set default input device = internal mic
+	 * current recording device = none
+	 */
+	waveartist_set_recmask(devc, 0);
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		waveartist_mixer_update(devc, i);
+}
+
+static int __init waveartist_init(wavnc_info *devc)
+{
+	wavnc_port_info *portc;
+	char rev[3], dev_name[64];
+	int my_dev;
+
+	if (waveartist_reset(devc))
+		return -ENODEV;
+
+	sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name);
+
+	if (waveartist_getrev(devc, rev)) {
+		strcat(dev_name, " rev. ");
+		strcat(dev_name, rev);
+	}
+	strcat(dev_name, ")");
+
+	conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq,
+		     devc->hw.dma, devc->hw.dma2);
+
+	portc = kzalloc(sizeof(wavnc_port_info), GFP_KERNEL);
+	if (portc == NULL)
+		goto nomem;
+
+	my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name,
+			&waveartist_audio_driver, sizeof(struct audio_driver),
+			devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8,
+			devc, devc->hw.dma, devc->hw.dma2);
+
+	if (my_dev < 0)
+		goto free;
+
+	audio_devs[my_dev]->portc = portc;
+
+	waveartist_mixer_reset(devc);
+
+	/*
+	 * clear any pending interrupt
+	 */
+	waveartist_iack(devc);
+
+	if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) {
+		printk(KERN_ERR "%s: IRQ %d in use\n",
+			devc->hw.name, devc->hw.irq);
+		goto uninstall;
+	}
+
+	if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) {
+		printk(KERN_ERR "%s: Can't allocate DMA%d\n",
+			devc->hw.name, devc->hw.dma);
+		goto uninstall_irq;
+	}
+
+	if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA)
+		if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) {
+			printk(KERN_ERR "%s: can't allocate DMA%d\n",
+				devc->hw.name, devc->hw.dma2);
+			goto uninstall_dma;
+		}
+
+	waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE);
+
+	audio_devs[my_dev]->mixer_dev =
+		sound_install_mixer(MIXER_DRIVER_VERSION,
+				dev_name,
+				&waveartist_mixer_operations,
+				sizeof(struct mixer_operations),
+				devc);
+
+	return my_dev;
+
+uninstall_dma:
+	sound_free_dma(devc->hw.dma);
+
+uninstall_irq:
+	free_irq(devc->hw.irq, devc);
+
+uninstall:
+	sound_unload_audiodev(my_dev);
+
+free:
+	kfree(portc);
+
+nomem:
+	return -1;
+}
+
+static int __init probe_waveartist(struct address_info *hw_config)
+{
+	wavnc_info *devc = &adev_info[nr_waveartist_devs];
+
+	if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
+		printk(KERN_WARNING "waveartist: too many audio devices\n");
+		return 0;
+	}
+
+	if (!request_region(hw_config->io_base, 15, hw_config->name))  {
+		printk(KERN_WARNING "WaveArtist: I/O port conflict\n");
+		return 0;
+	}
+
+	if (hw_config->irq > 15 || hw_config->irq < 0) {
+		release_region(hw_config->io_base, 15);
+		printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n",
+		       hw_config->irq);
+		return 0;
+	}
+
+	if (hw_config->dma != 3) {
+		release_region(hw_config->io_base, 15);
+		printk(KERN_WARNING "WaveArtist: Bad DMA %d\n",
+		       hw_config->dma);
+		return 0;
+	}
+
+	hw_config->name = "WaveArtist";
+	devc->hw = *hw_config;
+	devc->open_mode = 0;
+	devc->chip_name = "RWA-010";
+
+	return 1;
+}
+
+static void __init
+attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix)
+{
+	wavnc_info *devc = &adev_info[nr_waveartist_devs];
+
+	/*
+	 * 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).
+	 */
+	devc->hw = *hw;
+	devc->hw.irq = (hw->irq > 0) ? hw->irq : 0;
+	devc->open_mode = 0;
+	devc->playback_dev = 0;
+	devc->record_dev = 0;
+	devc->audio_flags = DMA_AUTOMODE;
+	devc->levels = levels;
+
+	if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA)
+		devc->audio_flags |= DMA_DUPLEX;
+
+	devc->mix = mix;
+	devc->dev_no = waveartist_init(devc);
+
+	if (devc->dev_no < 0)
+		release_region(hw->io_base, 15);
+	else {
+#ifdef CONFIG_ARCH_NETWINDER
+		if (machine_is_netwinder()) {
+			init_timer(&vnc_timer);
+			vnc_timer.function = vnc_slider_tick;
+			vnc_timer.expires  = jiffies;
+			vnc_timer.data     = nr_waveartist_devs;
+			add_timer(&vnc_timer);
+
+			vnc_configure_mixer(devc, 0);
+
+			devc->no_autoselect = 1;
+		}
+#endif
+		nr_waveartist_devs += 1;
+	}
+}
+
+static void __exit unload_waveartist(struct address_info *hw)
+{
+	wavnc_info *devc = NULL;
+	int i;
+
+	for (i = 0; i < nr_waveartist_devs; i++)
+		if (hw->io_base == adev_info[i].hw.io_base) {
+			devc = adev_info + i;
+			break;
+		}
+
+	if (devc != NULL) {
+		int mixer;
+
+#ifdef CONFIG_ARCH_NETWINDER
+		if (machine_is_netwinder())
+			del_timer(&vnc_timer);
+#endif
+
+		release_region(devc->hw.io_base, 15);
+
+		waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0);
+
+		if (devc->hw.irq >= 0)
+			free_irq(devc->hw.irq, devc);
+
+		sound_free_dma(devc->hw.dma);
+
+		if (devc->hw.dma != devc->hw.dma2 &&
+		    devc->hw.dma2 != NO_DMA)
+			sound_free_dma(devc->hw.dma2);
+
+		mixer = audio_devs[devc->dev_no]->mixer_dev;
+
+		if (mixer >= 0)
+			sound_unload_mixerdev(mixer);
+
+		if (devc->dev_no >= 0)
+			sound_unload_audiodev(devc->dev_no);
+
+		nr_waveartist_devs -= 1;
+
+		for (; i < nr_waveartist_devs; i++)
+			adev_info[i] = adev_info[i + 1];
+	} else
+		printk(KERN_WARNING "waveartist: can't find device "
+		       "to unload\n");
+}
+
+#ifdef CONFIG_ARCH_NETWINDER
+
+/*
+ * Rebel.com Netwinder specifics...
+ */
+
+#include <asm/hardware/dec21285.h>
+ 
+#define	VNC_TIMER_PERIOD (HZ/4)	//check slider 4 times/sec
+
+#define	MIXER_PRIVATE3_RESET	0x53570000
+#define	MIXER_PRIVATE3_READ	0x53570001
+#define	MIXER_PRIVATE3_WRITE	0x53570002
+
+#define	VNC_MUTE_INTERNAL_SPKR	0x01	//the sw mute on/off control bit
+#define	VNC_MUTE_LINE_OUT	0x10
+#define VNC_PHONE_DETECT	0x20
+#define VNC_HANDSET_DETECT	0x40
+#define VNC_DISABLE_AUTOSWITCH	0x80
+
+static inline void
+vnc_mute_spkr(wavnc_info *devc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&nw_gpio_lock, flags);
+	nw_cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
+	spin_unlock_irqrestore(&nw_gpio_lock, flags);
+}
+
+static void
+vnc_mute_lout(wavnc_info *devc)
+{
+	unsigned int left, right;
+
+	left  = waveartist_cmd1_r(devc, WACMD_GET_LEVEL);
+	right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400);
+
+	if (devc->line_mute_state) {
+		left &= ~1;
+		right &= ~1;
+	} else {
+		left |= 1;
+		right |= 1;
+	}
+	waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
+		
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+	static signed int old_slider_volume;
+	unsigned long flags;
+	signed int volume = 255;
+
+	*CSR_TIMER1_LOAD = 0x00ffffff;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	outb(0xFF, 0x201);
+	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+	while (volume && (inb(0x201) & 0x01))
+		volume--;
+
+	*CSR_TIMER1_CNTL = 0;
+
+	spin_unlock_irqrestore(&waveartist_lock,flags);
+	
+	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+	volume = 150 - (volume >> 5);
+#else
+	volume = (volume >> 6) - 25;
+#endif
+
+	if (volume < 0)
+		volume = 0;
+
+	if (volume > 100)
+		volume = 100;
+
+	/*
+	 * slider quite often reads +-8, so debounce this random noise
+	 */
+	if (abs(volume - old_slider_volume) > 7) {
+		old_slider_volume = volume;
+
+		if (debug_flg & DEBUG_MIXER)
+			printk(KERN_DEBUG "Slider volume: %d.\n", volume);
+	}
+
+	return old_slider_volume;
+}
+
+/*
+ * Decode a recording mask into a mixer selection on the NetWinder
+ * as follows:
+ *
+ *     OSS Source	WA Source	Actual source
+ *  SOUND_MASK_IMIX	Mixer		Mixer output (same as AD1848)
+ *  SOUND_MASK_LINE	Line		Line in
+ *  SOUND_MASK_LINE1	Left Mic	Handset
+ *  SOUND_MASK_PHONEIN	Left Aux	Telephone microphone
+ *  SOUND_MASK_MIC	Right Mic	Builtin microphone
+ */
+static unsigned int
+netwinder_select_input(wavnc_info *devc, unsigned int recmask,
+		       unsigned char *dev_l, unsigned char *dev_r)
+{
+	unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE;
+
+	if (recmask & SOUND_MASK_IMIX) {
+		recmask = SOUND_MASK_IMIX;
+		recdev_l = ADC_MUX_MIXER;
+		recdev_r = ADC_MUX_MIXER;
+	} else if (recmask & SOUND_MASK_LINE) {
+		recmask = SOUND_MASK_LINE;
+		recdev_l = ADC_MUX_LINE;
+		recdev_r = ADC_MUX_LINE;
+	} else if (recmask & SOUND_MASK_LINE1) {
+		recmask = SOUND_MASK_LINE1;
+		waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
+		recdev_l = ADC_MUX_MIC;
+		recdev_r = ADC_MUX_NONE;
+	} else if (recmask & SOUND_MASK_PHONEIN) {
+		recmask = SOUND_MASK_PHONEIN;
+		waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
+		recdev_l = ADC_MUX_AUX1;
+		recdev_r = ADC_MUX_NONE;
+	} else if (recmask & SOUND_MASK_MIC) {
+		recmask = SOUND_MASK_MIC;
+		waveartist_cmd1(devc, WACMD_SET_MONO | 0x100);	/* right */
+		recdev_l = ADC_MUX_NONE;
+		recdev_r = ADC_MUX_MIC;
+	}
+
+	*dev_l = recdev_l;
+	*dev_r = recdev_r;
+
+	return recmask;
+}
+
+static int
+netwinder_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
+		       unsigned char lev_r)
+{
+	switch (dev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_SYNTH:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_IGAIN:
+		devc->levels[dev] = lev_l | lev_r << 8;
+		break;
+
+	case SOUND_MIXER_MIC:		/* right mic only */
+		devc->levels[SOUND_MIXER_MIC] &= 0xff;
+		devc->levels[SOUND_MIXER_MIC] |= lev_l << 8;
+		break;
+
+	case SOUND_MIXER_LINE1:		/* left mic only  */
+		devc->levels[SOUND_MIXER_MIC] &= 0xff00;
+		devc->levels[SOUND_MIXER_MIC] |= lev_l;
+		dev = SOUND_MIXER_MIC;
+		break;
+
+	case SOUND_MIXER_PHONEIN:	/* left aux only  */
+		devc->levels[SOUND_MIXER_LINE1] = lev_l;
+		dev = SOUND_MIXER_LINE1;
+		break;
+
+	case SOUND_MIXER_IMIX:
+	case SOUND_MIXER_PHONEOUT:
+		break;
+
+	default:
+		dev = -EINVAL;
+		break;
+	}
+	return dev;
+}
+
+static int netwinder_get_mixer(wavnc_info *devc, int dev)
+{
+	int levels;
+
+	switch (dev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_SYNTH:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_IGAIN:
+		levels = devc->levels[dev];
+		break;
+
+	case SOUND_MIXER_MIC:		/* builtin mic: right mic only */
+		levels = devc->levels[SOUND_MIXER_MIC] >> 8;
+		levels |= levels << 8;
+		break;
+
+	case SOUND_MIXER_LINE1:		/* handset mic: left mic only */
+		levels = devc->levels[SOUND_MIXER_MIC] & 0xff;
+		levels |= levels << 8;
+		break;
+
+	case SOUND_MIXER_PHONEIN:	/* phone mic: left aux1 only */
+		levels = devc->levels[SOUND_MIXER_LINE1] & 0xff;
+		levels |= levels << 8;
+		break;
+
+	default:
+		levels = 0;
+	}
+
+	return levels;
+}
+
+/*
+ * Waveartist specific mixer information.
+ */
+static const struct waveartist_mixer_info netwinder_mixer = {
+	.supported_devs	= SOUND_MASK_VOLUME  | SOUND_MASK_SYNTH   |
+			SOUND_MASK_PCM     | SOUND_MASK_SPEAKER |
+			SOUND_MASK_LINE    | SOUND_MASK_MIC     |
+			SOUND_MASK_IMIX    | SOUND_MASK_LINE1   |
+			SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT|
+			SOUND_MASK_IGAIN,
+
+	.recording_devs	= SOUND_MASK_LINE    | SOUND_MASK_MIC     |
+			SOUND_MASK_IMIX    | SOUND_MASK_LINE1   |
+			SOUND_MASK_PHONEIN,
+
+	.stereo_devs	= SOUND_MASK_VOLUME  | SOUND_MASK_SYNTH   |
+			SOUND_MASK_PCM     | SOUND_MASK_LINE    |
+			SOUND_MASK_IMIX    | SOUND_MASK_IGAIN,
+
+	.select_input	= netwinder_select_input,
+	.decode_mixer	= netwinder_decode_mixer,
+	.get_mixer	= netwinder_get_mixer,
+};
+
+static void
+vnc_configure_mixer(wavnc_info *devc, unsigned int recmask)
+{
+	if (!devc->no_autoselect) {
+		if (devc->handset_detect) {
+			recmask = SOUND_MASK_LINE1;
+			devc->spkr_mute_state = devc->line_mute_state = 1;
+		} else if (devc->telephone_detect) {
+			recmask = SOUND_MASK_PHONEIN;
+			devc->spkr_mute_state = devc->line_mute_state = 1;
+		} else {
+			/* unless someone has asked for LINE-IN,
+			 * we default to MIC
+			 */
+			if ((devc->recmask & SOUND_MASK_LINE) == 0)
+				devc->recmask = SOUND_MASK_MIC;
+			devc->spkr_mute_state = devc->line_mute_state = 0;
+		}
+		vnc_mute_spkr(devc);
+		vnc_mute_lout(devc);
+
+		if (recmask != devc->recmask)
+			waveartist_set_recmask(devc, recmask);
+	}
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+	signed int slider_volume;
+	unsigned int temp, old_hs, old_td;
+
+	/*
+	 * read the "buttons" state.
+	 *  Bit 4 = 0 means handset present
+	 *  Bit 5 = 1 means phone offhook
+	 */
+	temp = inb(0x201);
+
+	old_hs = devc->handset_detect;
+	old_td = devc->telephone_detect;
+
+	devc->handset_detect = !(temp & 0x10);
+	devc->telephone_detect = !!(temp & 0x20);
+
+	if (!devc->no_autoselect &&
+	    (old_hs != devc->handset_detect ||
+	     old_td != devc->telephone_detect))
+		vnc_configure_mixer(devc, devc->recmask);
+
+	slider_volume = vnc_volume_slider(devc);
+
+	/*
+	 * If we're using software controlled volume, and
+	 * the slider moves by more than 20%, then we
+	 * switch back to slider controlled volume.
+	 */
+	if (abs(devc->slider_vol - slider_volume) > 20)
+		devc->use_slider = 1;
+
+	/*
+	 * use only left channel
+	 */
+	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+	if (slider_volume != temp && devc->use_slider) {
+		devc->slider_vol = slider_volume;
+
+		waveartist_set_mixer(devc, SOUND_MIXER_VOLUME,
+			slider_volume | slider_volume << 8);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+	int next_timeout;
+
+	if (vnc_slider(adev_info + data))
+		next_timeout = 5;	// mixer reported change
+	else
+		next_timeout = VNC_TIMER_PERIOD;
+
+	mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, int __user * arg)
+{
+	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+	int val;
+
+	switch (cmd) {
+	case SOUND_MIXER_PRIVATE1:
+	{
+		u_int prev_spkr_mute, prev_line_mute, prev_auto_state;
+		int val;
+
+		if (get_user(val, arg))
+			return -EFAULT;
+
+		/* check if parameter is logical */
+		if (val & ~(VNC_MUTE_INTERNAL_SPKR |
+			    VNC_MUTE_LINE_OUT |
+			    VNC_DISABLE_AUTOSWITCH))
+			return -EINVAL;
+
+		prev_auto_state = devc->no_autoselect;
+		prev_spkr_mute  = devc->spkr_mute_state;
+		prev_line_mute  = devc->line_mute_state;
+
+		devc->no_autoselect   = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0;
+		devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0;
+		devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0;
+
+		if (prev_spkr_mute != devc->spkr_mute_state)
+			vnc_mute_spkr(devc);
+
+		if (prev_line_mute != devc->line_mute_state)
+			vnc_mute_lout(devc);
+
+		if (prev_auto_state != devc->no_autoselect)
+			vnc_configure_mixer(devc, devc->recmask);
+
+		return 0;
+	}
+
+	case SOUND_MIXER_PRIVATE2:
+		if (get_user(val, arg))
+			return -EFAULT;
+
+		switch (val) {
+#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
+#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
+		case VNC_SOUND_PAUSE:
+			waveartist_cmd1(devc, 0x16);
+			break;
+
+		case VNC_SOUND_RESUME:
+			waveartist_cmd1(devc, 0x18);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		return 0;
+
+	/* private ioctl to allow bulk access to waveartist */
+	case SOUND_MIXER_PRIVATE3:
+	{
+		unsigned long	flags;
+		int		mixer_reg[15], i, val;
+
+		if (get_user(val, arg))
+			return -EFAULT;
+		if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg)))
+			return -EFAULT;
+
+		switch (mixer_reg[14]) {
+		case MIXER_PRIVATE3_RESET:
+			waveartist_mixer_reset(devc);
+			break;
+
+		case MIXER_PRIVATE3_WRITE:
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]);
+
+			waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]);
+			waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]);
+			break;
+
+		case MIXER_PRIVATE3_READ:
+			spin_lock_irqsave(&waveartist_lock, flags);
+
+			for (i = 0x30; i < 14 << 8; i += 1 << 8)
+				waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8));
+
+			spin_unlock_irqrestore(&waveartist_lock, flags);
+
+			if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg)))
+				return -EFAULT;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	/* read back the state from PRIVATE1 */
+	case SOUND_MIXER_PRIVATE4:
+		val = (devc->spkr_mute_state  ? VNC_MUTE_INTERNAL_SPKR : 0) |
+		      (devc->line_mute_state  ? VNC_MUTE_LINE_OUT      : 0) |
+		      (devc->handset_detect   ? VNC_HANDSET_DETECT     : 0) |
+		      (devc->telephone_detect ? VNC_PHONE_DETECT       : 0) |
+		      (devc->no_autoselect    ? VNC_DISABLE_AUTOSWITCH : 0);
+
+		return put_user(val, arg) ? -EFAULT : 0;
+	}
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		/*
+		 * special case for master volume: if we
+		 * received this call - switch from hw
+		 * volume control to a software volume
+		 * control, till the hw volume is modified
+		 * to signal that user wants to be back in
+		 * hardware...
+		 */
+		if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
+			devc->use_slider = 0;
+
+		/* speaker output            */
+		if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) {
+			unsigned int val, l, r;
+
+			if (get_user(val, arg))
+				return -EFAULT;
+
+			l = val & 0x7f;
+			r = (val & 0x7f00) >> 8;
+			val = (l + r) / 2;
+			devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8);
+			devc->spkr_mute_state = (val <= 50);
+			vnc_mute_spkr(devc);
+			return 0;
+		}
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+#endif
+
+static struct address_info cfg;
+
+static int attached;
+
+static int __initdata io = 0;
+static int __initdata irq = 0;
+static int __initdata dma = 0;
+static int __initdata dma2 = 0;
+
+
+static int __init init_waveartist(void)
+{
+	const struct waveartist_mixer_info *mix;
+
+	if (!io && machine_is_netwinder()) {
+		/*
+		 * The NetWinder WaveArtist is at a fixed address.
+		 * If the user does not supply an address, use the
+		 * well-known parameters.
+		 */
+		io   = 0x250;
+		irq  = 12;
+		dma  = 3;
+		dma2 = 7;
+	}
+
+	mix = &waveartist_mixer;
+#ifdef CONFIG_ARCH_NETWINDER
+	if (machine_is_netwinder())
+		mix = &netwinder_mixer;
+#endif
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma2;
+
+	if (!probe_waveartist(&cfg))
+		return -ENODEV;
+
+	attach_waveartist(&cfg, mix);
+	attached = 1;
+
+	return 0;
+}
+
+static void __exit cleanup_waveartist(void)
+{
+	if (attached)
+		unload_waveartist(&cfg);
+}
+
+module_init(init_waveartist);
+module_exit(cleanup_waveartist);
+
+#ifndef MODULE
+static int __init setup_waveartist(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("waveartist=", setup_waveartist);
+#endif
+
+MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver");
+module_param(io, int, 0);		/* IO base */
+module_param(irq, int, 0);		/* IRQ */
+module_param(dma, int, 0);		/* DMA */
+module_param(dma2, int, 0);		/* DMA2 */
+MODULE_LICENSE("GPL");
diff --git a/sound/oss/waveartist.h b/sound/oss/waveartist.h
new file mode 100644
index 00000000..dac4ca91
--- /dev/null
+++ b/sound/oss/waveartist.h
@@ -0,0 +1,92 @@
+/*
+ * linux/sound/oss/waveartist.h
+ *
+ * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
+ */
+
+//registers
+#define CMDR	0
+#define DATR	2
+#define CTLR	4
+#define	STATR	5
+#define	IRQSTAT	12
+
+//bit defs
+//reg STATR
+#define	CMD_WE	0x80
+#define	CMD_RF	0x40
+#define	DAT_WE	0x20
+#define	DAT_RF	0x10
+
+#define	IRQ_REQ	0x08
+#define	DMA1	0x04
+#define	DMA0	0x02
+
+//bit defs
+//reg CTLR
+#define	CMD_WEIE	0x80
+#define	CMD_RFIE	0x40
+#define	DAT_WEIE	0x20
+#define	DAT_RFIE	0x10
+
+#define	RESET	0x08
+#define	DMA1_IE	0x04
+#define	DMA0_IE	0x02
+#define	IRQ_ACK	0x01
+
+//commands
+
+#define	WACMD_SYSTEMID		0x00
+#define WACMD_GETREV		0x00
+#define	WACMD_INPUTFORMAT	0x10	//0-8S, 1-16S, 2-8U
+#define	WACMD_INPUTCHANNELS	0x11	//1-Mono, 2-Stereo
+#define	WACMD_INPUTSPEED	0x12	//sampling rate
+#define	WACMD_INPUTDMA		0x13	//0-8bit, 1-16bit, 2-PIO
+#define	WACMD_INPUTSIZE		0x14	//samples to interrupt
+#define	WACMD_INPUTSTART	0x15	//start ADC
+#define	WACMD_INPUTPAUSE	0x16	//pause ADC
+#define	WACMD_INPUTSTOP		0x17	//stop ADC
+#define	WACMD_INPUTRESUME	0x18	//resume ADC
+#define	WACMD_INPUTPIO		0x19	//PIO ADC
+
+#define	WACMD_OUTPUTFORMAT	0x20	//0-8S, 1-16S, 2-8U
+#define	WACMD_OUTPUTCHANNELS	0x21	//1-Mono, 2-Stereo
+#define	WACMD_OUTPUTSPEED	0x22	//sampling rate
+#define	WACMD_OUTPUTDMA		0x23	//0-8bit, 1-16bit, 2-PIO
+#define	WACMD_OUTPUTSIZE	0x24	//samples to interrupt
+#define	WACMD_OUTPUTSTART	0x25	//start ADC
+#define	WACMD_OUTPUTPAUSE	0x26	//pause ADC
+#define	WACMD_OUTPUTSTOP	0x27	//stop ADC
+#define	WACMD_OUTPUTRESUME	0x28	//resume ADC
+#define	WACMD_OUTPUTPIO		0x29	//PIO ADC
+
+#define	WACMD_GET_LEVEL		0x30
+#define	WACMD_SET_LEVEL		0x31
+#define	WACMD_SET_MIXER		0x32
+#define	WACMD_RST_MIXER		0x33
+#define	WACMD_SET_MONO		0x34
+
+/*
+ * Definitions for left/right recording input mux
+ */
+#define ADC_MUX_NONE	0
+#define ADC_MUX_MIXER	1
+#define ADC_MUX_LINE	2
+#define ADC_MUX_AUX2	3
+#define ADC_MUX_AUX1	4
+#define ADC_MUX_MIC	5
+
+/*
+ * Definitions for mixer gain settings
+ */
+#define MIX_GAIN_LINE	0	/* line in	 */
+#define MIX_GAIN_AUX1	1	/* aux1		 */
+#define MIX_GAIN_AUX2	2	/* aux2		 */
+#define MIX_GAIN_XMIC	3	/* crossover mic */
+#define MIX_GAIN_MIC	4	/* normal mic	 */
+#define MIX_GAIN_PREMIC	5	/* preamp mic	 */
+#define MIX_GAIN_OUT	6	/* output	 */
+#define MIX_GAIN_MONO	7	/* mono in	 */
+
+int wa_sendcmd(unsigned int cmd);
+int wa_writecmd(unsigned int cmd, unsigned int arg);
-- 
cgit