diff options
Diffstat (limited to 'drivers/sound')
91 files changed, 21372 insertions, 4195 deletions
diff --git a/drivers/sound/CHANGELOG b/drivers/sound/CHANGELOG index daa9652e5..8706cd66c 100644 --- a/drivers/sound/CHANGELOG +++ b/drivers/sound/CHANGELOG @@ -41,7 +41,7 @@ stream of received 0x00 bytes when the MIDI receiver is enabled. Since 3.5 - Changes almost everywhere. -- Support for OPTi 82C924 based soundcards. +- 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 @@ -79,7 +79,7 @@ Since 3.5.4-beta2 - 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 soundcards. See lowlevel/aci.readme for more info. + miroPCM sound cards. See lowlevel/aci.readme for more info. - Support for Aztech Washington chipset (AZT2316 ASIC). Since 3.5.4-beta1 @@ -271,7 +271,7 @@ Since pre-3.0-940712 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 soundcards. This version is developed +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 @@ -336,7 +336,7 @@ Since 2.2b - 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 soundcard is available. + 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. diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 8f1d2bec7..4f511f3ab 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -1,221 +1,229 @@ -dep_tristate 'ProAudioSpectrum 16 support' CONFIG_PAS $CONFIG_SOUND -if [ "$CONFIG_PAS" = "y" ]; then - int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' CONFIG_PAS_IRQ 10 - int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' CONFIG_PAS_DMA 3 - bool 'Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK -fi - -dep_tristate '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB $CONFIG_SOUND -if [ "$CONFIG_SB" = "y" ]; then - hex 'I/O base for SB Check from manual of the card' CONFIG_SB_BASE 220 - int 'Sound Blaster IRQ Check from manual of the card' CONFIG_SB_IRQ 7 - int 'Sound Blaster DMA 0, 1 or 3' CONFIG_SB_DMA 1 - int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' CONFIG_SB_DMA2 5 - hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' CONFIG_SB_MPU_BASE 330 - comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' - comment 'Enter -1 to the following question if you have something else such as SB16/32.' - int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1 -fi - -dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB $CONFIG_SOUND - -dep_tristate 'Gravis Ultrasound support' CONFIG_GUS $CONFIG_SOUND -if [ "$CONFIG_GUS" != "n" ]; then - bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 - bool 'GUS MAX support' CONFIG_GUSMAX - if [ "$CONFIG_GUS" = "y" ]; then - hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' CONFIG_GUS_BASE 220 - int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' CONFIG_GUS_IRQ 15 - int 'GUS DMA 1, 3, 5, 6 or 7' CONFIG_GUS_DMA 6 - int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' CONFIG_GUS_DMA2 -1 - if [ "$CONFIG_GUS16" = "y" ]; then - hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' CONFIG_GUS16_BASE 530 - int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' CONFIG_GUS16_IRQ 7 - int 'GUS DMA 0, 1 or 3' CONFIG_GUS16_DMA 3 - fi - fi -fi - -dep_tristate 'MPU-401 support (NOT for SB16)' CONFIG_MPU401 $CONFIG_SOUND -if [ "$CONFIG_MPU401" = "y" ]; then - hex 'I/O base for MPU401 Check from manual of the card' CONFIG_MPU_BASE 330 - int 'MPU401 IRQ Check from manual of the card' CONFIG_MPU_IRQ 9 -fi - -dep_tristate 'PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_PSS $CONFIG_SOUND -if [ "$CONFIG_PSS" = "y" ]; then - hex 'PSS I/O base 220 or 240' CONFIG_PSS_BASE 220 - hex 'PSS audio I/O base 530, 604, E80 or F40' CONFIG_PSS_MSS_BASE 530 - int 'PSS audio IRQ 7, 9, 10 or 11' CONFIG_PSS_MSS_IRQ 11 - int 'PSS audio DMA 0, 1 or 3' CONFIG_PSS_MSS_DMA 3 - hex 'PSS MIDI I/O base ' CONFIG_PSS_MPU_BASE 330 - int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_PSS_MPU_IRQ 9 - bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT - if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then - string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE - fi -fi -if [ "$CONFIG_PSS" = "m" ] || [ "$CONFIG_PSS" = "y" ]; then - bool ' Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER -fi - -dep_tristate 'Microsoft Sound System support' CONFIG_MSS $CONFIG_SOUND -if [ "$CONFIG_MSS" = "y" ]; then - hex 'MSS/WSS I/O base 530, 604, E80 or F40' CONFIG_MSS_BASE 530 - int 'MSS/WSS IRQ 7, 9, 10 or 11' CONFIG_MSS_IRQ 11 - int 'MSS/WSS DMA 0, 1 or 3' CONFIG_MSS_DMA 3 - int 'MSS/WSS second DMA (if possible) 0, 1 or 3' CONFIG_MSS_DMA2 -1 -fi - -dep_tristate 'Ensoniq SoundScape support' CONFIG_SSCAPE $CONFIG_SOUND -if [ "$CONFIG_SSCAPE" = "y" ]; then - hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' CONFIG_SSCAPE_BASE 330 - int 'SoundScape MIDI IRQ ' CONFIG_SSCAPE_IRQ 9 - int 'SoundScape initialization DMA 0, 1 or 3' CONFIG_SSCAPE_DMA 3 - hex 'SoundScape audio I/O base 534, 608, E84 or F44' CONFIG_SSCAPE_MSS_BASE 534 - int 'SoundScape audio IRQ 7, 9, 10 or 11' CONFIG_SSCAPE_MSS_IRQ 11 -fi - -dep_tristate 'MediaTrix AudioTrix Pro support' CONFIG_TRIX $CONFIG_SOUND -if [ "$CONFIG_TRIX" = "y" ]; then - hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' CONFIG_TRIX_BASE 530 - int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' CONFIG_TRIX_IRQ 11 - int 'OPL3-SA1 audio DMA 0, 1 or 3' CONFIG_TRIX_DMA 0 - int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' CONFIG_TRIX_DMA2 3 - hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_TRIX_MPU_BASE 330 - int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_TRIX_MPU_IRQ 9 - hex 'OPL3-SA1 SB I/O base 220, 210, 230, 240, 250, 260 or 270' CONFIG_TRIX_SB_BASE 220 - int 'OPL3-SA1 SB IRQ 3, 4, 5 or 7' CONFIG_TRIX_SB_IRQ 7 - int 'OPL3-SA1 SB DMA 1 or 3' CONFIG_TRIX_SB_DMA 1 - bool ' Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT - if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then - string ' Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE - fi -fi - -dep_tristate 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_MAD16 $CONFIG_SOUND -dep_tristate 'Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD $CONFIG_MAD16 -if [ "$CONFIG_MAD16" = "y" ]; then - hex 'MAD16 audio I/O base 530, 604, E80 or F40' CONFIG_MAD16_BASE 530 - int 'MAD16 audio IRQ 7, 9, 10 or 11' CONFIG_MAD16_IRQ 11 - int 'MAD16 audio DMA 0, 1 or 3' CONFIG_MAD16_DMA 3 - int 'MAD16 second (duplex) DMA 0, 1 or 3' CONFIG_MAD16_DMA2 0 - hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' CONFIG_MAD16_MPU_BASE 330 - int 'MAD16 MIDI IRQ 5, 7, 9 or 10' CONFIG_MAD16_MPU_IRQ 9 -fi - -dep_tristate 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232 $CONFIG_SOUND -if [ "$CONFIG_CS4232" = "y" ]; then - hex 'CS4232 audio I/O base 530, 604, E80 or F40' CONFIG_CS4232_BASE 530 - int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_IRQ 11 - int 'CS4232 audio DMA 0, 1 or 3' CONFIG_CS4232_DMA 0 - int 'CS4232 second (duplex) DMA 0, 1 or 3' CONFIG_CS4232_DMA2 3 - hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_CS4232_MPU_BASE 330 - int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_MPU_IRQ 9 -fi - -dep_tristate 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI $CONFIG_SOUND -if [ "$CONFIG_MAUI" = "y" ]; then - hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' CONFIG_MAUI_BASE 330 - int 'Maui IRQ 5, 9, 12 or 15' CONFIG_MAUI_IRQ 9 - bool ' Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT - if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then - string ' Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE - fi -fi - -dep_tristate 'Yamaha OPL3-SA1 audio controller' CONFIG_OPL3SA1 $CONFIG_SOUND -if [ "$CONFIG_OPL3SA1" = "y" ]; then - hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' CONFIG_OPL3SA1_BASE 530 - int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' CONFIG_OPL3SA1_IRQ 11 - int 'OPL3-SA1 audio DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA 0 - int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA2 3 - hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_OPL3SA1_MPU_BASE 330 - int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_OPL3SA1_MPU_IRQ 9 -fi - -dep_tristate 'SoftOSS software wave table engine' CONFIG_SOFTOSS $CONFIG_SOUND -if [ "$CONFIG_SOFTOSS" = "y" ]; then - int 'Sampling rate for SoftOSS 8000 to 48000' CONFIG_SOFTOSS_RATE 22050 - int 'Max # of concurrent voices for SoftOSS 4 to 32' CONFIG_SOFTOSS_VOICES 32 -fi - -dep_tristate 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 $CONFIG_SOUND -dep_tristate 'Loopback MIDI device support' CONFIG_VMIDI $CONFIG_SOUND - -dep_tristate '6850 UART support' CONFIG_UART6850 $CONFIG_SOUND -if [ "$CONFIG_UART6850" = "y" ]; then - hex 'I/O base for UART 6850 MIDI port (Unknown)' CONFIG_U6850_BASE 0 - int 'UART6850 IRQ (Unknown)' CONFIG_U6850_IRQ -1 -fi - - -bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND - -if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then - dep_tristate 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND - dep_tristate 'AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND - dep_tristate 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16 $CONFIG_SOUND - - if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then - hex ' I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 - fi - - if [ "$CONFIG_AEDSP16" = "y" ]; then - comment 'SC-6600 Audio Cards have no jumper switches at all' - bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 - - if [ "$CONFIG_SB" = "y" -o "$CONFIG_SB" = "m" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then - bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO - if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then - comment 'Audio Excel DSP 16 [Sound Blaster Pro]' - hex 'I/O base for Audio Excel DSP 16 220 or 240' \ - CONFIG_AEDSP16_BASE $CONFIG_SB_BASE - int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \ - CONFIG_AEDSP16_SB_IRQ $CONFIG_SB_IRQ - int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_SB_DMA $CONFIG_SB_DMA - fi +# drivers/sound/Config.in +# +# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net> +# More hacking for modularisation. +# +# See drivers/sound/README.CONFIG for more information. + + + +# Prompt user for primary drivers. + +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND + dep_tristate 'Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND + dep_tristate 'S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND +fi + +dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND +if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then + string ' Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin" + string ' Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin" +fi +if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then + int 'MSND Classic IRQ 5,7,9,10,11,12' CONFIG_MSNDCLAS_IRQ 5 + hex 'MSND Classic Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDCLAS_MEM D0000 + hex 'MSND Classic I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDCLAS_IO 290 +fi + +dep_tristate 'Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_MSNDPIN $CONFIG_SOUND +if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDPIN" = "m" ]; then + string ' Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin" + string ' Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin" +fi +if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then + int 'MSND Pinnacle IRQ 5,7,9,10,11,12' CONFIG_MSNDPIN_IRQ 5 + hex 'MSND Pinnacle Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDPIN_MEM D0000 + hex 'MSND Pinnacle I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDPIN_IO 290 +fi + +dep_tristate 'OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND + +if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then + dep_tristate 'ProAudioSpectrum 16 support' CONFIG_SOUND_PAS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_PAS" = "y" ]; then + int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' CONFIG_PAS_IRQ 10 + int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' CONFIG_PAS_DMA 3 + bool 'Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK + fi + + dep_tristate '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SB" = "y" ]; then + hex 'I/O base for SB Check from manual of the card' CONFIG_SB_BASE 220 + int 'Sound Blaster IRQ Check from manual of the card' CONFIG_SB_IRQ 7 + int 'Sound Blaster DMA 0, 1 or 3' CONFIG_SB_DMA 1 + int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' CONFIG_SB_DMA2 5 + hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' CONFIG_SB_MPU_BASE 330 + comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' + comment 'Enter -1 to the following question if you have something else such as SB16/32.' + int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1 + fi + + dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS + + dep_tristate 'Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_GUS" = "y" -o "$CONFIG_SOUND_GUS" = "m" ]; then + bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 + bool 'GUS MAX support' CONFIG_GUSMAX + fi + if [ "$CONFIG_SOUND_GUS" = "y" ]; then + hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' CONFIG_GUS_BASE 220 + int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' CONFIG_GUS_IRQ 15 + int 'GUS DMA 1, 3, 5, 6 or 7' CONFIG_GUS_DMA 6 + int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' CONFIG_GUS_DMA2 -1 + if [ "$CONFIG_GUS16" = "y" ]; then + hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' CONFIG_GUS16_BASE 530 + int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' CONFIG_GUS16_IRQ 7 + int 'GUS DMA 0, 1 or 3' CONFIG_GUS16_DMA 3 fi - - if [ "$CONFIG_MSS" = "y" -o "$CONFIG_MSS" = "m" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then - bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS - if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then - comment 'Audio Excel DSP 16 [Microsoft Sound System]' - hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 - int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \ - CONFIG_AEDSP16_MSS_IRQ $CONFIG_MSS_IRQ - int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_MSS_DMA $CONFIG_MSS_DMA - fi + fi + + dep_tristate 'MPU-401 support (NOT for SB16)' CONFIG_SOUND_MPU401 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MPU401" = "y" ]; then + hex 'I/O base for MPU401 Check from manual of the card' CONFIG_MPU_BASE 330 + int 'MPU401 IRQ Check from manual of the card' CONFIG_MPU_IRQ 9 + fi + + dep_tristate 'PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_PSS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_PSS" = "y" ]; then + hex 'PSS I/O base 220 or 240' CONFIG_PSS_BASE 220 + hex 'PSS audio I/O base 530, 604, E80 or F40' CONFIG_PSS_MSS_BASE 530 + int 'PSS audio IRQ 7, 9, 10 or 11' CONFIG_PSS_MSS_IRQ 11 + int 'PSS audio DMA 0, 1 or 3' CONFIG_PSS_MSS_DMA 3 + hex 'PSS MIDI I/O base ' CONFIG_PSS_MPU_BASE 330 + int 'PSS MIDI IRQ 3, 4, 5, 7, 9, 10, 11, 12' CONFIG_PSS_MPU_IRQ 9 + bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT + if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then + string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE fi - - if [ "$CONFIG_MPU401" = "y" -o "$CONFIG_MPU401" = "m" ]; then - bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401 - if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then - comment 'Audio Excel DSP 16 [MPU-401]' - if [ "$CONFIG_AEDSP16_SBPRO" != "y" \ - -a "$CONFIG_AEDSP16_MSS" != "y" ]; then - hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 - fi - int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \ - CONFIG_AEDSP16_MPU_IRQ $CONFIG_MPU_IRQ - fi + fi + if [ "$CONFIG_SOUND_PSS" = "y" -o "$CONFIG_SOUND_PSS" = "m" ]; then + bool ' Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER + fi + + dep_tristate 'Microsoft Sound System support' CONFIG_SOUND_MSS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MSS" = "y" ]; then + hex 'MSS/WSS I/O base 530, 604, E80 or F40' CONFIG_MSS_BASE 530 + int 'MSS/WSS IRQ 7, 9, 10 or 11' CONFIG_MSS_IRQ 11 + int 'MSS/WSS DMA 0, 1 or 3' CONFIG_MSS_DMA 3 + int 'MSS/WSS second DMA (if possible) 0, 1 or 3' CONFIG_MSS_DMA2 -1 + fi + + dep_tristate 'Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SSCAPE" = "y" ]; then + hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' CONFIG_SSCAPE_BASE 330 + int 'SoundScape MIDI IRQ ' CONFIG_SSCAPE_IRQ 9 + int 'SoundScape initialization DMA 0, 1 or 3' CONFIG_SSCAPE_DMA 3 + hex 'SoundScape audio I/O base 534, 608, E84 or F44' CONFIG_SSCAPE_MSS_BASE 534 + int 'SoundScape audio IRQ 7, 9, 10 or 11' CONFIG_SSCAPE_MSS_IRQ 11 + fi + + dep_tristate 'MediaTrix AudioTrix Pro support' CONFIG_SOUND_TRIX $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_TRIX" = "y" ]; then + hex 'TRIX audio I/O base 530, 604, E80 or F40' CONFIG_TRIX_BASE 530 + int 'TRIX audio IRQ 7, 9, 10 or 11' CONFIG_TRIX_IRQ 11 + int 'TRIX audio DMA 0, 1 or 3' CONFIG_TRIX_DMA 0 + int 'TRIX second (duplex) DMA 0, 1 or 3' CONFIG_TRIX_DMA2 3 + hex 'TRIX MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_TRIX_MPU_BASE 330 + int 'TRIX MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_TRIX_MPU_IRQ 9 + hex 'TRIX SB I/O base 220, 210, 230, 240, 250, 260 or 270' CONFIG_TRIX_SB_BASE 220 + int 'TRIX SB IRQ 3, 4, 5 or 7' CONFIG_TRIX_SB_IRQ 7 + int 'TRIX SB DMA 1 or 3' CONFIG_TRIX_SB_DMA 1 + bool ' Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT + if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then + string ' Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE fi - fi - - if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then - comment 'SC-6600 Audio Cards have no jumper switches at all' - bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 - - if [ "$CONFIG_SC6600" = "y" ]; then - comment 'SC-6600 specific configuration' - bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY - int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \ - CONFIG_SC6600_CDROM 4 - hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0 + fi + + dep_tristate 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_MAD16 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MAD16" = "y" -o "$CONFIG_SOUND_MAD16" = "m" ]; then + bool 'Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD + fi + if [ "$CONFIG_SOUND_MAD16" = "y" ]; then + hex 'MAD16 audio I/O base 530, 604, E80 or F40' CONFIG_MAD16_BASE 530 + int 'MAD16 audio IRQ 7, 9, 10 or 11' CONFIG_MAD16_IRQ 11 + int 'MAD16 audio DMA 0, 1 or 3' CONFIG_MAD16_DMA 3 + int 'MAD16 second (duplex) DMA 0, 1 or 3' CONFIG_MAD16_DMA2 0 + hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' CONFIG_MAD16_MPU_BASE 330 + int 'MAD16 MIDI IRQ 5, 7, 9 or 10' CONFIG_MAD16_MPU_IRQ 9 + fi + + dep_tristate 'Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_WAVEFRONT $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_WAVEFRONT" = "y" ]; then + hex 'I/O base for WaveFront 210, 230, 260, 290, 300, 320, 338 or 330' CONFIG_WAVEFRONT_BASE 330 + int 'WaveFront IRQ 5, 9, 12 or 15' CONFIG_WAVEFRONT_IRQ 9 + fi + + dep_tristate 'Support for Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_CS4232" = "y" ]; then + hex 'CS4232 audio I/O base 530, 604, E80 or F40' CONFIG_CS4232_BASE 530 + int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_IRQ 11 + int 'CS4232 audio DMA 0, 1 or 3' CONFIG_CS4232_DMA 0 + int 'CS4232 second (duplex) DMA 0, 1 or 3' CONFIG_CS4232_DMA2 3 + hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_CS4232_MPU_BASE 330 + int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_MPU_IRQ 9 + fi + + dep_tristate 'Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MAUI" = "y" ]; then + hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' CONFIG_MAUI_BASE 330 + int 'Maui IRQ 5, 9, 12 or 15' CONFIG_MAUI_IRQ 9 + bool ' Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT + if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then + string ' Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE fi - fi + fi + + + dep_tristate 'Support for Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SGALAXY" = "y" ]; then + hex 'SGALAXY audio I/O base 530, 604, E80 or F40' CONFIG_SGALAXY_BASE 530 + int 'SGALAXY audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_SGALAXY_IRQ 11 + int 'SGALAXY audio DMA 0, 1 or 3' CONFIG_SGALAXY_DMA 0 + int 'SGALAXY second (duplex) DMA 0, 1 or 3' CONFIG_SGALAXY_DMA2 3 + hex 'SGALAXY SB I/O base 220 or 240' CONFIG_SGALAXY_SGBASE 220 + fi + + dep_tristate 'Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_OPL3SA1" = "y" ]; then + hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' CONFIG_OPL3SA1_BASE 530 + int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' CONFIG_OPL3SA1_IRQ 11 + int 'OPL3-SA1 audio DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA 0 + int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA2 3 + hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_OPL3SA1_MPU_BASE 330 + int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_OPL3SA1_MPU_IRQ 9 + fi + + dep_tristate 'SoftOSS software wave table engine' CONFIG_SOUND_SOFTOSS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SOFTOSS" = "y" ]; then + int 'Sampling rate for SoftOSS 8000 to 48000' CONFIG_SOFTOSS_RATE 22050 + int 'Max # of concurrent voices for SoftOSS 4 to 32' CONFIG_SOFTOSS_VOICES 32 + fi + + dep_tristate 'FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS + + dep_tristate 'Loopback MIDI device support' CONFIG_SOUND_VMIDI $CONFIG_SOUND_OSS + + dep_tristate '6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS + if [ "$CONFIG_UART6850" = "y" ]; then + hex 'I/O base for UART 6850 MIDI port (Unknown)' CONFIG_U6850_BASE 0 + int 'UART6850 IRQ (Unknown)' CONFIG_U6850_IRQ -1 + fi + + if [ "$CONFIG_ARM" = "y" ]; then + bool 'VIDC 16-bit sound' CONFIG_VIDC_SOUND + fi + + + + # Additional low level drivers. + + mainmenu_option next_comment + comment 'Additional low level sound drivers' + bool 'Additional low level sound drivers' CONFIG_LOWLEVEL_SOUND $CONFIG_SOUND_OSS + if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then + source drivers/sound/lowlevel/Config.in + fi + endmenu fi -if [ "$CONFIG_ARM" = "y" ]; then - bool 'VIDC 16-bit sound' CONFIG_VIDC_SOUND -fi diff --git a/drivers/sound/Defines b/drivers/sound/Defines deleted file mode 100644 index b2aef2dfb..000000000 --- a/drivers/sound/Defines +++ /dev/null @@ -1,407 +0,0 @@ -# This used to be a computer generated file, but it's not any more. -# Edit all you need! - -ifdef CONFIG_PSS -ifneq ($(CONFIG_MPU_EMU),Y) -CONFIG_MPU_EMU=y -endif -endif - -ifdef CONFIG_SSCAPE -ifneq ($(CONFIG_MPU_EMU),Y) -CONFIG_MPU_EMU=y -endif -endif - -ifdef CONFIG_CS4232 -ifneq ($(CONFIG_MPU_EMU),Y) -CONFIG_MPU_EMU=y -endif -endif - -ifdef CONFIG_MAUI -ifneq ($(CONFIG_MPU_EMU),Y) -CONFIG_MPU_EMU=y -endif -endif - -ifdef CONFIG_PSS -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_GUS16 -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_GUSMAX -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_MSS -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_SSCAPE -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_TRIX -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_MAD16 -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_CS4232 -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_OPL3SA1 -ifneq ($(CONFIG_AD1848),Y) -CONFIG_AD1848=y -endif -endif - -ifdef CONFIG_SB -ifneq ($(CONFIG_SBDSP),Y) -CONFIG_SBDSP=y -endif -endif - -ifdef CONFIG_TRIX -ifneq ($(CONFIG_SBDSP),Y) -CONFIG_SBDSP=y -endif -endif - -ifdef CONFIG_MAD16 -ifneq ($(CONFIG_SBDSP),Y) -CONFIG_SBDSP=y -endif -endif - - - -ifdef CONFIG_SB - ifeq ($(CONFIG_SB),m) - ifneq ($(CONFIG_UART401),Y) - CONFIG_UART401=m - endif - else - ifneq ($(CONFIG_UART401),Y) - CONFIG_UART401=y - endif - endif -endif - -ifdef CONFIG_TRIX -ifneq ($(CONFIG_UART401),Y) -CONFIG_UART401=y -endif -endif - -ifdef CONFIG_MAD16 -ifneq ($(CONFIG_UART401),Y) -CONFIG_UART401=y -endif -endif - -ifdef CONFIG_OPL3SA1 -ifneq ($(CONFIG_UART401),Y) -CONFIG_UART401=y -endif -endif - -ifdef CONFIG_PAS -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_SB -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_ADLIB -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_GUS -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_MPU401 -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_PSS -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_GUS16 -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_GUSMAX -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_MSS -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_SSCAPE -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_TRIX -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_MAD16 -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_CS4232 -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_MAUI -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_OPL3SA1 -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_SOFTOSS -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_YM3812_AUTO -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_VIDC_SOUND -ifneq ($(CONFIG_SEQUENCER),Y) -CONFIG_SEQUENCER=y -endif -endif - -ifdef CONFIG_PAS -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_SB -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_GUS -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_PSS -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_GUS16 -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_GUSMAX -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_MSS -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_SSCAPE -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_TRIX -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_MAD16 -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_CS4232 -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_OPL3SA1 -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_SOFTOSS -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_VIDC_SOUND -ifneq ($(CONFIG_AUDIO),Y) -CONFIG_AUDIO=y -endif -endif - -ifdef CONFIG_PAS -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_SB -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_GUS -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_MPU401 -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_PSS -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_GUS16 -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_GUSMAX -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_SSCAPE -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_TRIX -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_MAD16 -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_CS4232 -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_MAUI -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_OPL3SA1 -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - -ifdef CONFIG_SOFTOSS -ifneq ($(CONFIG_MIDI),Y) -CONFIG_MIDI=y -endif -endif - diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 4cfb171dc..d04efe300 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -1,317 +1,284 @@ -BUILDCODE=s # Makefile for the Linux sound card driver # -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes. (hopefully) -# -# -# -# -ifeq ($(ARCH),m68k) - L_TARGET := sound.a - L_OBJS := - M_OBJS := - ifeq ($(CONFIG_DMASOUND),y) - L_OBJS += dmasound.o - else - ifeq ($(CONFIG_DMASOUND),m) - M_OBJS += dmasound.o - endif - endif - - include $(TOPDIR)/Rules.make - - clean: - rm -f core *.o *.a *.s +# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net> +# Rewritten to use lists instead of if-statements. -else -include Defines -ifndef TOPDIR -TOPDIR=/usr/src/linux +# My subdirectories. + +SUB_DIRS := +MOD_SUB_DIRS := +MOD_IN_SUB_DIRS := +ALL_SUB_DIRS := $(SUB_DIRS) lowlevel + +ifeq ($(CONFIG_LOWLEVEL_SOUND),y) + SUB_DIRS += lowlevel + MOD_IN_SUB_DIRS += lowlevel endif -SUB_DIRS := lowlevel -MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) -L_TARGET := sound.a -M_OBJS := -L_OBJS := -ifeq ($(CONFIG_SOUND),y) - L_OBJS += soundcard.o dev_table.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o audio.o dmabuf.o -else - ifeq ($(CONFIG_SOUND),m) - M_OBJS += sound.o - MIX_OBJS += sound_syms.o - endif -endif +# All of the (potential) objects that export symbols. +# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. -ifeq ($(CONFIG_MIDI),y) - L_OBJS += midibuf.o - LX_OBJS += midi_synth.o -endif +export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ + msnd.o opl3.o sb_card.o sequencer_syms.o \ + sound_core.o sound_firmware.o sound_syms.o \ + uart401.o -#ifeq ($(CONFIG_AUDIO),y) -#L_OBJS += dmabuf.o -#endif -ifeq ($(CONFIG_YM3812),y) -LX_OBJS += adlib_card.o opl3.o -else - ifeq ($(CONFIG_YM3812),m) - MX_OBJS += adlib_card.o opl3.o - endif -endif -ifeq ($(CONFIG_PAS),y) -L_OBJS += pas2.o -else - ifeq ($(CONFIG_PAS),m) - M_OBJS += pas2.o - endif -endif +# Object file lists. -ifeq ($(CONFIG_GUS),y) -L_OBJS += gus.o - ifeq ($(CONFIG_GUSMAX),y) - CONFIG_MSS = y - endif -else - ifeq ($(CONFIG_GUS),m) - M_OBJS += gus.o - endif - ifeq ($(CONFIG_GUSMAX),y) - ifneq ($(CONFIG_MSS),y) - CONFIG_MSS = m - endif - endif -endif +obj-y := +obj-m := +obj-n := +obj- := -ifeq ($(CONFIG_SB),y) -L_OBJS += sb_audio.o sb_common.o sb_midi.o sb_mixer.o -LX_OBJS += sb_card.o -CONFIG_UART401 = y -else - ifeq ($(CONFIG_SB),m) - M_OBJS += sb.o - MIX_OBJS += sb_card.o - ifneq ($(CONFIG_UART401),y) - CONFIG_UART401 = m - endif - endif -endif -ifeq ($(CONFIG_MSS),y) -LX_OBJS += ad1848.o -else - ifeq ($(CONFIG_MSS),m) - MX_OBJS += ad1848.o - endif -endif -ifeq ($(CONFIG_ADLIB),y) -LX_OBJS += adlib_card.o -else - ifeq ($(CONFIG_ADLIB),m) - MX_OBJS += adlib_card.o - endif -endif +# Each configuration option enables a list of files. -ifeq ($(CONFIG_MPU401),y) -LX_OBJS += mpu401.o -else - ifeq ($(CONFIG_MPU401),m) - MX_OBJS += mpu401.o - else - ifeq ($(CONFIG_MPU_EMU),y) - LX_OBJS += mpu401.o - else - ifeq ($(CONFIG_MPU_EMU),m) - MX_OBJS += mpu401.o - endif - endif - endif -endif +obj-$(CONFIG_SOUND) += soundcore.o -ifeq ($(CONFIG_UART401),y) -LX_OBJS += uart401.o -else - ifeq ($(CONFIG_UART401),m) - MX_OBJS += uart401.o - endif -endif +ifeq ($(ARCH),m68k) -ifeq ($(CONFIG_UART6850),y) -LX_OBJS += uart6850.o -else - ifeq ($(CONFIG_UART6850),m) - MX_OBJS += uart6850.o - endif -endif +obj-$(CONFIG_DMASOUND) += dmasound.o -ifeq ($(CONFIG_PSS),y) -L_OBJS += pss.o else - ifeq ($(CONFIG_PSS),m) - M_OBJS += pss.o - endif -endif -ifeq ($(CONFIG_SSCAPE),y) -L_OBJS += sscape.o -else - ifeq ($(CONFIG_SSCAPE),m) - M_OBJS += sscape.o - endif -endif +ifeq ($(CONFIG_PMAC),y) -ifeq ($(CONFIG_TRIX),y) -L_OBJS += trix.o -else - ifeq ($(CONFIG_TRIX),m) - M_OBJS += trix.o - endif -endif +obj-$(CONFIG_DMASOUND) += dmasound.o -ifeq ($(CONFIG_MAD16),y) -L_OBJS += mad16.o else - ifeq ($(CONFIG_MAD16),m) - M_OBJS += mad16.o sb.o uart401.o - MX_OBJS += sb_card.o ad1848.o - endif -endif -ifeq ($(CONFIG_CS4232),y) -LX_OBJS += cs4232.o -else - ifeq ($(CONFIG_CS4232),m) - MX_OBJS += cs4232.o - endif -endif +obj-$(CONFIG_SOUND_OSS) += sound.o +obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o +obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o + +# In theory, there's probably no reason to include the uart401 code +# to support a WaveFront card's CS4232 module. However, it makes +# reconfiguring things require a recompile, so just leave this +# here and try not to worry about the extra uart401 module. + +obj-$(CONFIG_SOUND_CS4232) += uart401.o +obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o +obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb.o uart401.o +obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o +obj-$(CONFIG_SOUND_MPU401) += mpu401.o +obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o +obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o +obj-$(CONFIG_SOUND_MSS) += ad1848.o +obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o +obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o uart401.o +obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_SB) += sb.o uart401.o +obj-$(CONFIG_SOUND_SOFTOSS) += softoss2.o +obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o +obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb.o uart401.o +obj-$(CONFIG_SOUND_UART6850) += uart6850.c +obj-$(CONFIG_SOUND_VMIDI) += v_midi.o +obj-$(CONFIG_SOUND_YM3812) += adlib_card.o opl3.o +obj-$(CONFIG_VIDC_SOUND) += vidc_mod.o +obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o +#jnx +obj-$(CONFIG_SOUND_ES1370) += es1370.o +obj-$(CONFIG_SOUND_ES1371) += es1371.o +obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o -ifeq ($(CONFIG_MAUI),y) -L_OBJS += maui.o -else - ifeq ($(CONFIG_MAUI),m) - M_OBJS += maui.o - endif endif - -ifeq ($(CONFIG_SOFTOSS),y) -L_OBJS += softoss.o softoss_rs.o -else - ifeq ($(CONFIG_SOFTOSS),m) - M_OBJS += softoss2.o - endif endif -ifeq ($(CONFIG_OPL3SA1),y) -L_OBJS += opl3sa.o -LX_OBJS += ad1848.o -else - ifeq ($(CONFIG_OPL3SA1),m) - M_OBJS += opl3sa.o - MX_OBJS += ad1848.o - endif -endif -ifeq ($(CONFIG_VMIDI),y) -L_OBJS += v_midi.o -else - ifeq ($(CONFIG_VMIDI),m) - M_OBJS += v_midi.o - endif -endif +# Declare multi-part drivers. + +list-multi := sound.o gus.o pas2.o sb.o softoss2.o vidc_mod.o \ + soundcore.o wavefront.o + +sound-objs := \ + dev_table.o soundcard.o sound_syms.o \ + audio.o audio_syms.o dmabuf.o \ + midi_syms.o midi_synth.o midibuf.o \ + sequencer.o sequencer_syms.o sound_timer.o sys_timer.o -ifeq ($(CONFIG_VIDC_SOUND),y) - L_OBJS += vidc.o vidc_audio.o vidc_mixer.o vidc_synth.o vidc_fill.o +soundcore-objs := sound_core.o sound_firmware.o + +gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o +pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o +sb-objs := sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o +softoss2-objs := softoss.o softoss_rs.o +vidc_mod-objs := vidc.o vidc_audio.o vidc_fill.o vidc_mixer.o vidc_synth.o +wavefront-objs := wavfront.o wf_midi.o yss225.o + + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + + + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + + + +# Set flags for secondary drivers. +# I have to do this before I reduce obj-y to components. + +EXTRA_CFLAGS := $(sort \ + $(patsubst ad1848.o, -DCONFIG_SOUND_AD1848, \ + $(patsubst mpu401.o, -DCONFIG_SOUND_MPU_EMU, \ + $(patsubst sb.o, -DCONFIG_SOUND_SBDSP, \ + $(patsubst uart401.o, -DCONFIG_SOUND_UART401, \ + $(filter ad1848.o mpu401.o sb.o uart401.o, $(obj-y)) \ + ))))) + + + +# Take multi-part drivers out of obj-y and put components in. + +obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) + + + +# Translate to Rules.make lists. + +L_TARGET := sound.a +# This is a nice idea but needs depmod altering +#MOD_LIST_NAME := SOUND_MODULES + +L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +LX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +ifeq ($(CONFIG_LOWLEVEL_SOUND),y) + L_OBJS += lowlevel/lowlevel.o endif include $(TOPDIR)/Rules.make -softoss2.o: softoss.o softoss_rs.o - $(LD) -r -o softoss2.o softoss.o softoss_rs.o -pas2.o: pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o - $(LD) -r -o pas2.o pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o -sb.o: sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o - $(LD) -r -o sb.o sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o +# Link rules for multi-part drivers. + +sound.o: $(sound-objs) + $(LD) -r -o $@ $(sound-objs) + +soundcore.o: $(soundcore-objs) + $(LD) -r -o $@ $(soundcore-objs) -lowlevel/lowlevel.o: - cd lowlevel; make +gus.o: $(gus-objs) + $(LD) -r -o $@ $(gus-objs) -sound.o: soundcard.o dev_table.o audio.o dmabuf.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o sound_syms.o - $(LD) -r -o sound.o soundcard.o dev_table.o audio.o dmabuf.o \ - sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o \ - midi_synth.o midibuf.o sound_firmware.o sound_syms.o +pas2.o: $(pas2-objs) + $(LD) -r -o $@ $(pas2-objs) -gus.o: gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o - $(LD) -r -o gus.o gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o +sb.o: $(sb-objs) + $(LD) -r -o $@ $(sb-objs) + +softoss2.o: $(softoss2-objs) + $(LD) -r -o $@ $(softoss2-objs) + +vidc_mod.o: $(vidc_mod-objs) + $(LD) -r -o $@ $(vidc_mod-objs) + +wavefront.o: $(wavefront-objs) + $(LD) -r -o $@ $(wavefront-objs) # 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. +# +# First make the utilities. bin2hex: bin2hex.c - $(HOSTCC) -o bin2hex bin2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o bin2hex bin2hex.c hex2hex: hex2hex.c - $(HOSTCC) -o hex2hex hex2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o hex2hex hex2hex.c + -ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) -CONFIG_MAUI_BOOT_FILE := $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) + +# Turtle Beach Maui / Tropez maui.o: maui_boot.h -maui_boot.h: $(CONFIG_MAUI_BOOT_FILE) bin2hex - bin2hex -i maui_os < "$(CONFIG_MAUI_BOOT_FILE)" > $@ +ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) + maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) bin2hex + bin2hex -i maui_os < $(CONFIG_MAUI_BOOT_FILE) > $@ +else + maui_boot.h: + ( \ + echo 'static unsigned char * maui_os = NULL;'; \ + echo 'static int maui_osLen = 0;'; \ + ) > $@ +endif @ ( \ - echo 'ifeq ($(strip $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_BOOT_FILE)))'; \ + echo 'ifeq ($(strip $(CONFIG_MAUI_HAVE_BOOT) $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_HAVE_BOOT) $$(CONFIG_MAUI_BOOT_FILE)))'; \ echo 'FILES_BOOT_UP_TO_DATE += $@'; \ echo 'endif' \ ) > .$@.boot -endif -ifeq ($(CONFIG_PSS_HAVE_BOOT),y) -CONFIG_PSS_BOOT_FILE := $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) +# PSS (ECHO-ADI2111) pss.o: pss_boot.h -pss_boot.h: $(CONFIG_PSS_BOOT_FILE) bin2hex - bin2hex pss_synth < "$(CONFIG_PSS_BOOT_FILE)" > $@ +ifeq ($(CONFIG_PSS_HAVE_BOOT),y) + pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) bin2hex + ./bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@ +else + pss_boot.h: + ( \ + echo 'static unsigned char * pss_synth = NULL;'; \ + echo 'static int pss_synthLen = 0;'; \ + ) > $@ +endif @ ( \ - echo 'ifeq ($(strip $(CONFIG_PSS_BOOT_FILE)),$$(strip $$(CONFIG_PSS_BOOT_FILE)))'; \ + echo 'ifeq ($(strip $(CONFIG_PSS_HAVE_BOOT) $(CONFIG_PSS_BOOT_FILE)),$$(strip $$(CONFIG_PSS_HAVE_BOOT) $$(CONFIG_PSS_BOOT_FILE)))'; \ echo 'FILES_BOOT_UP_TO_DATE += $@'; \ echo 'endif' \ ) > .$@.boot -endif -ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) -CONFIG_TRIX_BOOT_FILE := $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) +# MediaTrix AudioTrix Pro trix.o: trix_boot.h -trix_boot.h: $(CONFIG_TRIX_BOOT_FILE) hex2hex - hex2hex -i trix_boot < "$(CONFIG_TRIX_BOOT_FILE)" > $@ +ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) + trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) hex2hex + hex2hex -i trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@ +else + trix_boot.h: + ( \ + echo 'static unsigned char * trix_boot = NULL;'; \ + echo 'static int trix_boot_len = 0;'; \ + ) > $@ +endif @ ( \ - echo 'ifeq ($(strip $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_BOOT_FILE)))'; \ + echo 'ifeq ($(strip $(CONFIG_TRIX_HAVE_BOOT) $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_HAVE_BOOT) $$(CONFIG_TRIX_BOOT_FILE)))'; \ echo 'FILES_BOOT_UP_TO_DATE += $@'; \ echo 'endif' \ ) > .$@.boot -endif + # Find boot files whose source file names have changed and force rebuild. @@ -329,4 +296,3 @@ FILES_BOOT_CHANGED := $(strip \ ifneq ($(FILES_BOOT_CHANGED),) $(FILES_BOOT_CHANGED): dummy endif -endif diff --git a/drivers/sound/README.CONFIG b/drivers/sound/README.CONFIG new file mode 100644 index 000000000..5cd199230 --- /dev/null +++ b/drivers/sound/README.CONFIG @@ -0,0 +1,75 @@ +Sound Driver Configuration Notes +Michael Chastain, <mailto:mec@shout.net> +18 Apr 1998 + +The Linux sound driver is derived from OSS/Free, a multi-platform +Unix sound driver by Hannu Savolainen. You can find out +more about OSS/Free and the commercial version, OSS/Linux, at +<http://www.opensound.com/ossfree>. + +OSS/Free comes with the configuration program 'configure.c'. We have +discarded that program in favor of a standard Linux configuration file +Config.in. + +Config.in defines a set of symbols with the form CONFIG_SOUND_*. +These are the -native symbols-. Here is a description: + + CONFIG_SOUND + + This is the master symbol. It controls whether the basic + sound-driver code is resident, modular, or not present at all. + + If the basic driver is resident, each primary and secondary + driver can be resident, modular, or not present. + + If the basic driver is modular, each primary and secondary driver + can be modular or not present. + + And if the basic driver is not present, all other drivers are + not present, too. + + Primary drivers + + These are symbols such as CONFIG_SOUND_SB, CONFIG_SOUND_SB_MODULE, + CONFIG_SOUND_TRIX, or CONFIG_SOUND_TRIX_MODULE. Each driver + that the user can directly select is a primary driver and has + the usual pair of symbols: one resident and one modular. + + Each primary driver can be either resident or modular. + + Secondary drivers + + Primary drivers require the support of secondary drivers, such + as ad1848.o and uart401.o. + + In Makefile, each primary driver has a list of required secondary + drivers. The secondary driver requirements are merged and a + single definition is emitted at the end. + + For each secondary driver: if any resident primary driver + requires it, that secondary driver will be resident. If no + resident primary driver requires it but some modular primary + driver requires it, then that secondary driver will be modular. + Otherwise that secondary driver will be not present. + + OSS/Free also contains tests for secondary drivers. The Makefile + defines symbols for these drivers in EXTRA_CFLAGS. + + CONFIG_AUDIO, CONFIG_MIDI, CONFIG_SEQUENCER + + These three drivers are like secondary drivers, but not quite. + They can not yet be separated into modules. They are always + linked into the basic sound driver, whether they are needed + or not. (This is in case a primary driver is added to the + system later, as a module, and needs these facilities. If it + were possible to modularise them, then they would get built as + additional modules at that time). + +The OSS/Free code does not use the native symbols directly, primarily +because it does not know about modules. I could edit the code, but that +would make it harder to upgrade to new versions of OSS/Free. Instead, +the OSS/Free code continues to use -legacy symbols-. + +legacy.h defines all the legacy symbols to 1. This is because, whenever +OSS/Free tests a symbol, the Makefile has already arranged for that +driver to be included. diff --git a/drivers/sound/README.wavefront b/drivers/sound/README.wavefront new file mode 100644 index 000000000..9e39f5d8d --- /dev/null +++ b/drivers/sound/README.wavefront @@ -0,0 +1,375 @@ + An OSS/Free Driver for WaveFront Sound Cards + (Turtle Beach Maui, Tropez, Tropez Plus) + + Paul Barton-Davis, July 1998 + + VERSION 0.2.2 + +Driver Status +------------- + +Requires: Kernel 2.1.106 or later + +As of 7/13/1998, this driver is currently in *BETA* state. This means +that it compiles and runs, and that I use it on my system (Linux +2.1.106) with some reasonably demanding applications and uses. I +believe the code is approaching an initial "finished" state that +provides bug-free support for the Tropez Plus. + +Please note that to date, the driver has ONLY been tested on a Tropez +Plus. I would very much like to hear (and help out) people with Tropez +and Maui cards, since I think the driver can support those cards as +well. + +Finally, the driver has not been tested as a static (non-modular) part +of the kernel. Alan Cox's good work in modularizing OSS/Free for Linux +makes this rather unnecessary. + +Some Questions +-------------- + +********************************************************************** +0) What does this driver do that the maui driver did not ? +********************************************************************** + +* can fully initialize a WaveFront card from cold boot - no DOS + utilities needed +* working patch/sample/program loading and unloading (the maui + driver didn't document how to make this work, and assumed + user-level preparation of the patch data for writing + to the board. ick.) +* full user-level access to all WaveFront commands +* for the Tropez Plus, (primitive) control of the YSS225 FX processor +* Virtual MIDI mode supported - 2 MIDI devices accessible via the + WaveFront's MPU401/UART emulation. One + accesses the WaveFront synth, the other accesses the + external MIDI connector. Full MIDI read/write semantics + for both devices. +* OSS-compliant /dev/sequencer interface for the WaveFront synth, + including native and GUS-format patch downloading. +* semi-intelligent patch management (prototypical at this point) + + +********************************************************************** +1) What to do about MIDI interfaces ? +********************************************************************** + +The Tropez Plus (and perhaps other WF cards) can in theory support up +to 2 physical MIDI interfaces. One of these is connected to the +ICS2115 chip (the WaveFront synth itself) and is controlled by +MPU/UART-401 emulation code running as part of the WaveFront OS. The +other is controlled by the CS4232 chip present on the board. However, +physical access to the CS4232 connector is difficult, and it is +unlikely (though not impossible) that you will want to use it. + +An older version of this driver introduced an additional kernel config +variable which controlled whether or not the CS4232 MIDI interface was +configured. Because of Alan Cox's work on modularizing the sound +drivers, and now backporting them to 2.0.34 kernels, there seems to be +little reason to support "static" configuration variables, and so this +has been abandoned in favor of *only* module parameters. Specifying +"mpuio" and "mpuirq" for the cs4232 parameter will result in the +CS4232 MIDI interface being configured; leaving them unspecified will +leave it unconfigured (and thus unusable). + +BTW, I have heard from one Tropez+ user that the CS4232 interface is +more reliable than the ICS2115 one. I have had no problems with the +latter, and I don't have the right cable to test the former one +out. Reports welcome. + +********************************************************************** +2) Why does line XXX of the code look like this .... ? +********************************************************************** + +Either because its not finished yet, or because you're a better coder +than I am, or because you don't understand some aspect of how the card +or the code works. + +I absolutely welcome comments, criticisms and suggestions about the +design and implementation of the driver. + +********************************************************************** +3) What files are included ? +********************************************************************** + + drivers/sound/README.wavefront -- this file + drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers + needed to make the rest of this work + drivers/sound/wavfront.c -- the driver + drivers/sound/ys225.h -- data declarations for FX config + drivers/sound/ys225.c -- data definitions for FX config + drivers/sound/wf_midi.c -- the "uart401" driver + to support virtual MIDI mode. + include/wavefront.h -- the header file + Documentation/sound/Tropez+ -- short docs on configuration + +********************************************************************** +4) How do I compile/install/use it ? +********************************************************************** + +PART ONE: install the source code into your sound driver directory + + cd <top-of-your-2.1.106-code-base-e.g.-/usr/src/linux> + tar -zxvf <where-you-put/wavefront.tar.gz> + +PART TWO: apply the patches + + cd drivers/sound + patch < wavefront.patch + +PART THREE: configure your kernel + + cd <top of your kernel tree> + make xconfig (or whichever config option you use) + + - choose YES for Sound Support + - choose MODULE (M) for OSS Sound Modules + - choose MODULE(M) to YM3812/OPL3 support + - choose MODULE(M) for WaveFront support + - choose MODULE(M) for CS4232 support + + - choose "N" for everything else (unless you have other + sound cards you want support for) + + + make dep + make boot + . + . + . + <whatever you normally do for a kernel install> + make modules + . + . + . + make modules_install + +Here's my autoconf.h SOUND section: + +/* + * Sound + */ +#define CONFIG_SOUND 1 +#undef CONFIG_SOUND_OSS +#define CONFIG_SOUND_OSS_MODULE 1 +#undef CONFIG_SOUND_PAS +#undef CONFIG_SOUND_SB +#undef CONFIG_SOUND_ADLIB +#undef CONFIG_SOUND_GUS +#undef CONFIG_SOUND_MPU401 +#undef CONFIG_SOUND_PSS +#undef CONFIG_SOUND_MSS +#undef CONFIG_SOUND_SSCAPE +#undef CONFIG_SOUND_TRIX +#undef CONFIG_SOUND_MAD16 +#undef CONFIG_SOUND_WAVEFRONT +#define CONFIG_SOUND_WAVEFRONT_MODULE 1 +#undef CONFIG_SOUND_CS4232 +#define CONFIG_SOUND_CS4232_MODULE 1 +#undef CONFIG_SOUND_MAUI +#undef CONFIG_SOUND_SGALAXY +#undef CONFIG_SOUND_OPL3SA1 +#undef CONFIG_SOUND_SOFTOSS +#undef CONFIG_SOUND_YM3812 +#define CONFIG_SOUND_YM3812_MODULE 1 +#undef CONFIG_SOUND_VMIDI +#undef CONFIG_SOUND_UART6850 +/* + * Additional low level sound drivers + */ +#undef CONFIG_LOWLEVEL_SOUND + +************************************************************ +6) How do I configure my card ? +************************************************************ + +You need to edit /etc/conf.modules. Here's mine (edited to show the +relevant details): + + # Sound system + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "opl3" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the adlib_card module if you don't + want to use the OPL/[34] synth on the sound card + + the adlib_card io parameter is conventionally not adjustable. + In theory, any not-in-use IO port address would work, but + just use 0x388 and stick with the crowd. + +********************************************************************** +7) What about firmware ? +********************************************************************** + +Turtle Beach have not given me permission to distribute their firmware +for the ICS2115. However, if you have a WaveFront card, then you +almost certainly have the firmware, and if not, its freely available +on their website, at: + + http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus + +The file is called WFOS2001.MOT (for the Tropez+). + +This driver, however, doesn't use the pure firmware as distributed, +but instead relies on a somewhat processed form of it. You can +generate this very easily. Following an idea from Andrew Veliath's +Pinnacle driver, the following flex program will generate the +processed version: + +---- cut here ------------------------- +%option main +%% +^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); +<<EOF>> { fputc ('\0', stdout); return; } +\n {} +. {} +---- cut here ------------------------- + +To use it, put the above in file (say, ws.l) compile it like this: + + shell> flex -ows.c ws.l + shell> cc -o ws ws.c + +and then use it like this: + + ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os + +If you put it somewhere else, you'll always have to use the wf_ospath +module parameter (see below) or alter the source code. + +********************************************************************** +7) How do I get it working ? +********************************************************************** + +Optionally, you can reboot with the "new" kernel (even though the only +changes have really been made to a module). + +Then, as root do: + + modprobe wavefront + +You should get something like this in /var/log/messages: + + WaveFront: firmware 1.20 already loaded. + +or + + WaveFront: no response to firmware probe, assume raw. + +then: + + WaveFront: waiting for memory configuration ... + WaveFront: hardware version 1.64 + WaveFront: available DRAM 8191k + WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty + WaveFront: 128 programs slots in use + WaveFront: 256 patch slots filled, 142 in use + +The whole process takes about 16 seconds, the longest waits being +after reporting the hardware version (during the firmware download), +and after reporting program status (during patch status inquiry). Its +shorter (about 10 secs) if the firmware is already loaded (i.e. only +warm reboots since the last firmware load). + +The "available DRAM" line will vary depending on how much added RAM +your card has. Mine has 8MB. + +Next, check /dev/sndstat, which on my machine says: +--------------------------------------------------------------------- +OSS/Free:3.8s2++-971130 +Load type: Driver loaded as a module +Kernel: Linux bd 2.1.106 #12 SMP Fri Jul 3 00:37:34 EDT 1998 i486 +Config options: 0 + +Installed drivers: + +Card config: + +Audio devices: +0: Crystal audio controller (CS4232) (DUPLEX) + +Synth devices: +0: Turtle Beach WaveFront +1: Yamaha OPL-3 + +Midi devices: +0: WaveFront Internal MIDI +1: WaveFront External MIDI + +Timers: +0: System clock +1: Crystal audio controller (CS4232) + +Mixers: +0: Crystal audio controller (CS4232) +----------------------------------------------------------- + +To check basically functionality, use play(1) or splay(1) to send a +.WAV or other audio file through the audio portion. Then use playmidi +to play a General MIDI file. Try the "-D 0" to hear the +difference between sending MIDI to the WaveFront and using the OPL/3, +which is the default (I think ...). If you have an external synth(s) +hooked to the sound card, you can use "-e" to route to the +external synth(s) (in theory, -D 1 should work as well, but I think +there is a bug in playmidi which prevents this from doing what it +should). + +********************************************************************** +8) What are the module parameters ? +********************************************************************** + +Its best to read wavefront.c for this, but here is a summary: + +integers: + wf_raw - if set, ignore apparent presence of firmware + loaded onto the ICS2115, reset the whole + board, and initialize it from scratch. (default = 0) + + fx_raw - if set, always initialize the YSS225 processor + on the Tropez plus. (default = 1) + + < The next 4 are basically for kernel hackers to allow + tweaking the driver for testing purposes. > + + wf_short_wait_count - loop counter used when waiting for + status conditions on the board. This + is CPU-specific. After this many + loops, the driver will sleep. + The default is 5000. I have a 66Mhz 486. + + wf_sleep_interval - the driver sleeps for + HZ/wf_sleep_interval seconds per sleep. + The default is 50. + + wf_sleep_tries - the number of times the driver will sleep + when waiting for a status condition on the + board. The default is 100 (2 secs, if + wf_sleep_interval is 50). + + wf_debug_default - debugging flags. See sound/wavefront.h + for WF_DEBUG_* values. Default is zero. + Setting this allows you to debug the + driver during module installation. +strings: + wf_ospath - path to get to the pre-processed OS firmware. + (default: /etc/sound/wavefront.os) + +********************************************************************** +9) Who should I contact if I have problems? +********************************************************************** + +Just me: Paul Barton-Davis <pbd@op.net> + + diff --git a/drivers/sound/Readme b/drivers/sound/Readme index 9fb851c5f..2bb76549f 100644 --- a/drivers/sound/Readme +++ b/drivers/sound/Readme @@ -26,13 +26,13 @@ download instructions. If you are looking for the installation instructions, please look at Readme.linux. -Supported soundcards --------------------- +Supported sound cards +--------------------- See Readme.cards. Please check http://www.opensound.com/ossfree if you don't find -your soundcard there. +your sound card there. Contributors ------------ @@ -127,9 +127,9 @@ Also look at the home page (http://www.opensound.com/ossfree). It may contain info about some recent bug fixes. It's likely that you have some problems when trying to use the sound driver -first time. Soundcards don't have standard configuration so there are no +first time. Sound cards don't have standard configuration so there are no good default configuration to use. Please try to use same I/O, DMA and IRQ -values for the soundcard than with DOS. +values for the sound card than with DOS. If you get an error message when trying to use the driver, please look at /var/adm/messages for more verbose error message. @@ -159,7 +159,7 @@ The following errors are likely with /dev/dsp and /dev/audio. work if "digitized voice support" was not enabled during "make config". - "Device or resource busy". Probably the IRQ (or DMA) channel - required by the soundcard is in use by some other device/driver. + required by the sound card is in use by some other device/driver. - "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict. Look at the kernel messages in /var/adm/notice for more info. diff --git a/drivers/sound/Readme.cards b/drivers/sound/Readme.cards index f8a9bf2ca..45cad259e 100644 --- a/drivers/sound/Readme.cards +++ b/drivers/sound/Readme.cards @@ -1,7 +1,7 @@ -Configuring version 3.8 (for Linux) with some most common soundcards -==================================================================== +Configuring version 3.8 (for Linux) with some common sound cards +================================================================ -This document describes configuring soundcards with freeware version of +This document describes configuring sound cards with the freeware version of Open Sound Systems (OSS/Free). Information about the commercial version (OSS/Linux) and its configuration is available from http://www.opensound.com/linux.html. Information presented here is @@ -9,7 +9,7 @@ not valid for OSS/Linux. If you are unsure about how to configure OSS/Free you can download the free evaluation version of OSS/Linux from the above -address. There is a chance that it can autodetect your soundcard. In this case +address. There is a chance that it can autodetect your sound card. In this case you can use the information included in soundon.log when configuring OSS/Free. @@ -24,8 +24,8 @@ IMPORTANT! This document covers only cards that were "known" when offered by these programs are not necessarily valid. -THE BIGGEST MISTAKES YOU CAN DO -=============================== +THE BIGGEST MISTAKES YOU CAN MAKE +================================= 1. Assuming that the card is Sound Blaster compatible when it's not. -------------------------------------------------------------------- @@ -37,12 +37,12 @@ are few sound chipsets which are SB compatible in Linux such as ESS1688 or Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything in Linux. -IF YOU REALLY ARE 150% SURE YOU REALLY HAVE A SOUND BLASTER YOU CAN SKIP THE -REST OF THIS CHAPTER. +IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF +THIS CHAPTER. -For most other "supposed to be SB compatible" cards you have use other -than SB drivers (see below). It is possible to get most soundcards to work -in SB mode but in general it's complete waste of time. There are several +For most other "supposed to be SB compatible" cards you have to use other +than SB drivers (see below). It is possible to get most sound cards to work +in SB mode but in general it's a complete waste of time. There are several problems which you will encounter by using SB mode with cards that are not truly SB compatible: @@ -53,7 +53,7 @@ many users claim that sound quality in Linux is much worse than in DOS. In addition some applications require 16 bit mode and they produce just noise with a 8 bit only device. - The card may work only in some cases but refuse to work most of the -time. The SB compatible mode always requires special intialization which is +time. The SB compatible mode always requires special initialization which is done by the DOS/Windows drivers. This kind of cards work in Linux after you have warm booted it after DOS but they don't work after cold boot (power on or reset). @@ -64,8 +64,8 @@ get the above error message whenever you try to record or play. There are few other reasons to the DMA timeout message but using the SB mode seems to be the most common cause. -2. Trying to use a PnP (Plug & Play) card just like an ordinary soundcard -------------------------------------------------------------------------- +2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card +-------------------------------------------------------------------------- Plug & Play is a protocol defined by Intel and Microsoft. It lets operating systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA @@ -74,39 +74,39 @@ cards. The problem with PnP cards is that the standard Linux doesn't currently to use some special tricks (see later) to get a PnP card alive. Many PnP cards work after they have been initialized but this is not always the case. -There are usually both a non PnP and PnP versions of the same soundcard. -The non PnP version is the original model which usually has been discontinued -more than an year ago. The PnP version has the same name but with a PnP -appended to it (sometimes not). This causes major confusion since even the -non PnP model works with Linux the PnP one doesn't. +There are sometimes both PnP and non-PnP versions of the same sound card. +The non-PnP version is the original model which usually has been discontinued +more than an year ago. The PnP version has the same name but with "PnP" +appended to it (sometimes not). This causes major confusion since the non-PnP +model works with Linux but the PnP one doesn't. You should carefully check if "Plug & Play" or "PnP" is mentioned in the name of the card or in the documentation or package that came with the card. -Everything described in the rest of this document is not necessarily valid -for PnP models of soudcards even you have managed to wake up the card properly. -Many PnP cards are simply too much different than their original non PnP -ancestors which are covered by this document. +Everything described in the rest of this document is not necessarily valid for +PnP models of sound cards even you have managed to wake up the card properly. +Many PnP cards are simply too different from their non-PnP ancestors which are +covered by this document. Cards that are not (fully) supported by this driver =================================================== -See http://www.opensound.com/ossfree for information about soundcards +See http://www.opensound.com/ossfree for information about sound cards to be supported in future. How to use sound without recompiling kernel and/or sound driver =============================================================== -There is commercial sound driver which should be released during Apr 96. -It comes in precompiled form and doesn't require recompiling of kernel. See -http://www.4Front-tech.com/oss.html for more info. +There is a commercial sound driver which comes in precompiled form and doesn't +require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for +more info. Configuring PnP cards ===================== -New versions of most soundcards use so called ISA PnP protocol for +New versions of most sound cards use the so-called ISA PnP protocol for soft configuring their I/O, IRQ, DMA and shared memory resources. Currently at least cards made by Creative Technology (SB32 and SB32AWE PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and @@ -115,7 +115,7 @@ chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other motherboards) is also based on PnP technology but there is a "native" driver available for it (see information about CS4232 later in this document). -PnP soundcards (as well as most other PnP ISA cards) are not supported +PnP sound cards (as well as most other PnP ISA cards) are not supported by this version of the driver . Proper support for them should be released during 97 once the kernel level PnP support is available. @@ -123,36 +123,34 @@ PnP support is available. There is a method to get most of the PnP cards to work. The basic method is the following: -1) Boot DOS so that card's DOS drivers have chance to initialize the -card. -2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del -works with older machines but causes hard reset of all cards on latest +1) Boot DOS so the card's DOS drivers have a chance to initialize it. +2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del +works with older machines but causes a hard reset of all cards on recent (Pentium) machines. -3) If you have sound driver in Linux configured properly, the card should work -now. "Proper" means here that I/O, IRQ and DMA settings are the same than in -DOS. The hard part is to find which settings were used. See documentation of +3) If you have the sound driver in Linux configured properly, the card should +work now. "Proper" means that I/O, IRQ and DMA settings are the same as in +DOS. The hard part is to find which settings were used. See the documentation of your card for more info. -Windows 95 could work as well as DOS but running loadlin may be somehow -difficult. Probably you should "shut down" your machine to MS-DOS mode -before running it. +Windows 95 could work as well as DOS but running loadlin may be difficult. +Probably you should "shut down" your machine to MS-DOS mode before running it. -Some machines have BIOS utility for setting PnP resources. This is a good +Some machines have a BIOS utility for setting PnP resources. This is a good way to configure some cards. In this case you don't need to boot DOS/Win95 -prior starting Linux. +before starting Linux. Another way to initialize PnP cards without DOS/Win95 is a Linux based PnP isolation tool. When writing this there is a pre alpha test version -of such tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The +of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The file is called isapnptools-*. Please note that this tool is just a temporary solution which may be incompatible with future kernel versions having proper support for PnP cards. There are bugs in setting DMA channels in earlier -versions of isapnptools so at least version 1.6 is required with soundcards. +versions of isapnptools so at least version 1.6 is required with sound cards. -Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. -See http://www.opensound.com/linux.html for more info. This is the way -you probably like to do it if you don't waste hours of time in recompiling -kernel and the required tools. +Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. See +http://www.opensound.com/linux.html for more info. This is probably the way you +should do it if you don't want to spend time recompiling the kernel and +required tools. Read this before trying to configure the driver @@ -188,7 +186,7 @@ Sound Blasters using isapnptools before they work with OSS/Free. SB16 compatible cards by other manufacturers than Creative. You have been fooled since there are _no_ SB16 compatible - cards on the market (May 97). It's likely that your card + cards on the market (as of May 1997). It's likely that your card is compatible just with SB Pro but there is also a non-SB- compatible 16 bit mode. Usually it's MSS/WSS but it could also be a proprietary one like MV Jazz16 or ESS ES688. OPTi @@ -206,7 +204,7 @@ Sound Blasters Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or "Crystal" are _NOT_ SB compatible in Linux. - Practically all soundcards have some kind of SB emulation mode + Practically all sound cards have some kind of SB emulation mode in addition to their native (16 bit) mode. In most cases this (8 bit only) SB compatible mode doesn't work with Linux. If you get it working it may cause problems with games and @@ -231,7 +229,7 @@ MPU-401 and compatibles The driver works both with the full (intelligent mode) MPU-401 cards (such as MPU IPC-T and MQX-32M) and with the UART only dumb MIDI ports. MPU-401 is currently the most common MIDI - interface. Most soundcards are compatible with it. However, + interface. Most sound cards are compatible with it. However, don't enable MPU401 mode blindly. Many cards with native support in the driver have their own MPU401 driver. Enabling the standard one will cause a conflict with these cards. So check if your card is @@ -242,7 +240,7 @@ Windows Sound System (MSS/WSS) they managed to make it a standard. MSS compatible cards are based on a codec chip which is easily available from at least two manufacturers (AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor). - Currently most soundcards are based on one of the MSS compatible codec + Currently most sound cards are based on one of the MSS compatible codec chips. The CS4231 is used in the high quality cards such as GUS MAX, MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). @@ -256,7 +254,7 @@ Windows Sound System (MSS/WSS) enabling the MSS support. Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) - Most soundcards have a FM synthesizer chip. The OPL2 is a 2 + Most sound cards have a FM synthesizer chip. The OPL2 is a 2 operator chip used in the original AdLib card. Currently it's used only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator FM chip which provides better sound quality and/or more available @@ -280,7 +278,7 @@ Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) Yamaha OPL3-SA1 Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some - (Intel) motherboards and on cheap soundcards. It should not be + (Intel) motherboards and on cheap sound cards. It should not be confused with the original OPL3 chip (YMF278) which is entirely different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro (not used in OSS/Free) in addition to the OPL3 FM synth. @@ -293,7 +291,7 @@ Yamaha OPL3-SA1 4Front Technologies SoftOSS SoftOSS is a software based wave table emulation which works with - any 16 bit stereo soundcard. Due to its nature a fast CPU is + any 16 bit stereo sound card. Due to its nature a fast CPU is required (P133 is minimum). Although SoftOSS does _not_ use MMX instructions it has proven out that recent processors (which appear to have MMX) perform significantly better with SoftOSS than earlier @@ -314,8 +312,8 @@ Yamaha OPL3-SA1 higher sampling rates. However using fewer voices decreases playback quality more than decreasing the sampling rate. - SoftOSS keeps the samples loaded on system's RAM so large RAM is - required. SoftOSS should never be used on machines with less than 16M + SoftOSS keeps the samples loaded on the system's RAM so much RAM is + required. SoftOSS should never be used on machines with less than 16 MB of RAM since this is potentially dangerous (you may accidently run out of memory which probably crashes the machine). @@ -330,14 +328,14 @@ Yamaha OPL3-SA1 sites. ********************************************************************* - IMPORTANT NOTICE! The original patch set distributed with Gravis + IMPORTANT NOTICE! The original patch set distributed with the Gravis Ultrasound card is not in public domain (even though it's available from - some ftp sites). You should contact Voice Crystal (www.voicecrystal.com) + some FTP sites). You should contact Voice Crystal (www.voicecrystal.com) if you like to use these patches with SoftOSS included in OSS/Free. ********************************************************************* PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) - Analog Devices and Echo Speech have together defined a soundcard + Analog Devices and Echo Speech have together defined a sound card architecture based on the above chips. The DSP chip is used for emulation of SB Pro, FM and General MIDI/MT32. @@ -355,19 +353,20 @@ MediaTrix AudioTrix Pro General MIDI emulator. There is also a SB 1.5 compatible playback mode. Ensoniq SoundScape and compatibles - Ensoniq has designed a soundcard architecture based on the + Ensoniq has designed a sound card architecture based on the OTTO synthesizer chip used in their professional MIDI synthesizers. Several companies (including Ensoniq, Reveal and Spea) are selling cards based on this architecture. NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and - VIVO90 cards are not compatible with Soundscapes so the Soundscape driver - will not work with them. You may want to use OSS/Linux with these cards. + VIVO90 cards are not compatible with Soundscapes so the Soundscape + driver will not work with them. You may want to use OSS/Linux with these + cards. OPTi MAD16 and Mozart based cards The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface - chips are used in many different soundcards, including some + chips are used in many different sound cards, including some cards by Reveal miro and Turtle Beach (Tropez). The purpose of these chips is to connect other audio components to the PC bus. The interface chip performs address decoding for the other chips. @@ -394,8 +393,8 @@ Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ an SB and MPU401 emulations. There is support for OPL3 too. Unfortunately the MPU401 mode doesn't work (I don't know how to initialize it). CS4236 is an enhanced (compatible) version of CS4232. - NOTE! Don't ever try to use isapnptools with CS4232 since this just - freezes your machine (due to chip bugs). If you have problems in getting + NOTE! Don't ever try to use isapnptools with CS4232 since this will just + freeze your machine (due to chip bugs). If you have problems in getting CS4232 working you could try initializing it with DOS (CS4232C.EXE) and then booting Linux using loadlin. CS4232C.EXE loads a secret firmware patch which is not documented by Crystal. @@ -413,7 +412,7 @@ Turtle Beach Maui and Tropez "classic" Jumpers and software configuration ================================== -Some of the earliest soundcards were jumper configurable. You have to +Some of the earliest sound cards were jumper configurable. You have to configure the driver use I/O, IRQ and DMA settings that match the jumpers. Just few 8 bit cards are fully jumper configurable (SB 1.x/2.x, SB Pro and clones). @@ -436,7 +435,7 @@ when installing new hardware to the machine). Sound driver sets the soft configurable parameters of the card automatically during boot. Usually you don't need to run any extra initialization programs when booting Linux but there are some exceptions. See the -card specific instructions (below) for more info. +card-specific instructions below for more info. The drawback of software configuration is that the driver needs to know how the card must be initialized. It cannot initialize unknown cards @@ -448,7 +447,7 @@ What if your card was not listed above? ======================================= The first thing to do is to look at the major IC chips on the card. -Many of the latest soundcards are based on some standard chips. If you +Many of the latest sound cards are based on some standard chips. If you are lucky, all of them could be supported by the driver. The most common ones are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures listed above. Also look at the end of this file for list of unsupported @@ -458,21 +457,21 @@ The last resort is to send _exact_ name and model information of the card to me together with a list of the major IC chips (manufactured, model) to me. I could then try to check if your card looks like something familiar. -There are much more cards in the word than listed above. The first thing to -do with these cards is to check if they emulate some other card/interface +There are many more cards in the world than listed above. The first thing to +do with these cards is to check if they emulate some other card or interface such as SB, MSS and/or MPU401. In this case there is a chance to get the card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del and boot Linux without hard resetting the machine). In this method the -DOS based driver initializes the hardware to use a known I/O, IRQ and DMA -settings. If sound driver is configured to use the same settings, everything should -work OK. +DOS based driver initializes the hardware to use known I/O, IRQ and DMA +settings. If sound driver is configured to use the same settings, everything +should work OK. Configuring sound driver (with Linux) ===================================== -Sound driver is currently a part of Linux kernel distribution. The -driver files are located in directory /usr/src/linux/drivers/sound. +The sound driver is currently distributed as part of the Linux kernel. The +files are in /usr/src/linux/drivers/sound/. **************************************************************************** * ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * @@ -491,9 +490,9 @@ driver files are located in directory /usr/src/linux/drivers/sound. **************************************************************************** To configure the driver, run "make config" in the kernel source directory -(/usr/src/linux). Answer y to the question about Sound card support (after -questions about mouse, CD-ROM, ftape, etc. supports). Sound config options -will then be asked after some additional questions. +(/usr/src/linux). Answer "y" or "m" to the question about Sound card support +(after the questions about mouse, CD-ROM, ftape, etc. support). Questions +about options for sound will then be asked. After configuring the kernel and sound driver, run "make dep" and compile the kernel following instructions in the kernel README. @@ -501,9 +500,9 @@ the kernel following instructions in the kernel README. The sound driver configuration dialog ------------------------------------- -If you already have the sound driver installed, consult printout of +If you already have the sound driver installed, consult a printout of "cat /dev/sndstat" when configuring the driver again. It gives the I/O, -IRQ and DMA settings you have used earlier. +IRQ and DMA settings you used earlier. Sound configuration starts by making some yes/no questions. Be careful when answering to these questions since answering y to a question may @@ -559,7 +558,7 @@ select some options automatically as well. know what to answer with it. "MPU-401 support (NOT for SB16)", - Be careful with this question. The MPU401 interface is supported - by almost any soundcard today. However some natively supported cards + by almost any sound card today. However some natively supported cards have their own driver for MPU401. Enabling the 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 @@ -569,7 +568,7 @@ select some options automatically as well. In MOST cases this MPU401 driver should only be used with "true" MIDI-only MPU401 professional cards. In most other cases there is another way to get the MPU401 compatible interface of a - soundcard to work. + sound card to work. Support for the MPU401 compatible MIDI port of SB16, ESS1688 and MV Jazz16 cards is included in the SB driver. Use it instead of this separate MPU401 driver with these cards. As well @@ -607,9 +606,9 @@ select some options automatically as well. channels with cards that don't support this feature will prevent audio (at least recording) from working. "Ensoniq Soundscape support", - - Answer 'y' if you have a soundcard based on the Ensoniq SoundScape + - Answer 'y' if you have a sound card based on the Ensoniq SoundScape chipset. Such cards are being manufactured at least by Ensoniq, - Spea and Reveal (note that Reveal makes other cards also). Oldest + Spea and Reveal (note that Reveal makes other cards also). The oldest cards made by Spea don't work properly with Linux. Soundscape PnP as well as Ensoniq VIVO work only with the commercial OSS/Linux version. @@ -695,7 +694,7 @@ first configure OSS/Free for one card. Then edit local.h manually and define SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get the OPL3, MIDI and EMU8000 devices of the second card to work. If you are going to use two PnP Sound Blasters, ensure that they are of different model -and have different PnP ID's. There is no way to get two cards with the same +and have different PnP IDs. There is no way to get two cards with the same card ID and serial number to work. The easiest way to check this is trying if isapnptools can see both cards or just one. @@ -710,7 +709,7 @@ First of all: There are no SB16 clones. There are SB Pro clones with a 16 bit mode which is not SB16 compatible. The most likely alternative is that the 16 bit mode means MSS/WSS. -There are just few fully 100% hardware SB or SB Pro compatible cards. +There are just a few fully 100% hardware SB or SB Pro compatible cards. I know just Thunderboard and SM Games. Other cards require some kind of hardware initialization before they become SB compatible. Check if your card was listed in the beginning of this file. In this case you should follow @@ -860,7 +859,7 @@ Configuring ATP is little bit tricky since it uses so many I/O, IRQ and DMA numbers. Using the same values than with DOS/Win is a good idea. Don't attempt to use the same IRQ or DMA channels twice. -The SB mode of ATP is implemented so the the ATP driver just enables SB +The SB mode of ATP is implemented so the ATP driver just enables SB in the proper address. The SB driver handles the rest. You have to configure both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O settings. @@ -1004,7 +1003,7 @@ Logitech Soundman Wave Read the above MV Jazz specific instructions first. -The Logitech SoundMan Wave (don't confuse with the SM16 or SM Games) is +The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is a MV Jazz based card which has an additional OPL4 based wave table synthesizer. The OPL4 chip is handled by an on board microcontroller which must be initialized during boot. The config program asks if @@ -1053,7 +1052,7 @@ with ES688). NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support of OSS doesn't work with it. -There are some ES1688/688 based soundcards and (particularily) motherboards +There are some ES1688/688 based sound cards and (particularily) motherboards which use software configurable I/O port relocation feature of the chip. This ESS proprietary feature is supported only by OSS/Linux. @@ -1070,12 +1069,12 @@ Reveal cards There are several different cards made/marketed by Reveal. Some of them are compatible with SoundScape and some use the MAD16 chip. You may have -to look at the card and try to identify origin of the card. +to look at the card and try to identify its origin. Diamond ------- -The oldest (Sierra Aria based) soundcards made by Diamond are not supported +The oldest (Sierra Aria based) sound cards made by Diamond are not supported (they may work if the card is initialized using DOS). The recent (LX?) models are based on the MAD16 chip which is supported by the driver. @@ -1150,7 +1149,7 @@ make menuconfig/xconfig. Others? ------- -Since there are so many different soundcards, it's likely that I have +Since there are so many different sound cards, it's likely that I have forgotten to mention many of them. Please inform me if you know yet another card which works with Linux, please inform me (or is anybody else willing to maintain a database of supported cards (just like in XF86)?). @@ -1158,18 +1157,18 @@ willing to maintain a database of supported cards (just like in XF86)?). Cards not supported yet ======================= -Please check which version of sound driver you are using before -complaining that your card is not supported. It's possible that you are +Please check the version of sound driver you are using before +complaining that your card is not supported. It's possible you are using a driver version which was released months before your card was -introduced. Driver's release date is listed after its version number -in "cat /dev/sndstat" printout and in file linux/drivers/sound/soundvers.h. +introduced. The driver's release date is listed after its version number in a +"cat /dev/sndstat" printout and in the file linux/drivers/sound/soundvers.h. -First of all. There is an easy way to make most soundcards to work -with Linux. Just use the DOS based driver to initialize the card -to a _known_ state. Then use loadlin.exe to boot Linux. If Linux is configured -to use the same I/O, IRQ and DMA numbers than DOS, the card could work. +First of all, there is an easy way to make most sound cards work with Linux. +Just use the DOS based driver to initialize the card to a known state, then use +loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and +DMA numbers as DOS, the card could work. (ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with -new motherboards). This method works also with all/most PnP soundcards. +new motherboards). This method works also with all/most PnP sound cards. Don't get fooled with SB compatibility. Most cards are compatible with SB but that may require a TSR which is not possible with Linux. If @@ -1178,7 +1177,7 @@ don't work in the SB and MSS modes at the same time. Then there are cards which are no longer manufactured and/or which are relatively rarely used (such as the 8 bit ProAudioSpectrum -models). It's extremely unlikely that such cards never get supported. +models). It's extremely unlikely that such cards ever get supported. Adding support for a new card requires much work and increases time required in maintaining the driver (some changes need to be done to all low level drivers and be tested too, maybe with multiple @@ -1213,8 +1212,8 @@ get some support in future but I can't make any promises. Just look at the home page (http://www.opensound.com/ossfree/new_cards.html) for latest info. -Information about unsupported soundcards and chipsets is welcome as well -as free copies of soundcards, SDKs and operating systems. +Information about unsupported sound cards and chipsets is welcome as well +as free copies of sound cards, SDKs and operating systems. If you have any corrections and/or comments, please contact me. @@ -1222,7 +1221,7 @@ Hannu Savolainen hannu@opensound.com Personal home page: http://www.compusonic.fi/~hannu -www home page of OSS/Free: http://www.opensound.com/ossfree +home page of OSS/Free: http://www.opensound.com/ossfree -www home page of commercial OSS +home page of commercial OSS (Open Sound System) drivers: http://www.opensound.com/oss.html diff --git a/drivers/sound/Readme.linux b/drivers/sound/Readme.linux index b3fa3a833..97fe386b3 100644 --- a/drivers/sound/Readme.linux +++ b/drivers/sound/Readme.linux @@ -28,7 +28,7 @@ IMPORTANT! Read this if you are installing a separately sound support during "make config"). Please refer to kernel documentation for instructions about configuring and compiling kernel. File Readme.cards contains card specific instructions for configuring this driver for - use with various soundcards. + use with various sound cards. Boot time configuration (using lilo and insmod) ----------------------------------------------- diff --git a/drivers/sound/Readme.modules b/drivers/sound/Readme.modules index aed47ee16..9740035b4 100644 --- a/drivers/sound/Readme.modules +++ b/drivers/sound/Readme.modules @@ -29,9 +29,9 @@ modules in the usual way. Then, add to your /etc/modules.conf or /etc/conf.modules something like: alias char-major-14 sb -post-install sb modprobe "-k" "adlib_card" +post-install sb /sbin/modprobe "-k" "adlib_card" options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 -options adlib_card io=0x388 # FM synthetiser +options adlib_card io=0x388 # FM synthesizer The effect of this is that the sound driver and all necessary bits and pieces autoload on demand, assuming you use kerneld (a sound choice) and diff --git a/drivers/sound/ad1848.c b/drivers/sound/ad1848.c index 2bf9f2c52..34148d990 100644 --- a/drivers/sound/ad1848.c +++ b/drivers/sound/ad1848.c @@ -105,7 +105,7 @@ static volatile char irq2dev[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; -#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) || defined(MODULE) static int timer_installed = -1; @@ -141,7 +141,7 @@ static void ad1848_halt_input(int dev); static void ad1848_halt_output(int dev); static void ad1848_trigger(int dev, int bits); -#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE) +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) static int ad1848_tmr_install(int dev); static void ad1848_tmr_reprogram(int dev); @@ -1048,7 +1048,7 @@ static int ad1848_prepare_for_output(int dev, int bsize, int bcount) restore_flags(flags); devc->xfer_count = 0; -#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE) +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) if (dev == timer_installed && devc->timer_running) if ((fs & 0x01) != (old_fs & 0x01)) { @@ -1492,7 +1492,7 @@ int ad1848_detect(int io_base, int *ad_flags, int *osp) } /* - * The indirect register I12 has some read only bits. Lets + * The indirect register I12 has some read only bits. Let's * try to change them. */ @@ -1781,6 +1781,7 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt } audio_devs[my_dev]->portc = portc; + audio_devs[my_dev]->mixer_dev = -1; memset((char *) portc, 0, sizeof(*portc)); nr_ad1848_devs++; @@ -1828,7 +1829,7 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt } else if (irq < 0) irq2dev[-irq] = devc->dev_no = my_dev; -#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE) +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) if (devc->model != MD_1848 && devc->model != MD_C930 && devc->irq_ok) ad1848_tmr_install(my_dev); @@ -1913,7 +1914,7 @@ int ad1848_control(int cmd, int arg) void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma) { - int i, dev = 0; + int i, mixer, dev = 0; ad1848_info *devc = NULL; for (i = 0; devc == NULL && i < nr_ad1848_devs; i++) @@ -1941,6 +1942,9 @@ void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma) sound_free_dma(audio_devs[dev]->dmap_in->dma); } + mixer = audio_devs[devc->dev_no]->mixer_dev; + if(mixer>=0) + sound_unload_mixerdev(mixer); } else printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); @@ -2005,7 +2009,7 @@ interrupt_again: /* Jump back here if int status doesn't reset */ if (devc->model != MD_1848 && alt_stat & 0x40) /* Timer interrupt */ { devc->timer_ticks++; -#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE) +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) if (timer_installed == dev && devc->timer_running) sound_timer_interrupt(); #endif @@ -2279,7 +2283,10 @@ int probe_ms_sound(struct address_info *hw_config) hw_config->card_subtype = 1; return 1; } - if (hw_config->irq > 11) + if ((hw_config->irq != 7) && + (hw_config->irq != 9) && + (hw_config->irq != 10) && + (hw_config->irq != 11)) { printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); return 0; @@ -2399,13 +2406,11 @@ void unload_ms_sound(struct address_info *hw_config) hw_config->irq, hw_config->dma, hw_config->dma, 0); - if(mixer>=0) - sound_unload_mixerdev(mixer); sound_unload_audiodev(hw_config->slots[0]); release_region(hw_config->io_base, 4); } -#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE) +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) /* * Timer stuff (for /dev/music). @@ -2576,5 +2581,6 @@ void cleanup_module(void) if(loaded) unload_ms_sound(&hw_config); } + #endif #endif diff --git a/drivers/sound/ad1848_mixer.h b/drivers/sound/ad1848_mixer.h index 0cf54e710..ab8eb2035 100644 --- a/drivers/sound/ad1848_mixer.h +++ b/drivers/sound/ad1848_mixer.h @@ -15,7 +15,7 @@ /* * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. - * Soundcard manufacturers have connected actual inputs (CD, synth, line, + * 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 to these inputs correctly. The following * contains two alternative mappings. The first one is for GUS MAX and @@ -71,7 +71,7 @@ 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 soundcards is not fixed. Some + * 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. */ diff --git a/drivers/sound/adlib_card.c b/drivers/sound/adlib_card.c index b9b16cd8e..b21d7316e 100644 --- a/drivers/sound/adlib_card.c +++ b/drivers/sound/adlib_card.c @@ -16,7 +16,7 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_YM3812) || defined(MODULE) +#ifdef CONFIG_YM3812 void attach_adlib_card(struct address_info *hw_config) { diff --git a/drivers/sound/audio.c b/drivers/sound/audio.c index 905bd83e7..f08f6aaca 100644 --- a/drivers/sound/audio.c +++ b/drivers/sound/audio.c @@ -25,7 +25,7 @@ #include "sound_config.h" -#if defined(CONFIG_AUDIO) || defined(MODULE) +#ifdef CONFIG_AUDIO #include "ulaw.h" #include "coproc.h" diff --git a/drivers/sound/audio_syms.c b/drivers/sound/audio_syms.c new file mode 100644 index 000000000..e06dea9be --- /dev/null +++ b/drivers/sound/audio_syms.c @@ -0,0 +1,21 @@ +/* + * Exported symbols for audio driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include <linux/module.h> + +char audio_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +EXPORT_SYMBOL(DMAbuf_start_dma); +EXPORT_SYMBOL(DMAbuf_open_dma); +EXPORT_SYMBOL(DMAbuf_close_dma); +EXPORT_SYMBOL(DMAbuf_inputintr); +EXPORT_SYMBOL(DMAbuf_outputintr); +EXPORT_SYMBOL(dma_ioctl); +EXPORT_SYMBOL(audio_open); +EXPORT_SYMBOL(audio_release); diff --git a/drivers/sound/awacs_defs.h b/drivers/sound/awacs_defs.h new file mode 100644 index 000000000..c84e24ff0 --- /dev/null +++ b/drivers/sound/awacs_defs.h @@ -0,0 +1,160 @@ +/*********************************************************/ +/* This file was written by someone, somewhere, sometime */ +/* And is released into the Public Domain */ +/*********************************************************/ + +#ifndef _AWACS_DEFS_H_ +#define _AWACS_DEFS_H_ + +/*******************************/ +/* AWACs Audio Register Layout */ +/*******************************/ + +struct awacs_regs { + unsigned control; /* Audio control register */ + unsigned pad0[3]; + unsigned codec_ctrl; /* Codec control register */ + unsigned pad1[3]; + unsigned codec_stat; /* Codec status register */ + unsigned pad2[3]; + unsigned clip_count; /* Clipping count register */ + unsigned pad3[3]; + unsigned byteswap; /* Data is little-endian if 1 */ +}; + +/*******************/ +/* Audio Bit Masks */ +/*******************/ + +/* Audio Control Reg Bit Masks */ +/* ----- ------- --- --- ----- */ +#define MASK_ISFSEL (0xf) /* Input SubFrame Select */ +#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */ +#define MASK_RATE (0x7 << 8) /* Sound Rate */ +#define MASK_CNTLERR (0x1 << 11) /* Error */ +#define MASK_PORTCHG (0x1 << 12) /* Port Change */ +#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */ +#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */ +#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */ + +/* Audio Codec Control Reg Bit Masks */ +/* ----- ----- ------- --- --- ----- */ +#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */ +#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */ +#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */ +#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */ + +/* Audio Codec Control Address Values / Masks */ +/* ----- ----- ------- ------- ------ - ----- */ +#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */ +#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */ +#define MASK_ADDR_GAIN MASK_ADDR0 + +#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */ +#define MASK_ADDR_MUTE MASK_ADDR1 +#define MASK_ADDR_RATE MASK_ADDR1 + +#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */ +#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */ +#define MASK_ADDR_VOLHD MASK_ADDR2 + +#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */ +#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */ +#define MASK_ADDR_VOLSPK MASK_ADDR4 + +/* Address 0 Bit Masks & Macros */ +/* ------- - --- ----- - ------ */ +#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */ +#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */ +#define MASK_GAINLINE (0x1 << 8) /* Change Gain for Line??? */ +#define MASK_GAINMIC (0x0 << 8) /* Change Gain for Mic??? */ + +#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */ +#define MASK_MUX_MIC (0x1 << 10) /* Select Mic in MUX */ +#define MASK_MUX_AUDIN (0x1 << 11) /* Select Audio In in MUX */ +#define MASK_MUX_LINE MASK_MUX_AUDIN + +#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT) +#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT) + +/* Address 1 Bit Masks */ +/* ------- - --- ----- */ +#define MASK_ADDR1RES1 (0x3) /* Reserved */ +#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */ +#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */ +#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */ +#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ +#define MASK_SPKMUTE MASK_CMUTE +#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ +#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ +#define MASK_HDMUTE MASK_AMUTE +#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ + +#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ +#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ +#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */ +#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */ +#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */ +#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */ +#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */ +#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */ + +/* Address 2 & 4 Bit Masks & Macros */ +/* ------- - - - --- ----- - ------ */ +#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */ +#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */ +#define MASK_ADDR4RES1 MASK_ADDR2RES1 +#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */ +#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */ +#define MASK_ADDR4RES2 MASK_ADDR2RES2 + +#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT)) +#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) + +/* Audio Codec Status Reg Bit Masks */ +/* ----- ----- ------ --- --- ----- */ +#define MASK_EXTEND (0x1 << 23) /* Extend */ +#define MASK_VALID (0x1 << 22) /* Valid Data? */ +#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */ +#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */ +#define MASK_ERRCODE (0xf << 16) /* Error Code */ +#define MASK_REVISION (0xf << 12) /* Revision Number */ +#define MASK_MFGID (0xf << 8) /* Mfg. ID */ +#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ +#define MASK_INPPORT (0xf) /* Input Port */ +#define MASK_HDPCONN 8 /* headphone plugged in */ + +/* Clipping Count Reg Bit Masks */ +/* -------- ----- --- --- ----- */ +#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */ +#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */ + +/* DBDMA ChannelStatus Bit Masks */ +/* ----- ------------- --- ----- */ +#define MASK_CSERR (0x1 << 7) /* Error */ +#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ +#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ +#define MASK_WAIT (0x1) /* Wait */ + +/* Various Rates */ +/* ------- ----- */ +#define RATE_48000 (0x0 << 8) /* 48 kHz */ +#define RATE_44100 (0x0 << 8) /* 44.1 kHz */ +#define RATE_32000 (0x1 << 8) /* 32 kHz */ +#define RATE_29400 (0x1 << 8) /* 29.4 kHz */ +#define RATE_24000 (0x2 << 8) /* 24 kHz */ +#define RATE_22050 (0x2 << 8) /* 22.05 kHz */ +#define RATE_19200 (0x3 << 8) /* 19.2 kHz */ +#define RATE_17640 (0x3 << 8) /* 17.64 kHz */ +#define RATE_16000 (0x4 << 8) /* 16 kHz */ +#define RATE_14700 (0x4 << 8) /* 14.7 kHz */ +#define RATE_12000 (0x5 << 8) /* 12 kHz */ +#define RATE_11025 (0x5 << 8) /* 11.025 kHz */ +#define RATE_9600 (0x6 << 8) /* 9.6 kHz */ +#define RATE_8820 (0x6 << 8) /* 8.82 kHz */ +#define RATE_8000 (0x7 << 8) /* 8 kHz */ +#define RATE_7350 (0x7 << 8) /* 7.35 kHz */ + +#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ + +#endif /* _AWACS_DEFS_H_ */ diff --git a/drivers/sound/coproc.h b/drivers/sound/coproc.h index f9023821d..7306346e9 100644 --- a/drivers/sound/coproc.h +++ b/drivers/sound/coproc.h @@ -1,5 +1,5 @@ /* - * Definitions for various on board processors on the soundcards. For + * Definitions for various on board processors on the sound cards. For * example DSP processors. */ diff --git a/drivers/sound/cs4232.c b/drivers/sound/cs4232.c index 571a3372b..43bc92911 100644 --- a/drivers/sound/cs4232.c +++ b/drivers/sound/cs4232.c @@ -8,6 +8,21 @@ * gets implemented. Just the WSS codec, FM synth and the MIDI ports are * supported. Other interfaces are left uninitialized. * + * ifdef ...WAVEFRONT... + * + * Support is provided for initializing the WaveFront synth + * interface as well, which is logical device #4. Note that if + * you have a Tropez+ card, you probably don't need to setup + * the CS4232-supported MIDI interface, since it corresponds to + * the internal 26-pin header that's hard to access. Using this + * requires an additional IRQ, a resource none too plentiful in + * this environment. Just don't set module parameters mpuio and + * mpuirq, and the MIDI port will be left uninitialized. You can + * still use the ICS2115 hosted MIDI interface which corresponds + * to the 9-pin D connector on the back of the card. + * + * endif ...WAVEFRONT... + * * Supported chips are: * CS4232 * CS4236 @@ -17,7 +32,9 @@ * anyway. * * Changes - * Alan Cox Modularisation, Basic cleanups. + * Alan Cox Modularisation, Basic cleanups. + * Paul Barton-Davis Separated MPU configuration, added + * Tropez+ (WaveFront) support */ /* @@ -34,8 +51,6 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_CS4232) || defined (MODULE) - #define KEY_PORT 0x279 /* Same as LPT1 status port */ #define CSN_NUM 0x99 /* Just a random number */ @@ -48,6 +63,9 @@ static void CS_OUT(unsigned char a) #define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);} static int mpu_base = 0, mpu_irq = 0; +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE +static int synth_base = 0, synth_irq = 0; +#endif CONFIG_SOUND_WAVEFRONT_MODULE static int mpu_detected = 0; int probe_cs4232_mpu(struct address_info *hw_config) @@ -108,7 +126,7 @@ int probe_cs4232(struct address_info *hw_config) * method conflicts with possible PnP support in the OS. For this reason * driver is just a temporary kludge. * - * Also the Cirrus/Crystal method doesnt always work. Try ISAPnP first ;) + * Also the Cirrus/Crystal method doesnt always work. Try ISA PnP first ;) */ /* @@ -172,6 +190,15 @@ int probe_cs4232(struct address_info *hw_config) } #endif +#if defined(CONFIG_SOUND_WAVEFRONT) || defined(CONFIG_SOUND_WAVEFRONT_MODULE) + { + CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */ + CS_OUT3 (0x47, (synth_base >> 8) & 0xff, + synth_base & 0xff); /* base */ + CS_OUT2 (0x22, synth_irq); /* IRQ */ + CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */ + } +#endif /* * Finally activate the chip */ @@ -196,7 +223,6 @@ void attach_cs4232(struct address_info *hw_config) { int base = hw_config->io_base, irq = hw_config->irq; int dma1 = hw_config->dma, dma2 = hw_config->dma2; - int old_num_mixers = num_mixers; if (dma2 == -1) dma2 = dma1; @@ -208,7 +234,8 @@ void attach_cs4232(struct address_info *hw_config) 0, hw_config->osp); - if (num_mixers > old_num_mixers) + if (hw_config->slots[0] != -1 && + audio_devs[hw_config->slots[0]]->mixer_dev!=-1) { /* Assume the mixer map is as suggested in the CS4232 databook */ AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); @@ -259,6 +286,7 @@ void unload_cs4232(struct address_info *hw_config) dma1, /* Playback DMA */ dma2, /* Capture DMA */ 0); + sound_unload_audiodev(hw_config->slots[0]); #if defined(CONFIG_UART401) && defined(CONFIG_MIDI) if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) @@ -295,15 +323,27 @@ int io = -1; int irq = -1; int dma = -1; int dma2 = -1; +int mpuio = -1; +int mpuirq = -1; MODULE_PARM(io,"i"); MODULE_PARM(irq,"i"); MODULE_PARM(dma,"i"); MODULE_PARM(dma2,"i"); +MODULE_PARM(mpuio,"i"); +MODULE_PARM(mpuirq,"i"); + +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE +int synthio = -1; +int synthirq = -1; +MODULE_PARM(synthio,"i"); +MODULE_PARM(synthirq,"i"); +#endif CONFIG_SOUND_WAVEFRONT_MODULE EXPORT_NO_SYMBOLS; struct address_info cfg; +struct address_info mpu_cfg; /* * Install a CS4232 based card. Need to have ad1848 and mpu401 @@ -312,33 +352,61 @@ struct address_info cfg; int init_module(void) { + +#ifndef CONFIG_SOUND_WAVEFRONT_MODULE + if (io == -1 || irq == -1 || dma == -1 || dma2 == -1) { printk(KERN_ERR "cs4232: dma, dma2, irq and io must be set.\n"); return -EINVAL; } +#else + if (synthio == -1 || synthirq == -1 || + io == -1 || irq == -1 || dma == -1 || dma2 == -1) + { + printk(KERN_ERR "cs4232: synthio, synthirq, dma, dma2, " + "irq and io must be set.\n"); + return -EINVAL; + } + +#endif CONFIG_SOUND_WAVEFRONT_MODULE + cfg.io_base = io; cfg.irq = irq; cfg.dma = dma; cfg.dma2 = dma2; +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE + synth_base = synthio; + synth_irq = synthirq; +#endif CONFIG_SOUND_WAVEFRONT_MODULE + if (probe_cs4232(&cfg) == 0) return -ENODEV; - probe_cs4232_mpu(&cfg); /* Bug always returns 0 not OK -- AC */ + mpu_cfg.io_base = -1; + mpu_cfg.irq = -1; + + if (mpuio != -1 && mpuirq != -1) { + mpu_cfg.io_base = mpuio; + mpu_cfg.irq = mpuirq; + probe_cs4232_mpu(&mpu_cfg); /* Bug always returns 0 not OK -- AC */ + } attach_cs4232(&cfg); - attach_cs4232_mpu(&cfg); + + if (mpuio != -1 && mpuirq != -1) { + attach_cs4232_mpu(&mpu_cfg); + } + SOUND_LOCK; return 0; } void cleanup_module(void) { - unload_cs4232_mpu(&cfg); - unload_cs4232(&cfg); + unload_cs4232(&cfg); /* unloads MPU as well, if needed */ SOUND_LOCK_END; } #endif -#endif diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c index 155cf529d..99e15cb22 100644 --- a/drivers/sound/dev_table.c +++ b/drivers/sound/dev_table.c @@ -515,39 +515,32 @@ int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, void sound_unload_audiodev(int dev) { if (dev != -1) + { audio_devs[dev] = NULL; + unregister_sound_dsp((dev<<4)+3); + } } int sound_alloc_audiodev(void) -{ - int i; - - for (i = 0; i < MAX_AUDIO_DEV; i++) - { - if (audio_devs[i] == NULL) - { - if (i >= num_audiodevs) - num_audiodevs = i + 1; - return i; - } - } - return -1; +{ + int i = register_sound_dsp(&oss_sound_fops); + if(i==-1) + return i; + i>>=4; + if(i>=num_audiodevs) + num_audiodevs = i + 1; + return i; } int sound_alloc_mididev(void) { - int i; - - for (i = 0; i < MAX_MIDI_DEV; i++) - { - if (midi_devs[i] == NULL) - { - if (i >= num_midis) - num_midis++; - return i; - } - } - return -1; + int i = register_sound_midi(&oss_sound_fops); + if(i==-1) + return i; + i>>=4; + if(i>=num_midis) + num_midis = i + 1; + return i; } int sound_alloc_synthdev(void) @@ -568,18 +561,13 @@ int sound_alloc_synthdev(void) int sound_alloc_mixerdev(void) { - int i; - - for (i = 0; i < MAX_MIXER_DEV; i++) - { - if (mixer_devs[i] == NULL) - { - if (i >= num_mixers) - num_mixers++; - return i; - } - } - return -1; + int i = register_sound_mixer(&oss_sound_fops); + if(i==-1) + return -1; + i>>=4; + if(i>=num_mixers) + num_mixers = i + 1; + return i; } int sound_alloc_timerdev(void) @@ -601,14 +589,20 @@ int sound_alloc_timerdev(void) void sound_unload_mixerdev(int dev) { if (dev != -1) + { mixer_devs[dev] = NULL; + unregister_sound_mixer(dev<<4); + } } void sound_unload_mididev(int dev) { #ifdef CONFIG_MIDI if (dev != -1) + { midi_devs[dev] = NULL; + unregister_sound_midi((dev<<4)+2); + } #endif } diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index b6e273d40..bf5d62bff 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -30,6 +30,7 @@ #define SNDCARD_OPL3SA1_MPU 40 #define SNDCARD_SOFTOSS 36 #define SNDCARD_VMIDI 37 +#define SNDCARD_WAVEFRONT 41 void attach_opl3sa_wss (struct address_info *hw_config); int probe_opl3sa_wss (struct address_info *hw_config); @@ -159,7 +160,7 @@ struct dma_buffparms /* * Structure for use with various microcontrollers and DSP processors - * in the recent soundcards. + * in the recent sound cards. */ typedef struct coproc_operations { @@ -229,7 +230,7 @@ struct audio_operations /* fields formerly in audio.c */ int audio_mode; - /* why dont we use file->f_flags & O_NONBLOCK for the following? - ts */ + /* why don't we use file->f_flags & O_NONBLOCK for the following? - ts */ int dev_nblock; /* 1 if in nonblocking mode */ #define AM_NONE 0 @@ -379,13 +380,13 @@ int num_sound_timers = 0; struct driver_info sound_drivers[] = { -#if defined(CONFIG_PSS) && !defined(CONFIG_PSS_MODULE) +#ifdef CONFIG_SOUND_PSS {"PSS", 0, SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss, unload_pss}, {"PSSMPU", 0, SNDCARD_PSS_MPU, "PSS-MPU", attach_pss_mpu, probe_pss_mpu, unload_pss_mpu}, {"PSSMSS", 0, SNDCARD_PSS_MSS, "PSS-MSS", attach_pss_mss, probe_pss_mss, unload_pss_mss}, #endif -#if defined(CONFIG_GUS) && !defined(CONFIG_GUS_MODULE) +#ifdef CONFIG_SOUND_GUS #ifdef CONFIG_GUS16 {"GUS16", 0, SNDCARD_GUS16, "Ultrasound 16-bit opt.", attach_gus_db16, probe_gus_db16, unload_gus_db16}, #endif @@ -395,43 +396,61 @@ struct driver_info sound_drivers[] = #endif #endif -#if defined(CONFIG_MSS) && !defined(CONFIG_MSS_MODULE) +#ifdef CONFIG_SOUND_MSS {"MSS", 0, SNDCARD_MSS, "MS Sound System", attach_ms_sound, probe_ms_sound, unload_ms_sound}, /* Compaq Deskpro XL */ {"DESKPROXL", 2, SNDCARD_DESKPROXL, "Compaq Deskpro XL", attach_ms_sound, probe_ms_sound, unload_ms_sound}, #endif -#ifdef CONFIG_MAD16 + +#ifdef CONFIG_SOUND_MAD16 {"MAD16", 0, SNDCARD_MAD16, "MAD16/Mozart (MSS)", attach_mad16, probe_mad16, unload_mad16}, {"MAD16MPU", 0, SNDCARD_MAD16_MPU, "MAD16/Mozart (MPU)", attach_mad16_mpu, probe_mad16_mpu, unload_mad16_mpu}, #endif -#ifdef CONFIG_CS4232 + +#ifdef CONFIG_SOUND_CS4232 {"CS4232", 0, SNDCARD_CS4232, "CS4232", attach_cs4232, probe_cs4232, unload_cs4232}, +#endif +#ifdef CONFIG_CS4232_MPU_BASE {"CS4232MPU", 0, SNDCARD_CS4232_MPU, "CS4232 MIDI", attach_cs4232_mpu, probe_cs4232_mpu, unload_cs4232_mpu}, #endif -#if defined(CONFIG_YM3812) && !defined(CONFIG_YM3812_MODULE) + +#ifdef CONFIG_SGALAXY + {"SGALAXY", 0, SNDCARD_SGALAXY, "Sound Galaxy WSS", attach_sgalaxy, probe_sgalaxy, unload_sgalaxy}, +#endif + +#ifdef CONFIG_SOUND_YM3812 {"OPL3", 0, SNDCARD_ADLIB, "OPL-2/OPL-3 FM", attach_adlib_card, probe_adlib, unload_adlib}, #endif -#if defined(CONFIG_PAS) && !defined(CONFIG_PAS_MODULE) + +#ifdef CONFIG_SOUND_PAS {"PAS16", 0, SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas, unload_pas}, #endif -#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) && !defined(CONFIG_MPU401_MODULE) + +#if (defined(CONFIG_SOUND_MPU401) || defined(CONFIG_SOUND_MPU_EMU)) && defined(CONFIG_MIDI) {"MPU401", 0, SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401, unload_mpu401}, #endif -#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) && !defined(CONFIG_UART401_MODULE) + +#if defined(CONFIG_SOUND_UART401) && defined(CONFIG_MIDI) {"UART401", 0, SNDCARD_UART401,"MPU-401 (UART)", attach_uart401, probe_uart401, unload_uart401}, #endif -#if defined(CONFIG_MAUI) && !defined(CONFIG_MAUI_MODULE) + +#if defined(CONFIG_SOUND_WAVEFRONT) + {"WAVEFRONT", 0, SNDCARD_WAVEFRONT,"TB WaveFront", attach_wavefront, probe_wavefront, unload_wavefront}, +#endif + +#if defined(CONFIG_SOUND_MAUI) {"MAUI", 0, SNDCARD_MAUI,"TB Maui", attach_maui, probe_maui, unload_maui}, #endif -#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) && !defined(CONFIG_UART6850_MODULE) + +#if defined(CONFIG_SOUND_UART6850) && defined(CONFIG_MIDI) {"MIDI6850", 0, SNDCARD_UART6850,"6860 UART Midi", attach_uart6850, probe_uart6850, unload_uart6850}, #endif -#if defined(CONFIG_SBDSP) && !defined(CONFIG_SBDSP_MODULE) +#ifdef CONFIG_SOUND_SBDSP {"SBLAST", 0, SNDCARD_SB, "Sound Blaster", attach_sb_card, probe_sb, unload_sb}, {"SBPNP", 6, SNDCARD_SBPNP, "Sound Blaster PnP", attach_sb_card, probe_sb, unload_sb}, @@ -440,29 +459,29 @@ struct driver_info sound_drivers[] = #endif #endif -#ifdef CONFIG_SSCAPE +#ifdef CONFIG_SOUND_SSCAPE {"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq SoundScape", attach_sscape, probe_sscape, unload_sscape}, {"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS, "MS Sound System (SoundScape)", attach_ss_ms_sound, probe_ss_ms_sound, unload_ss_ms_sound}, #endif -#ifdef CONFIG_OPL3SA1 +#ifdef CONFIG_SOUND_OPL3SA1 {"OPL3SA", 0, SNDCARD_OPL3SA1, "Yamaha OPL3-SA", attach_opl3sa_wss, probe_opl3sa_wss, unload_opl3sa_wss}, /* {"OPL3SASB", 0, SNDCARD_OPL3SA1_SB, "OPL3-SA (SB mode)", attach_opl3sa_sb, probe_opl3sa_sb, unload_opl3sa_sb}, */ {"OPL3SAMPU", 0, SNDCARD_OPL3SA1_MPU, "OPL3-SA MIDI", attach_opl3sa_mpu, probe_opl3sa_mpu, unload_opl3sa_mpu}, #endif -#if defined (CONFIG_TRIX) && !defined(CONFIG_TRIX_MODULE) +#ifdef CONFIG_SOUND_TRIX {"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTrix AudioTrix Pro", attach_trix_wss, probe_trix_wss, unload_trix_wss}, {"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTrix (SB mode)", attach_trix_sb, probe_trix_sb, unload_trix_sb}, {"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTrix MIDI", attach_trix_mpu, probe_trix_mpu, unload_trix_mpu}, #endif -#if defined(CONFIG_SOFTOSS) && !defined(CONFIG_SOFTOSS_MODULE) +#ifdef CONFIG_SOUND_SOFTOSS {"SOFTSYN", 0, SNDCARD_SOFTOSS, "SoftOSS Virtual Wave Table", attach_softsyn_card, probe_softsyn, unload_softsyn}, #endif -#if defined(CONFIG_VMIDI) && defined(CONFIG_MIDI) && !defined(CONFIG_VMIDI_MODULE) +#if defined(CONFIG_SOUND_VMIDI) && defined(CONFIG_MIDI) {"VMIDI", 0, SNDCARD_VMIDI,"Loopback MIDI Device", attach_v_midi, probe_v_midi, unload_v_midi}, #endif #ifdef CONFIG_VIDC_SOUND @@ -484,7 +503,7 @@ int num_sound_drivers = sizeof(sound_drivers) / sizeof (struct driver_info); struct card_info snd_installed_cards[] = { -#ifdef CONFIG_PSS +#ifdef CONFIG_SOUND_PSS {SNDCARD_PSS, {CONFIG_PSS_BASE, 0, -1, -1}, SND_DEFAULT_ENABLE}, #ifdef CONFIG_PSS_MPU_BASE {SNDCARD_PSS_MPU, {CONFIG_PSS_MPU_BASE, CONFIG_PSS_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, @@ -493,7 +512,8 @@ struct card_info snd_installed_cards[] = {SNDCARD_PSS_MSS, {CONFIG_PSS_MSS_BASE, CONFIG_PSS_MSS_IRQ, CONFIG_PSS_MSS_DMA, -1}, SND_DEFAULT_ENABLE}, #endif #endif -#ifdef CONFIG_TRIX + +#ifdef CONFIG_SOUND_TRIX #ifndef CONFIG_TRIX_DMA2 #define CONFIG_TRIX_DMA2 CONFIG_TRIX_DMA #endif @@ -506,22 +526,23 @@ struct card_info snd_installed_cards[] = #endif #endif -#ifdef CONFIG_OPL3SA1 +#ifdef CONFIG_SOUND_OPL3SA1 {SNDCARD_OPL3SA1, {CONFIG_OPL3SA1_BASE, CONFIG_OPL3SA1_IRQ, CONFIG_OPL3SA1_DMA, CONFIG_OPL3SA1_DMA2}, SND_DEFAULT_ENABLE}, #ifdef CONFIG_OPL3SA1_MPU_BASE {SNDCARD_OPL3SA1_MPU, {CONFIG_OPL3SA1_MPU_BASE, CONFIG_OPL3SA1_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif #endif -#ifdef CONFIG_SOFTOSS +#ifdef CONFIG_SOUND_SOFTOSS {SNDCARD_SOFTOSS, {0, 0, -1, -1}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_SSCAPE +#ifdef CONFIG_SOUND_SSCAPE {SNDCARD_SSCAPE, {CONFIG_SSCAPE_BASE, CONFIG_SSCAPE_IRQ, CONFIG_SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, {SNDCARD_SSCAPE_MSS, {CONFIG_SSCAPE_MSS_BASE, CONFIG_SSCAPE_MSS_IRQ, CONFIG_SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_MAD16 + +#ifdef CONFIG_SOUND_MAD16 #ifndef CONFIG_MAD16_DMA2 #define CONFIG_MAD16_DMA2 CONFIG_MAD16_DMA #endif @@ -531,7 +552,7 @@ struct card_info snd_installed_cards[] = #endif #endif -#ifdef CONFIG_CS4232 +#ifdef CONFIG_SOUND_CS4232 #ifndef CONFIG_CS4232_DMA2 #define CONFIG_CS4232_DMA2 CONFIG_CS4232_DMA #endif @@ -541,8 +562,15 @@ struct card_info snd_installed_cards[] = {SNDCARD_CS4232, {CONFIG_CS4232_BASE, CONFIG_CS4232_IRQ, CONFIG_CS4232_DMA, CONFIG_CS4232_DMA2}, SND_DEFAULT_ENABLE}, #endif +#ifdef CONFIG_SGALAXY +#ifndef CONFIG_SGALAXY_DMA2 +#define CONFIG_SGALAXY_DMA2 CONFIG_SGALAXY_DMA +#endif + {SNDCARD_SGALAXY, {CONFIG_SGALAXY_BASE, CONFIG_SGALAXY_IRQ, CONFIG_SGALAXY_DMA, CONFIG_SGALAXY_DMA2, 0, NULL, CONFIG_SGALAXY_SGBASE}, SND_DEFAULT_ENABLE}, +#endif + -#ifdef CONFIG_MSS +#ifdef CONFIG_SOUND_MSS #ifndef CONFIG_MSS_DMA2 #define CONFIG_MSS_DMA2 -1 #endif @@ -552,17 +580,17 @@ struct card_info snd_installed_cards[] = #else {SNDCARD_MSS, {CONFIG_MSS_BASE, CONFIG_MSS_IRQ, CONFIG_MSS_DMA, CONFIG_MSS_DMA2}, SND_DEFAULT_ENABLE}, #endif + #ifdef MSS2_BASE {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA, MSS2_DMA2}, SND_DEFAULT_ENABLE}, #endif #endif - -#ifdef CONFIG_PAS +#ifdef CONFIG_SOUND_PAS {SNDCARD_PAS, {CONFIG_PAS_BASE, CONFIG_PAS_IRQ, CONFIG_PAS_DMA, -1}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_SB +#ifdef CONFIG_SOUND_SB #ifndef CONFIG_SB_DMA #define CONFIG_SB_DMA 1 #endif @@ -574,11 +602,16 @@ struct card_info snd_installed_cards[] = {SNDCARD_SB, {SB2_BASE, SB2_IRQ, SB2_DMA, SB2_DMA2}, SND_DEFAULT_ENABLE}, #endif #endif -#if defined(CONFIG_MAUI) + +#if defined(CONFIG_WAVEFRONT) + {SNDCARD_WAVEFRONT, {WAVEFRONT_BASE, WAVEFRONT_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#endif + +#ifdef CONFIG_SOUND_MAUI {SNDCARD_MAUI, {CONFIG_MAUI_BASE, CONFIG_MAUI_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#if defined(CONFIG_MPU401) && defined(CONFIG_MIDI) +#if defined(CONFIG_SOUND_MPU401) && defined(CONFIG_MIDI) {SNDCARD_MPU401, {CONFIG_MPU_BASE, CONFIG_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #ifdef MPU2_BASE {SNDCARD_MPU401, {MPU2_BASE, MPU2_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, @@ -588,17 +621,17 @@ struct card_info snd_installed_cards[] = #endif #endif -#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) +#if defined(CONFIG_SOUND_UART6850) && defined(CONFIG_MIDI) {SNDCARD_UART6850, {CONFIG_U6850_BASE, CONFIG_U6850_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#if defined(CONFIG_SB) +#ifdef CONFIG_SOUND_SB #if defined(CONFIG_MIDI) && defined(CONFIG_SB_MPU_BASE) {SNDCARD_SB16MIDI,{CONFIG_SB_MPU_BASE, CONFIG_SB_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif #endif -#ifdef CONFIG_GUS +#ifdef CONFIG_SOUND_GUS #ifndef CONFIG_GUS_DMA2 #define CONFIG_GUS_DMA2 CONFIG_GUS_DMA #endif @@ -608,11 +641,11 @@ struct card_info snd_installed_cards[] = {SNDCARD_GUS, {CONFIG_GUS_BASE, CONFIG_GUS_IRQ, CONFIG_GUS_DMA, CONFIG_GUS_DMA2}, SND_DEFAULT_ENABLE}, #endif -#if defined(CONFIG_YM3812) +#ifdef CONFIG_SOUND_YM3812 {SNDCARD_ADLIB, {FM_MONO, 0, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#if defined(CONFIG_VMIDI) && defined(CONFIG_MIDI) +#if defined(CONFIG_SOUND_VMIDI) && defined(CONFIG_MIDI) {SNDCARD_VMIDI, {0, 0, 0, -1}, SND_DEFAULT_ENABLE}, #endif diff --git a/drivers/sound/dm.h b/drivers/sound/dm.h new file mode 100644 index 000000000..14a90593c --- /dev/null +++ b/drivers/sound/dm.h @@ -0,0 +1,79 @@ +#ifndef _DRIVERS_SOUND_DM_H +#define _DRIVERS_SOUND_DM_H + +/* + * Definitions of the 'direct midi sound' interface used + * by the newer commercial OSS package. We should export + * this to userland somewhere in glibc later. + */ + +/* + * Data structure composing an FM "note" or sound event. + */ + +struct dm_fm_voice +{ + u8 op; + u8 voice; + u8 am; + u8 vibrato; + u8 do_sustain; + u8 kbd_scale; + u8 harmonic; + u8 scale_level; + u8 volume; + u8 attack; + u8 decay; + u8 sustain; + u8 release; + u8 feedback; + u8 connection; + u8 left; + u8 right; + u8 waveform; +}; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +struct dm_fm_note +{ + u8 voice; + u8 octave; + u32 fnum; + u8 key_on; +}; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +struct dm_fm_params +{ + u8 am_depth; + u8 vib_depth; + u8 kbd_split; + u8 rhythm; + + /* This block is the percussion instrument data */ + u8 bass; + u8 snare; + u8 tomtom; + u8 cymbal; + u8 hihat; +}; + +/* + * FM mode ioctl settings + */ + +#define FM_IOCTL_RESET 0x20 +#define FM_IOCTL_PLAY_NOTE 0x21 +#define FM_IOCTL_SET_VOICE 0x22 +#define FM_IOCTL_SET_PARAMS 0x23 +#define FM_IOCTL_SET_MODE 0x24 +#define FM_IOCTL_SET_OPL 0x25 + +#endif diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c index 5066cfe5a..ecabf72ab 100644 --- a/drivers/sound/dmabuf.c +++ b/drivers/sound/dmabuf.c @@ -765,6 +765,7 @@ static int find_output_space(int dev, char **buf, int *size) offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP; if (offs < 0 || offs >= dmap->bytes_in_use) { + restore_flags(flags); 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; @@ -1131,6 +1132,12 @@ void DMAbuf_init(int dev, int dma1, int dma2) * NOTE! This routine could be called several times. */ + /* drag in audio_syms.o */ + { + extern char audio_syms_symbol; + audio_syms_symbol = 0; + } + if (adev && adev->dmap_out == NULL) { if (adev->d == NULL) panic("OSS: audio_devs[%d]->d == NULL\n", dev); diff --git a/drivers/sound/dmasound.c b/drivers/sound/dmasound.c index 7805bed43..f944252bf 100644 --- a/drivers/sound/dmasound.c +++ b/drivers/sound/dmasound.c @@ -4,6 +4,7 @@ /* 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 @@ -72,6 +73,8 @@ History: 1996/9/25 ++geert: modularization +1998-06-10 ++andreas: converted to use sound_core + */ @@ -84,8 +87,12 @@ History: #include <linux/errno.h> #include <linux/mm.h> #include <linux/malloc.h> +#include <linux/sound.h> +#include <linux/init.h> +#ifdef __mc68000__ #include <asm/setup.h> +#endif #include <asm/system.h> #include <asm/irq.h> #include <asm/pgtable.h> @@ -100,13 +107,28 @@ History: #include <asm/amigahw.h> #include <asm/amigaints.h> #endif /* CONFIG_AMIGA */ +#ifdef CONFIG_PMAC +#include <asm/prom.h> +#include <asm/io.h> +#include <asm/dbdma.h> +#ifdef CONFIG_PMAC_PBOOK +#include <asm/adb.h> +#include <asm/pmu.h> +#endif /* CONFIG_PMAC_PBOOK */ +#include "awacs_defs.h" +#include <linux/nvram.h> +#include <linux/vt_kern.h> +#endif /* CONFIG_PMAC */ #include "dmasound.h" #include <linux/soundcard.h> +#define HAS_8BIT_TABLES #ifdef MODULE -static int chrdev_registered = 0; +static int sq_unit = -1; +static int mixer_unit = -1; +static int state_unit = -1; static int irq_installed = 0; #endif /* MODULE */ static char **sound_buffers = NULL; @@ -143,6 +165,91 @@ extern u_short amiga_audio_period; #endif /* CONFIG_AMIGA */ +#ifdef CONFIG_PMAC +/* + * Interrupt numbers and addresses, obtained from the device tree. + */ +static int awacs_irq, awacs_tx_irq, awacs_rx_irq; +static volatile struct awacs_regs *awacs; +static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma; +static int awacs_rate_index; +static int awacs_subframe; + +/* + * Space for the DBDMA command blocks. + */ +static void *awacs_tx_cmd_space; +static volatile struct dbdma_cmd *awacs_tx_cmds; + +/* + * Cached values of AWACS registers (we can't read them). + */ +int awacs_reg[5]; + +#define HAS_16BIT_TABLES +#undef HAS_8BIT_TABLES + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SPEED 2 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static int beep_volume = BEEP_VOLUME; +static int beep_playing = 0; +static short *beep_buf; +static volatile struct dbdma_cmd *beep_dbdma_cmd; +static void (*orig_mksound)(unsigned int, unsigned int); + +#ifdef CONFIG_PMAC_PBOOK +/* + * Stuff for restoring after a sleep. + */ +static int awacs_sleep_notify(struct notifier_block *, unsigned long, void *); +struct notifier_block awacs_sleep_notifier = { + awacs_sleep_notify +}; +#endif /* CONFIG_PMAC_PBOOK */ + +#endif /* CONFIG_PMAC */ /*** Some declarations *******************************************************/ @@ -150,6 +257,7 @@ extern u_short amiga_audio_period; #define DMASND_TT 1 #define DMASND_FALCON 2 #define DMASND_AMIGA 3 +#define DMASND_AWACS 4 #define MAX_CATCH_RADIUS 10 #define MIN_BUFFERS 4 @@ -157,7 +265,9 @@ extern u_short amiga_audio_period; #define MAX_BUFSIZE 128 /* Limit for Amiga */ static int catchRadius = 0, numBufs = 4, bufSize = 32; - +MODULE_PARM(catchRadius, "i"); +MODULE_PARM(numBufs, "i"); +MODULE_PARM(bufSize, "i"); #define arraysize(x) (sizeof(x)/sizeof(*(x))) #define min(x, y) ((x) < (y) ? (x) : (y)) @@ -165,164 +275,164 @@ static int catchRadius = 0, numBufs = 4, bufSize = 32; #define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) #define IOCTL_IN(arg, ret) \ - do { int error = get_user(ret, (int *)(arg)); \ - if (error) return error; \ - } while (0) + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) #define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) /*** Some low level helpers **************************************************/ - +#ifdef HAS_8BIT_TABLES /* 8 bit mu-law */ static char 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 + -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 */ static char 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 + -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 */ #ifdef HAS_16BIT_TABLES /* 16 bit mu-law */ -static char ulaw2dma16[] = { - -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, - -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, - -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, - -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0, +static short ulaw2dma16[] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0, }; /* 16 bit A-law */ -static char alaw2dma16[] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, - -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, - -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, - -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848, +static short alaw2dma16[] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, + -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, + -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848, }; #endif /* HAS_16BIT_TABLES */ @@ -332,75 +442,75 @@ static char alaw2dma16[] = { /* 14 bit mu-law (LSB) */ static char alaw2dma14l[] = { - 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 49, 17, 49, 17, 49, 17, 49, 17, - 49, 17, 49, 17, 49, 17, 49, 17, - 41, 57, 9, 25, 41, 57, 9, 25, - 41, 57, 9, 25, 41, 57, 9, 25, - 37, 45, 53, 61, 5, 13, 21, 29, - 37, 45, 53, 61, 5, 13, 21, 29, - 35, 39, 43, 47, 51, 55, 59, 63, - 3, 7, 11, 15, 19, 23, 27, 31, - 34, 36, 38, 40, 42, 44, 46, 48, - 50, 52, 54, 56, 58, 60, 62, 0, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, - 15, 47, 15, 47, 15, 47, 15, 47, - 15, 47, 15, 47, 15, 47, 15, 47, - 23, 7, 55, 39, 23, 7, 55, 39, - 23, 7, 55, 39, 23, 7, 55, 39, - 27, 19, 11, 3, 59, 51, 43, 35, - 27, 19, 11, 3, 59, 51, 43, 35, - 29, 25, 21, 17, 13, 9, 5, 1, - 61, 57, 53, 49, 45, 41, 37, 33, - 30, 28, 26, 24, 22, 20, 18, 16, - 14, 12, 10, 8, 6, 4, 2, 0 + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 49, 17, 49, 17, 49, 17, 49, 17, + 49, 17, 49, 17, 49, 17, 49, 17, + 41, 57, 9, 25, 41, 57, 9, 25, + 41, 57, 9, 25, 41, 57, 9, 25, + 37, 45, 53, 61, 5, 13, 21, 29, + 37, 45, 53, 61, 5, 13, 21, 29, + 35, 39, 43, 47, 51, 55, 59, 63, + 3, 7, 11, 15, 19, 23, 27, 31, + 34, 36, 38, 40, 42, 44, 46, 48, + 50, 52, 54, 56, 58, 60, 62, 0, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, + 15, 47, 15, 47, 15, 47, 15, 47, + 15, 47, 15, 47, 15, 47, 15, 47, + 23, 7, 55, 39, 23, 7, 55, 39, + 23, 7, 55, 39, 23, 7, 55, 39, + 27, 19, 11, 3, 59, 51, 43, 35, + 27, 19, 11, 3, 59, 51, 43, 35, + 29, 25, 21, 17, 13, 9, 5, 1, + 61, 57, 53, 49, 45, 41, 37, 33, + 30, 28, 26, 24, 22, 20, 18, 16, + 14, 12, 10, 8, 6, 4, 2, 0 }; /* 14 bit A-law (LSB) */ static char alaw2dma14l[] = { - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, - 16, 48, 16, 48, 16, 48, 16, 48, - 16, 48, 16, 48, 16, 48, 16, 48, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 42, 46, 34, 38, 58, 62, 50, 54, - 10, 14, 2, 6, 26, 30, 18, 22, - 42, 46, 34, 38, 58, 62, 50, 54, - 10, 14, 2, 6, 26, 30, 18, 22, - 40, 56, 8, 24, 40, 56, 8, 24, - 40, 56, 8, 24, 40, 56, 8, 24, - 20, 28, 4, 12, 52, 60, 36, 44, - 20, 28, 4, 12, 52, 60, 36, 44, - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, - 48, 16, 48, 16, 48, 16, 48, 16, - 48, 16, 48, 16, 48, 16, 48, 16, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 22, 18, 30, 26, 6, 2, 14, 10, - 54, 50, 62, 58, 38, 34, 46, 42, - 22, 18, 30, 26, 6, 2, 14, 10, - 54, 50, 62, 58, 38, 34, 46, 42, - 24, 8, 56, 40, 24, 8, 56, 40, - 24, 8, 56, 40, 24, 8, 56, 40, - 44, 36, 60, 52, 12, 4, 28, 20, - 44, 36, 60, 52, 12, 4, 28, 20 + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 16, 48, 16, 48, 16, 48, 16, 48, + 16, 48, 16, 48, 16, 48, 16, 48, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 42, 46, 34, 38, 58, 62, 50, 54, + 10, 14, 2, 6, 26, 30, 18, 22, + 42, 46, 34, 38, 58, 62, 50, 54, + 10, 14, 2, 6, 26, 30, 18, 22, + 40, 56, 8, 24, 40, 56, 8, 24, + 40, 56, 8, 24, 40, 56, 8, 24, + 20, 28, 4, 12, 52, 60, 36, 44, + 20, 28, 4, 12, 52, 60, 36, 44, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 48, 16, 48, 16, 48, 16, 48, 16, + 48, 16, 48, 16, 48, 16, 48, 16, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 22, 18, 30, 26, 6, 2, 14, 10, + 54, 50, 62, 58, 38, 34, 46, 42, + 22, 18, 30, 26, 6, 2, 14, 10, + 54, 50, 62, 58, 38, 34, 46, 42, + 24, 8, 56, 40, 24, 8, 56, 40, + 24, 8, 56, 40, 24, 8, 56, 40, + 44, 36, 60, 52, 12, 4, 28, 20, + 44, 36, 60, 52, 12, 4, 28, 20 }; #endif /* HAS_14BIT_TABLES */ @@ -409,73 +519,126 @@ static char alaw2dma14l[] = { #ifdef CONFIG_ATARI -static long ata_ct_law(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ct_s8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ct_u8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ctx_law(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA -static long ami_ct_law(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ami_ct_s8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ami_ct_u8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); -static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft); +static ssize_t ami_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ami_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ami_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ami_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ami_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ami_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); #endif /* CONFIG_AMIGA */ +#ifdef CONFIG_PMAC +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +#endif /* CONFIG_PMAC */ /*** Machine definitions *****************************************************/ typedef struct { - int type; - void *(*dma_alloc)(unsigned int, int); - void (*dma_free)(void *, unsigned int); - int (*irqinit)(void); + int type; + void *(*dma_alloc)(unsigned int, int); + void (*dma_free)(void *, unsigned int); + int (*irqinit)(void); #ifdef MODULE - void (*irqcleanup)(void); + void (*irqcleanup)(void); #endif /* MODULE */ - 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 (*init)(void); + void (*silence)(void); + int (*setFormat)(int); + int (*setVolume)(int); + int (*setBass)(int); + int (*setTreble)(int); + int (*setGain)(int); + void (*play)(void); } MACHINE; @@ -483,38 +646,38 @@ typedef struct { typedef struct { - int format; /* AFMT_* */ - int stereo; /* 0 = mono, 1 = stereo */ - int size; /* 8/16 bit*/ - int speed; /* speed */ + int format; /* AFMT_* */ + int stereo; /* 0 = mono, 1 = stereo */ + int size; /* 8/16 bit*/ + int speed; /* speed */ } SETTINGS; typedef struct { - long (*ct_ulaw)(const u_char *, unsigned long, u_char *, long *, long); - long (*ct_alaw)(const u_char *, unsigned long, u_char *, long *, long); - long (*ct_s8)(const u_char *, unsigned long, u_char *, long *, long); - long (*ct_u8)(const u_char *, unsigned long, u_char *, long *, long); - long (*ct_s16be)(const u_char *, unsigned long, u_char *, long *, long); - long (*ct_u16be)(const u_char *, unsigned long, u_char *, long *, long); - long (*ct_s16le)(const u_char *, unsigned long, u_char *, long *, long); - long (*ct_u16le)(const u_char *, unsigned long, u_char *, long *, long); + ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16le)(const u_char *, 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; /* 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 */ -#ifdef CONFIG_ATARI - int bal; /* balance factor for expanding (not volume!) */ - u_long data; /* data for expanding */ + MACHINE mach; /* machine dependent things */ + SETTINGS hard; /* hardware settings */ + SETTINGS soft; /* software settings */ + SETTINGS dsp; /* /dev/dsp default settings */ + TRANS *trans; /* 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 */ +#if defined(CONFIG_ATARI) || defined(CONFIG_PMAC) + int bal; /* balance factor for expanding (not volume!) */ + u_long data; /* data for expanding */ #endif /* CONFIG_ATARI */ }; @@ -522,9 +685,9 @@ static struct sound_settings sound; #ifdef CONFIG_ATARI -static void *AtaAlloc(unsigned int size, int flags); -static void AtaFree(void *, unsigned int size); -static int AtaIrqInit(void); +static void *AtaAlloc(unsigned int size, int flags) __init; +static void AtaFree(void *, unsigned int size) __init; +static int AtaIrqInit(void) __init; #ifdef MODULE static void AtaIrqCleanUp(void); #endif /* MODULE */ @@ -545,9 +708,9 @@ static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp); #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA -static void *AmiAlloc(unsigned int size, int flags); -static void AmiFree(void *, unsigned int); -static int AmiIrqInit(void); +static void *AmiAlloc(unsigned int size, int flags) __init; +static void AmiFree(void *, unsigned int) __init; +static int AmiIrqInit(void) __init; #ifdef MODULE static void AmiIrqCleanUp(void); #endif /* MODULE */ @@ -561,6 +724,26 @@ static void AmiPlay(void); static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp); #endif /* CONFIG_AMIGA */ +#ifdef CONFIG_PMAC +static void *PMacAlloc(unsigned int size, int flags) __init; +static void PMacFree(void *ptr, unsigned int size) __init; +static int PMacIrqInit(void) __init; +#ifdef MODULE +static void PMacIrqCleanup(void); +#endif /* MODULE */ +static void PMacSilence(void); +static void PMacInit(void); +static void PMacPlay(void); +static int PMacSetFormat(int format); +static int PMacSetVolume(int volume); +static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs); +static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs); +static void awacs_write(int val); +static int awacs_get_volume(int reg, int lshift); +static int awacs_volume_setter(int volume, int n, int mute, int lshift); +static void awacs_mksound(unsigned int hz, unsigned int ticks); +static void awacs_nosound(unsigned long xx); +#endif /* CONFIG_PMAC */ /*** Mid level stuff *********************************************************/ @@ -574,11 +757,13 @@ static int sound_set_volume(int volume); #ifdef CONFIG_ATARI static int sound_set_bass(int bass); #endif /* CONFIG_ATARI */ +#if defined(CONFIG_ATARI) || defined(CONFIG_AMIGA) static int sound_set_treble(int treble); -static long sound_copy_translate(const u_char *userPtr, - unsigned long userCount, - u_char frame[], long *frameUsed, - long frameLeft); +#endif /* CONFIG_ATARI || CONFIG_AMIGA */ +static ssize_t sound_copy_translate(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); /* @@ -591,41 +776,35 @@ struct sound_mixer { static struct sound_mixer mixer; -static void mixer_init(void); -static int mixer_open(int open_mode); -static int mixer_release(void); -static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg); - - /* * Sound queue stuff, the heart of the driver */ struct sound_queue { - int max_count, block_size; - char **buffers; - - /* 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: 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 playing; - struct wait_queue *write_queue, *open_queue, *sync_queue; - int open_mode; - int busy, syncing; + int max_count, block_size; + char **buffers; + int max_active; + + /* 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 playing; + struct wait_queue *write_queue, *open_queue, *sync_queue; + int open_mode; + int busy, syncing; #ifdef CONFIG_ATARI - int ignore_int; /* ++TeSche: used for Falcon */ + int ignore_int; /* ++TeSche: used for Falcon */ #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA - int block_size_half, block_size_quarter; + int block_size_half, block_size_quarter; #endif /* CONFIG_AMIGA */ }; @@ -641,62 +820,35 @@ static struct sound_queue sq; interruptible_sleep_on(&queue); #define WAKE_UP(queue) (wake_up_interruptible(&queue)) -static void sq_init(int numBufs, int bufSize, char **buffers); -static void sq_play(void); -static long sq_write(const char *src, unsigned long uLeft); -static int sq_open(int open_mode); -static void sq_reset(void); -static int sq_sync(void); -static int sq_release(void); - - /* * /dev/sndstat */ struct sound_state { - int busy; - char buf[512]; - int len, ptr; + int busy; + char buf[512]; + int len, ptr; }; static struct sound_state state; -static void state_init(void); -static int state_open(int open_mode); -static int state_release(void); -static long state_read(char *dest, unsigned long count); - +/*** Common stuff ********************************************************/ -/*** High level stuff ********************************************************/ - - -static int sound_open(struct inode *inode, struct file *file); -static int sound_fsync(struct file *filp, struct dentry *dentry); -static int sound_release(struct inode *inode, struct file *file); static long long sound_lseek(struct file *file, long long offset, int orig); -static ssize_t sound_read(struct file *file, char *buf, size_t count, - loff_t *ppos); -static ssize_t sound_write(struct file *file, const char *buf, size_t count, - loff_t *ppos); static inline int ioctl_return(int *addr, int value) { - if (value < 0) - return(value); + if (value < 0) + return(value); - return put_user(value, addr); + return put_user(value, addr)? -EFAULT: 0; } -static int unknown_minor_dev(char *fname, int dev); -static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg); /*** Config & Setup **********************************************************/ -void soundcard_init(void); +void dmasound_init(void); void dmasound_setup(char *str, int *ints); -void sound_setup(char *str, int *ints); /* ++Martin: stub for now */ /*** Translations ************************************************************/ @@ -727,850 +879,1330 @@ void sound_setup(char *str, int *ints); /* ++Martin: stub for now */ */ #ifdef CONFIG_ATARI -static long ata_ct_law(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; - long count, used; - u_char *p = &frame[*frameUsed]; + char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; + ssize_t count, used; + u_char *p = &frame[*frameUsed]; - count = min(userCount, frameLeft); - if (sound.soft.stereo) - count &= ~1; - used = count; - while (count > 0) { - u_char data; - get_user(data, userPtr++); - *p++ = table[data]; - count--; - } - *frameUsed += used; - return(used); + count = min(userCount, frameLeft); + if (sound.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 long ata_ct_s8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; - void *p = &frame[*frameUsed]; + ssize_t count, used; + void *p = &frame[*frameUsed]; - count = min(userCount, frameLeft); - if (sound.soft.stereo) - count &= ~1; - used = count; - copy_from_user(p, userPtr, count); - *frameUsed += used; - return(used); + count = min(userCount, frameLeft); + if (sound.soft.stereo) + count &= ~1; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; + return(used); } -static long ata_ct_u8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; + ssize_t count, used; + + if (!sound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + count = min(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(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x8080; + count--; + } + } + *frameUsed += used; + return(used); +} - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - count = min(userCount, frameLeft); - used = count; - while (count > 0) { - u_char data; - get_user(data, userPtr++); - *p++ = data ^ 0x80; - count--; - } - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - get_user(data, ((u_short *)userPtr)++); - *p++ = data ^ 0x8080; - count--; + +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + void *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft) & ~3; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; } - } - *frameUsed += used; - return(used); + return(used); } -static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; - u_long data; + ssize_t count, used; + + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x80008000; + count--; + } + *frameUsed += used; + } + return(used); +} - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - void *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft) & ~3; - used = count; - copy_from_user(p, userPtr, count); - *frameUsed += used; - } - return(used); + +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + count = frameLeft; + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + *p++ = data; + count--; + } + *frameUsed += used; + } + return(used); } -static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; - u_long data; + ssize_t count, used; + + count = frameLeft; + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + *p++ = data; + *p++ = data; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>2; + used = count; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + *p++ = data; + count--; + } + *frameUsed += used; + } + return(used); +} - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data ^= 0x8000; - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>2; - used = count*4; - while (count > 0) { - get_user(data, ((u_int *)userPtr)++); - *p++ = data ^ 0x80008000; - count--; + +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; + /* this should help gcc to stuff everything into registers */ + long bal = sound.bal; + long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!sound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = sound.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; + } + sound.data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = sound.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; + } + sound.data = data; } - *frameUsed += used; - } - return(used); + sound.bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return(used); } -static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; - u_long data; + /* this should help gcc to stuff everything into registers */ + long bal = sound.bal; + long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!sound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = sound.data; + while (frameLeft) { + if (bal < 0) { + if (!userCount) + break; + if (get_user(data, userPtr++)) + return -EFAULT; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + sound.data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = sound.data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + sound.data = data; + } + sound.bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return(used); +} - count = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data); - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>2; - used = count*4; - while (count > 0) { - get_user(data, ((u_int *)userPtr)++); - data = le2be16dbl(data); - *p++ = data; - count--; + +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = sound.bal; + long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!sound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = sound.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; + } + sound.data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = sound.data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8080; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + sound.data = data; } - *frameUsed += used; - } - return(used); + sound.bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return(used); } -static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; - u_long data; + /* this should help gcc to stuff everything into registers */ + long bal = sound.bal; + long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; + } + sound.bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return(used); +} - count = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data) ^ 0x8000; - *p++ = data; - *p++ = data; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>2; - used = count; - while (count > 0) { - get_user(data, ((u_int *)userPtr)++); - data = le2be16dbl(data) ^ 0x80008000; - *p++ = data; - count--; + +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = sound.bal; + long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data ^= 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; } - *frameUsed += used; - } - return(used); + sound.bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return(used); } -static long ata_ctx_law(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; - /* this should help gcc to stuff everything into registers */ - u_long data = sound.data; - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - long used, usedf; + /* this should help gcc to stuff everything into registers */ + long bal = sound.bal; + long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; + } + sound.bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return(used); +} - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (!userCount) - break; - get_user(c, userPtr++); - data = table[c]; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - while (frameLeft >= 2) { - u_char c; - if (bal < 0) { - if (userCount < 2) - break; - get_user(c, userPtr++); - data = table[c] << 8; - get_user(c, userPtr++); - data |= table[c]; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - } - sound.bal = bal; - sound.data = data; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - u_long data = sound.data; - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - long used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - while (frameLeft) { - if (bal < 0) { - if (!userCount) - break; - get_user(data, userPtr++); - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - while (frameLeft >= 2) { - if (bal < 0) { - if (userCount < 2) - break; - get_user(data, ((u_short *)userPtr)++); - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - } - sound.bal = bal; - sound.data = data; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - u_long data = sound.data; - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - long used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - while (frameLeft) { - if (bal < 0) { - if (!userCount) - break; - get_user(data, userPtr++); - data ^= 0x80; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - while (frameLeft >= 2) { - if (bal < 0) { - if (userCount < 2) - break; - get_user(data, ((u_short *)userPtr)++); - data ^= 0x8080; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - } - sound.bal = bal; - sound.data = data; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - u_long data = sound.data; - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - long used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - get_user(data, ((u_short *)userPtr)++); - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - get_user(data, ((u_int *)userPtr)++); - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } - sound.bal = bal; - sound.data = data; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - u_long data = sound.data; - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - long used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - get_user(data, ((u_short *)userPtr)++); - data ^= 0x8000; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - get_user(data, ((u_int *)userPtr)++); - data ^= 0x80008000; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } - sound.bal = bal; - sound.data = data; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - u_long data = sound.data; - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - long used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data); - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - get_user(data, ((u_int *)userPtr)++); - data = le2be16dbl(data); - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } - sound.bal = bal; - sound.data = data; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - u_long data = sound.data; - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - long used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data) ^ 0x8000; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - get_user(data, ((u_int *)userPtr)++); - data = le2be16dbl(data) ^ 0x80008000; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - } - sound.bal = bal; - sound.data = data; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); + +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = sound.bal; + long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!sound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = sound.data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + sound.data = data; + } + sound.bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return(used); } #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA -static long ami_ct_law(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ami_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; - long count, used; + char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; + ssize_t count, used; + + if (!sound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + count = min(userCount, frameLeft) & ~1; + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = table[data]; + count--; + } + } else { + u_char *left = &frame[*frameUsed>>1]; + u_char *right = left+sq.block_size_half; + count = min(userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *left++ = table[data]; + if (get_user(data, userPtr++)) + return -EFAULT; + *right++ = table[data]; + count--; + } + } + *frameUsed += used; + return(used); +} - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - count = min(userCount, frameLeft) & ~1; - used = count; - while (count > 0) { - u_char data; - get_user(data, userPtr++); - *p++ = table[data]; - count--; - } - } else { - u_char *left = &frame[*frameUsed>>1]; - u_char *right = left+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - u_char data; - get_user(data, userPtr++); - *left++ = table[data]; - get_user(data, userPtr++); - *right++ = table[data]; - count--; + +static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) +{ + ssize_t count, used; + + if (!sound.soft.stereo) { + void *p = &frame[*frameUsed]; + count = min(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+sq.block_size_half; + count = min(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); + *frameUsed += used; + return(used); } -static long ami_ct_s8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ami_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) { - long count, used; + ssize_t count, used; + + if (!sound.soft.stereo) { + char *p = &frame[*frameUsed]; + count = min(userCount, frameLeft) & ~1; + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = data ^ 0x80; + count--; + } + } else { + u_char *left = &frame[*frameUsed>>1]; + u_char *right = left+sq.block_size_half; + count = min(userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *left++ = data ^ 0x80; + if (get_user(data, userPtr++)) + return -EFAULT; + *right++ = data ^ 0x80; + count--; + } + } + *frameUsed += used; + return(used); +} - if (!sound.soft.stereo) { - void *p = &frame[*frameUsed]; - count = min(userCount, frameLeft) & ~1; - used = count; - copy_from_user(p, userPtr, count); - } else { - u_char *left = &frame[*frameUsed>>1]; - u_char *right = left+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - get_user(*left++, userPtr++); - get_user(*right++, userPtr++); - count--; + +static ssize_t ami_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_short data; + + if (!sound.soft.stereo) { + u_char *high = &frame[*frameUsed>>1]; + u_char *low = high+sq.block_size_half; + count = min(userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; + count--; + } + } else { + u_char *lefth = &frame[*frameUsed>>2]; + u_char *leftl = lefth+sq.block_size_quarter; + u_char *righth = lefth+sq.block_size_half; + u_char *rightl = righth+sq.block_size_quarter; + count = min(userCount, frameLeft)>>2 & ~1; + used = count*4; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; + count--; + } } - } - *frameUsed += used; - return(used); + *frameUsed += used; + return(used); } -static long ami_ct_u8(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t ami_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; + ssize_t count, used; + u_short data; + + if (!sound.soft.stereo) { + u_char *high = &frame[*frameUsed>>1]; + u_char *low = high+sq.block_size_half; + count = min(userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; + count--; + } + } else { + u_char *lefth = &frame[*frameUsed>>2]; + u_char *leftl = lefth+sq.block_size_quarter; + u_char *righth = lefth+sq.block_size_half; + u_char *rightl = righth+sq.block_size_quarter; + count = min(userCount, frameLeft)>>2 & ~1; + used = count*4; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; + count--; + } + } + *frameUsed += used; + return(used); +} - if (!sound.soft.stereo) { - char *p = &frame[*frameUsed]; - count = min(userCount, frameLeft) & ~1; - used = count; - while (count > 0) { - u_char data; - get_user(data, userPtr++); - *p++ = data ^ 0x80; - count--; - } - } else { - u_char *left = &frame[*frameUsed>>1]; - u_char *right = left+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - u_char data; - get_user(data, userPtr++); - *left++ = data ^ 0x80; - get_user(data, userPtr++); - *right++ = data ^ 0x80; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - long count, used; - u_long data; - - if (!sound.soft.stereo) { - u_char *high = &frame[*frameUsed>>1]; - u_char *low = high+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - *high++ = data>>8; - *low++ = (data>>2) & 0x3f; - count--; - } - } else { - u_char *lefth = &frame[*frameUsed>>2]; - u_char *leftl = lefth+sq.block_size_quarter; - u_char *righth = lefth+sq.block_size_half; - u_char *rightl = righth+sq.block_size_quarter; - count = min(userCount, frameLeft)>>2 & ~1; - used = count*4; + +static ssize_t ami_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_short data; + + if (!sound.soft.stereo) { + u_char *high = &frame[*frameUsed>>1]; + u_char *low = high+sq.block_size_half; + count = min(userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; + count--; + } + } else { + u_char *lefth = &frame[*frameUsed>>2]; + u_char *leftl = lefth+sq.block_size_quarter; + u_char *righth = lefth+sq.block_size_half; + u_char *rightl = righth+sq.block_size_quarter; + count = min(userCount, frameLeft)>>2 & ~1; + used = count*4; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; + count--; + } + } + *frameUsed += used; + return(used); +} + + +static ssize_t ami_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_short data; + + if (!sound.soft.stereo) { + u_char *high = &frame[*frameUsed>>1]; + u_char *low = high+sq.block_size_half; + count = min(userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; + count--; + } + } else { + u_char *lefth = &frame[*frameUsed>>2]; + u_char *leftl = lefth+sq.block_size_quarter; + u_char *righth = lefth+sq.block_size_half; + u_char *rightl = righth+sq.block_size_quarter; + count = min(userCount, frameLeft)>>2 & ~1; + used = count*4; + while (count > 0) { + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; + count--; + } + } + *frameUsed += used; + return(used); +} +#endif /* CONFIG_AMIGA */ + +#ifdef CONFIG_PMAC +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + short *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16; + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - get_user(data, ((u_short *)userPtr)++); - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) -{ - long count, used; - u_long data; - - if (!sound.soft.stereo) { - u_char *high = &frame[*frameUsed>>1]; - u_char *low = high+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data ^= 0x8000; - *high++ = data>>8; - *low++ = (data>>2) & 0x3f; - count--; - } - } else { - u_char *lefth = &frame[*frameUsed>>2]; - u_char *leftl = lefth+sq.block_size_quarter; - u_char *righth = lefth+sq.block_size_half; - u_char *rightl = righth+sq.block_size_quarter; - count = min(userCount, frameLeft)>>2 & ~1; - used = count*4; + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data ^= 0x8000; - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - get_user(data, ((u_short *)userPtr)++); - data ^= 0x8000; - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + } + *p++ = val; + count--; } - } - *frameUsed += used; - return(used); + *frameUsed += used * 4; + return stereo? used * 2: used; } -static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; - u_long data; + ssize_t count, used; + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + if (get_user(data, up++)) + return -EFAULT; + *fp++ = data; + *fp++ = data; + count--; + } + } else { + if (copy_from_user(fp, userPtr, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} - if (!sound.soft.stereo) { - u_char *high = &frame[*frameUsed>>1]; - u_char *low = high+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data); - *high++ = data>>8; - *low++ = (data>>2) & 0x3f; - count--; - } - } else { - u_char *lefth = &frame[*frameUsed>>2]; - u_char *leftl = lefth+sq.block_size_quarter; - u_char *righth = lefth+sq.block_size_half; - u_char *rightl = righth+sq.block_size_quarter; - count = min(userCount, frameLeft)>>2 & ~1; - used = count*4; +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data); - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data); - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; + int data; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + *fp++ = data; + if (stereo) { + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + } + *fp++ = data; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned short *table = (unsigned short *) + (sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16); + unsigned int data = sound.data; + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + int bal = sound.bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int utotal, ftotal; + int stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + 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]; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + table[c]; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; } - } - *frameUsed += used; - return(used); + sound.bal = bal; + sound.data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; } -static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount, - u_char frame[], long *frameUsed, long frameLeft) +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long count, used; - u_long data; + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = sound.data; + int bal = sound.bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + 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 << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + (c << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + sound.bal = bal; + sound.data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} - if (!sound.soft.stereo) { - u_char *high = &frame[*frameUsed>>1]; - u_char *low = high+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data) ^ 0x8000; - *high++ = data>>8; - *low++ = (data>>2) & 0x3f; - count--; - } - } else { - u_char *lefth = &frame[*frameUsed>>2]; - u_char *leftl = lefth+sq.block_size_quarter; - u_char *righth = lefth+sq.block_size_half; - u_char *rightl = righth+sq.block_size_quarter; - count = min(userCount, frameLeft)>>2 & ~1; - used = count*4; - while (count > 0) { - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data) ^ 0x8000; - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - get_user(data, ((u_short *)userPtr)++); - data = le2be16(data) ^ 0x8000; - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; - } - } - *frameUsed += used; - return(used); + +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = sound.data; + int bal = sound.bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + 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 ^ 0x80) << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + ((c ^ 0x80) << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + sound.bal = bal; + sound.data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; } -#endif /* CONFIG_AMIGA */ + + +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = sound.data; + unsigned short *up = (unsigned short *) userPtr; + int bal = sound.bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + c; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + sound.bal = bal; + sound.data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + + +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = sound.data; + unsigned short *up = (unsigned short *) userPtr; + int bal = sound.bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + (c ^ mask); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + sound.bal = bal; + sound.data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + +#endif /* CONFIG_PMAC */ #ifdef CONFIG_ATARI static TRANS transTTNormal = { - ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, NULL, NULL, NULL, NULL + ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, NULL, NULL, NULL, NULL }; static TRANS transTTExpanding = { - ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, NULL, NULL, NULL, NULL + ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, NULL, NULL, NULL, NULL }; static TRANS transFalconNormal = { - ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, ata_ct_s16be, ata_ct_u16be, - ata_ct_s16le, ata_ct_u16le + ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, + ata_ct_s16be, ata_ct_u16be, ata_ct_s16le, ata_ct_u16le }; static TRANS transFalconExpanding = { - ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, ata_ctx_s16be, - ata_ctx_u16be, ata_ctx_s16le, ata_ctx_u16le + ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, + ata_ctx_s16be, ata_ctx_u16be, ata_ctx_s16le, ata_ctx_u16le }; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA static TRANS transAmiga = { - ami_ct_law, ami_ct_law, ami_ct_s8, ami_ct_u8, ami_ct_s16be, ami_ct_u16be, - ami_ct_s16le, ami_ct_u16le + ami_ct_law, ami_ct_law, ami_ct_s8, ami_ct_u8, + ami_ct_s16be, ami_ct_u16be, ami_ct_s16le, ami_ct_u16le }; #endif /* CONFIG_AMIGA */ +#ifdef CONFIG_PMAC +static TRANS transAwacsNormal = { + pmac_ct_law, pmac_ct_law, pmac_ct_s8, pmac_ct_u8, + pmac_ct_s16, pmac_ct_u16, pmac_ct_s16, pmac_ct_u16 +}; + +static TRANS transAwacsExpand = { + pmac_ctx_law, pmac_ctx_law, pmac_ctx_s8, pmac_ctx_u8, + pmac_ctx_s16, pmac_ctx_u16, pmac_ctx_s16, pmac_ctx_u16 +}; +#endif /* CONFIG_PMAC */ /*** Low level stuff *********************************************************/ @@ -1583,40 +2215,40 @@ static TRANS transAmiga = { static void *AtaAlloc(unsigned int size, int flags) { - return( atari_stram_alloc( size, NULL, "dmasound" )); + return( atari_stram_alloc( size, NULL, "dmasound" )); } static void AtaFree(void *obj, unsigned int size) { - atari_stram_free( obj ); + atari_stram_free( obj ); } static int 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. */ - mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ - mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ - mfp.tim_ct_a = 8; /* Turn on event counting. */ - /* Register interrupt handler. */ - request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW, - "DMA sound", ata_sq_interrupt); - mfp.int_en_a |= 0x20; /* Turn interrupt on. */ - mfp.int_mk_a |= 0x20; - return(1); + /* 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. */ + mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ + mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ + mfp.tim_ct_a = 8; /* Turn on event counting. */ + /* Register interrupt handler. */ + request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW, + "DMA sound", ata_sq_interrupt); + mfp.int_en_a |= 0x20; /* Turn interrupt on. */ + mfp.int_mk_a |= 0x20; + return(1); } #ifdef MODULE static void AtaIrqCleanUp(void) { - mfp.tim_ct_a = 0; /* stop timer */ - mfp.int_en_a &= ~0x20; /* turn interrupt off */ - free_irq(IRQ_MFP_TIMA, ata_sq_interrupt); + mfp.tim_ct_a = 0; /* stop timer */ + mfp.int_en_a &= ~0x20; /* turn interrupt off */ + free_irq(IRQ_MFP_TIMA, ata_sq_interrupt); } #endif /* MODULE */ @@ -1628,17 +2260,17 @@ static void AtaIrqCleanUp(void) static int AtaSetBass(int bass) { - sound.bass = TONE_VOXWARE_TO_DB(bass); - atari_microwire_cmd(MW_LM1992_BASS(sound.bass)); - return(TONE_DB_TO_VOXWARE(sound.bass)); + sound.bass = TONE_VOXWARE_TO_DB(bass); + atari_microwire_cmd(MW_LM1992_BASS(sound.bass)); + return(TONE_DB_TO_VOXWARE(sound.bass)); } static int AtaSetTreble(int treble) { - sound.treble = TONE_VOXWARE_TO_DB(treble); - atari_microwire_cmd(MW_LM1992_TREBLE(sound.treble)); - return(TONE_DB_TO_VOXWARE(sound.treble)); + sound.treble = TONE_VOXWARE_TO_DB(treble); + atari_microwire_cmd(MW_LM1992_TREBLE(sound.treble)); + return(TONE_DB_TO_VOXWARE(sound.treble)); } @@ -1650,86 +2282,86 @@ static int AtaSetTreble(int treble) static void TTSilence(void) { - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */ + 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}; + int mode, i, idx; + const int freq[4] = {50066, 25033, 12517, 6258}; - /* search a frequency that fits into the allowed error range */ + /* search a frequency that fits into the allowed error range */ - idx = -1; - for (i = 0; i < arraysize(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(sound.soft.speed - freq[i]) / freq[i]) < catchRadius) - idx = i; - if (idx > -1) { - sound.soft.speed = freq[idx]; - sound.trans = &transTTNormal; - } else - sound.trans = &transTTExpanding; - - TTSilence(); - sound.hard = sound.soft; - - if (sound.hard.speed > 50066) { - /* we would need to squeeze the sound, but we won't do that */ - sound.hard.speed = 50066; - mode = DMASND_MODE_50KHZ; - sound.trans = &transTTNormal; - } else if (sound.hard.speed > 25033) { - sound.hard.speed = 50066; - mode = DMASND_MODE_50KHZ; - } else if (sound.hard.speed > 12517) { - sound.hard.speed = 25033; - mode = DMASND_MODE_25KHZ; - } else if (sound.hard.speed > 6258) { - sound.hard.speed = 12517; - mode = DMASND_MODE_12KHZ; - } else { - sound.hard.speed = 6258; - mode = DMASND_MODE_6KHZ; - } - - tt_dmasnd.mode = (sound.hard.stereo ? - DMASND_MODE_STEREO : DMASND_MODE_MONO) | - DMASND_MODE_8BIT | mode; - - sound.bal = -sound.soft.speed; + idx = -1; + for (i = 0; i < arraysize(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(sound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + sound.soft.speed = freq[idx]; + sound.trans = &transTTNormal; + } else + sound.trans = &transTTExpanding; + + TTSilence(); + sound.hard = sound.soft; + + if (sound.hard.speed > 50066) { + /* we would need to squeeze the sound, but we won't do that */ + sound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + sound.trans = &transTTNormal; + } else if (sound.hard.speed > 25033) { + sound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + } else if (sound.hard.speed > 12517) { + sound.hard.speed = 25033; + mode = DMASND_MODE_25KHZ; + } else if (sound.hard.speed > 6258) { + sound.hard.speed = 12517; + mode = DMASND_MODE_12KHZ; + } else { + sound.hard.speed = 6258; + mode = DMASND_MODE_6KHZ; + } + + tt_dmasnd.mode = (sound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + DMASND_MODE_8BIT | mode; + + sound.bal = -sound.soft.speed; } static int TTSetFormat(int format) { - /* TT sound DMA supports only 8bit modes */ + /* TT sound DMA supports only 8bit modes */ - switch (format) { + switch (format) { case AFMT_QUERY: - return(sound.soft.format); + return(sound.soft.format); case AFMT_MU_LAW: case AFMT_A_LAW: case AFMT_S8: case AFMT_U8: - break; + break; default: - format = AFMT_S8; - } + format = AFMT_S8; + } - sound.soft.format = format; - sound.soft.size = 8; - if (sound.minDev == SND_DEV_DSP) { - sound.dsp.format = format; - sound.dsp.size = 8; - } - TTInit(); + sound.soft.format = format; + sound.soft.size = 8; + if (sound.minDev == SND_DEV_DSP) { + sound.dsp.format = format; + sound.dsp.size = 8; + } + TTInit(); - return(format); + return(format); } @@ -1740,12 +2372,12 @@ static int TTSetFormat(int format) static int TTSetVolume(int volume) { - sound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff); - atari_microwire_cmd(MW_LM1992_BALLEFT(sound.volume_left)); - sound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8); - atari_microwire_cmd(MW_LM1992_BALRIGHT(sound.volume_right)); - return(VOLUME_DB_TO_VOXWARE(sound.volume_left) | - (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8)); + sound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff); + atari_microwire_cmd(MW_LM1992_BALLEFT(sound.volume_left)); + sound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8); + atari_microwire_cmd(MW_LM1992_BALRIGHT(sound.volume_right)); + return(VOLUME_DB_TO_VOXWARE(sound.volume_left) | + (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8)); } @@ -1755,9 +2387,9 @@ static int TTSetVolume(int volume) static int TTSetGain(int gain) { - sound.gain = GAIN_VOXWARE_TO_DB(gain); - atari_microwire_cmd(MW_LM1992_VOLUME(sound.gain)); - return GAIN_DB_TO_VOXWARE(sound.gain); + sound.gain = GAIN_VOXWARE_TO_DB(gain); + atari_microwire_cmd(MW_LM1992_VOLUME(sound.gain)); + return GAIN_DB_TO_VOXWARE(sound.gain); } @@ -1769,133 +2401,133 @@ static int TTSetGain(int gain) 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 */ + /* 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}; + 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 */ + /* search a frequency that fits into the allowed error range */ - idx = -1; - for (i = 0; i < arraysize(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(sound.soft.speed - freq[i]) / freq[i]) < catchRadius) - idx = i; - if (idx > -1) { - sound.soft.speed = freq[idx]; - sound.trans = &transFalconNormal; - } else - sound.trans = &transFalconExpanding; - - FalconSilence(); - sound.hard = sound.soft; - - if (sound.hard.size == 16) { - /* the Falcon can play 16bit samples only in stereo */ - sound.hard.stereo = 1; - } - - if (sound.hard.speed > 49170) { - /* we would need to squeeze the sound, but we won't do that */ - sound.hard.speed = 49170; - divider = 1; - sound.trans = &transFalconNormal; - } else if (sound.hard.speed > 32780) { - sound.hard.speed = 49170; - divider = 1; - } else if (sound.hard.speed > 24585) { - sound.hard.speed = 32780; - divider = 2; - } else if (sound.hard.speed > 19668) { - sound.hard.speed = 24585; - divider = 3; - } else if (sound.hard.speed > 16390) { - sound.hard.speed = 19668; - divider = 4; - } else if (sound.hard.speed > 12292) { - sound.hard.speed = 16390; - divider = 5; - } else if (sound.hard.speed > 9834) { - sound.hard.speed = 12292; - divider = 7; - } else if (sound.hard.speed > 8195) { - sound.hard.speed = 9834; - divider = 9; - } else { - sound.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 = (sound.hard.stereo ? - DMASND_MODE_STEREO : DMASND_MODE_MONO) | - ((sound.hard.size == 8) ? - DMASND_MODE_8BIT : DMASND_MODE_16BIT) | - DMASND_MODE_6KHZ; - - sound.bal = -sound.soft.speed; + idx = -1; + for (i = 0; i < arraysize(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(sound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + sound.soft.speed = freq[idx]; + sound.trans = &transFalconNormal; + } else + sound.trans = &transFalconExpanding; + + FalconSilence(); + sound.hard = sound.soft; + + if (sound.hard.size == 16) { + /* the Falcon can play 16bit samples only in stereo */ + sound.hard.stereo = 1; + } + + if (sound.hard.speed > 49170) { + /* we would need to squeeze the sound, but we won't do that */ + sound.hard.speed = 49170; + divider = 1; + sound.trans = &transFalconNormal; + } else if (sound.hard.speed > 32780) { + sound.hard.speed = 49170; + divider = 1; + } else if (sound.hard.speed > 24585) { + sound.hard.speed = 32780; + divider = 2; + } else if (sound.hard.speed > 19668) { + sound.hard.speed = 24585; + divider = 3; + } else if (sound.hard.speed > 16390) { + sound.hard.speed = 19668; + divider = 4; + } else if (sound.hard.speed > 12292) { + sound.hard.speed = 16390; + divider = 5; + } else if (sound.hard.speed > 9834) { + sound.hard.speed = 12292; + divider = 7; + } else if (sound.hard.speed > 8195) { + sound.hard.speed = 9834; + divider = 9; + } else { + sound.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 = (sound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + ((sound.hard.size == 8) ? + DMASND_MODE_8BIT : DMASND_MODE_16BIT) | + DMASND_MODE_6KHZ; + + sound.bal = -sound.soft.speed; } static int FalconSetFormat(int format) { - int size; - /* Falcon sound DMA supports 8bit and 16bit modes */ + int size; + /* Falcon sound DMA supports 8bit and 16bit modes */ - switch (format) { + switch (format) { case AFMT_QUERY: - return(sound.soft.format); + return(sound.soft.format); case AFMT_MU_LAW: case AFMT_A_LAW: case AFMT_U8: case AFMT_S8: - size = 8; - break; + size = 8; + break; case AFMT_S16_BE: case AFMT_U16_BE: case AFMT_S16_LE: case AFMT_U16_LE: - size = 16; - break; + size = 16; + break; default: /* :-) */ - size = 8; - format = AFMT_S8; - } + size = 8; + format = AFMT_S8; + } - sound.soft.format = format; - sound.soft.size = size; - if (sound.minDev == SND_DEV_DSP) { - sound.dsp.format = format; - sound.dsp.size = sound.soft.size; - } + sound.soft.format = format; + sound.soft.size = size; + if (sound.minDev == SND_DEV_DSP) { + sound.dsp.format = format; + sound.dsp.size = sound.soft.size; + } - FalconInit(); + FalconInit(); - return(format); + return(format); } @@ -1909,164 +2541,164 @@ static int FalconSetFormat(int format) static int FalconSetVolume(int volume) { - sound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff); - sound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8); - tt_dmasnd.output_atten = sound.volume_left << 8 | sound.volume_right << 4; - return(VOLUME_ATT_TO_VOXWARE(sound.volume_left) | - VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8); + sound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff); + sound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8); + tt_dmasnd.output_atten = sound.volume_left << 8 | sound.volume_right << 4; + return(VOLUME_ATT_TO_VOXWARE(sound.volume_left) | + VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8); } static void ata_sq_play_next_frame(int index) { - char *start, *end; + char *start, *end; - /* used by AtaPlay() if all doubts whether there really is something - * to be played are already wiped out. - */ - start = sq_block_address(sq.front); - end = start+((sq.count == index) ? sq.rear_size : sq.block_size); - /* end might not be a legal virtual address. */ - DMASNDSetEnd(VTOP(end - 1) + 1); - DMASNDSetBase(VTOP(start)); + /* used by AtaPlay() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = sq_block_address(sq.front); + end = start+((sq.count == index) ? sq.rear_size : sq.block_size); + /* end might not be a legal virtual address. */ + DMASNDSetEnd(VTOP(end - 1) + 1); + DMASNDSetBase(VTOP(start)); /* Since only an even number of samples per frame can - be played, we might lose one byte here. (TO DO) */ - sq.front = (sq.front+1) % sq.max_count; - sq.playing++; - tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT; + be played, we might lose one byte here. (TO DO) */ + sq.front = (sq.front+1) % sq.max_count; + sq.playing++; + tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT; } static void AtaPlay(void) { - /* ++TeSche: Note that sq.playing 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 sq.count and sq.playing 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 (sq.playing == 2 || /* DMA is 'full' */ - sq.count <= 0) { /* nothing to do */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - - if (sq.playing == 0) { - /* looks like there's nothing 'in' the DMA yet, so try - * to put two frames into it (at least one is available). - */ - if (sq.count == 1 && sq.rear_size < sq.block_size && !sq.syncing) { - /* hmmm, the only existing frame is not - * yet filled and we're not syncing? - */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - ata_sq_play_next_frame(1); - if (sq.count == 1) { - /* no more frames */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - if (sq.count == 2 && sq.rear_size < sq.block_size && !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; - } - ata_sq_play_next_frame(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 sq.count. + /* ++TeSche: Note that sq.playing 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 sq.count and sq.playing 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! */ - if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing) { - /* hmmm, the only existing frame is not - * yet filled and we're not syncing? - */ - atari_enable_irq(IRQ_MFP_TIMA); - return; + atari_disable_irq(IRQ_MFP_TIMA); + + if (sq.playing == 2 || /* DMA is 'full' */ + sq.count <= 0) { /* nothing to do */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + + if (sq.playing == 0) { + /* looks like there's nothing 'in' the DMA yet, so try + * to put two frames into it (at least one is available). + */ + if (sq.count == 1 && sq.rear_size < sq.block_size && !sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + ata_sq_play_next_frame(1); + if (sq.count == 1) { + /* no more frames */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + if (sq.count == 2 && sq.rear_size < sq.block_size && !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; + } + ata_sq_play_next_frame(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 sq.count. + */ + if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + ata_sq_play_next_frame(2); } - ata_sq_play_next_frame(2); - } - atari_enable_irq(IRQ_MFP_TIMA); + atari_enable_irq(IRQ_MFP_TIMA); } static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp) { #if 0 - /* ++TeSche: if you should want to test this... */ - static int cnt = 0; - if (sq.playing == 2) - if (++cnt == 10) { - /* simulate losing an interrupt */ - cnt = 0; - return; - } + /* ++TeSche: if you should want to test this... */ + static int cnt = 0; + if (sq.playing == 2) + if (++cnt == 10) { + /* simulate losing an interrupt */ + cnt = 0; + return; + } #endif - if (sq.ignore_int && (sound.mach.type == DMASND_FALCON)) { - /* ++TeSche: Falcon only: ignore first irq because it comes - * immediately after starting a frame. after that, irqs come - * (almost) like on the TT. - */ - sq.ignore_int = 0; - return; - } + if (sq.ignore_int && (sound.mach.type == DMASND_FALCON)) { + /* ++TeSche: Falcon only: ignore first irq because it comes + * immediately after starting a frame. after that, irqs come + * (almost) like on the TT. + */ + sq.ignore_int = 0; + return; + } - if (!sq.playing) { - /* playing was interrupted and sq_reset() has already cleared - * the sq variables, so better don't do anything here. + if (!sq.playing) { + /* playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(sq.sync_queue); + return; + } + + /* 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. */ - WAKE_UP(sq.sync_queue); - return; - } - - /* 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. - */ - sq.count--; - sq.playing--; - - if (!sq.playing) { - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - sq.ignore_int = 1; - } + sq.count--; + sq.playing--; - WAKE_UP(sq.write_queue); + if (!sq.playing) { + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + sq.ignore_int = 1; + } + + WAKE_UP(sq.write_queue); /* At least one block of the queue is free now - so wake up a writing process blocked because - of a full queue. */ - - if ((sq.playing != 1) || (sq.count != 1)) - /* We must be a bit carefully here: sq.count indicates the - * number of buffers used and not the number of frames to - * be played. If sq.count==1 and sq.playing==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(); + so wake up a writing process blocked because + of a full queue. */ + + if ((sq.playing != 1) || (sq.count != 1)) + /* We must be a bit carefully here: sq.count indicates the + * number of buffers used and not the number of frames to + * be played. If sq.count==1 and sq.playing==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 (!sq.playing) WAKE_UP(sq.sync_queue); + if (!sq.playing) WAKE_UP(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. */ + is nothing to play any more. Wake up a process + waiting for audio output to drain. */ } #endif /* CONFIG_ATARI */ @@ -2080,107 +2712,107 @@ static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp) static void *AmiAlloc(unsigned int size, int flags) { - return(amiga_chip_alloc((long)size)); + return(amiga_chip_alloc((long)size)); } static void AmiFree(void *obj, unsigned int size) { - amiga_chip_free (obj); + amiga_chip_free (obj); } static int AmiIrqInit(void) { - /* turn off DMA for audio channels */ - custom.dmacon = AMI_AUDIO_OFF; + /* turn off DMA for audio channels */ + custom.dmacon = AMI_AUDIO_OFF; - /* Register interrupt handler. */ - if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0, - "DMA sound", ami_sq_interrupt)) - return(0); - return(1); + /* Register interrupt handler. */ + if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0, + "DMA sound", ami_sq_interrupt)) + return(0); + return(1); } #ifdef MODULE static void AmiIrqCleanUp(void) { - /* turn off DMA for audio channels */ - custom.dmacon = AMI_AUDIO_OFF; - /* release the interrupt */ - free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt); + /* turn off DMA for audio channels */ + custom.dmacon = AMI_AUDIO_OFF; + /* release the interrupt */ + free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt); } #endif /* MODULE */ static void AmiSilence(void) { - /* turn off DMA for audio channels */ - custom.dmacon = AMI_AUDIO_OFF; + /* turn off DMA for audio channels */ + custom.dmacon = AMI_AUDIO_OFF; } static void AmiInit(void) { - int period, i; - - AmiSilence(); - - if (sound.soft.speed) - period = amiga_colorclock/sound.soft.speed-1; - else - period = amiga_audio_min_period; - sound.hard = sound.soft; - sound.trans = &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; - } - sound.hard.speed = amiga_colorclock/(period+1); + int period, i; + + AmiSilence(); + + if (sound.soft.speed) + period = amiga_colorclock/sound.soft.speed-1; + else + period = amiga_audio_min_period; + sound.hard = sound.soft; + sound.trans = &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; + } + sound.hard.speed = amiga_colorclock/(period+1); - for (i = 0; i < 4; i++) - custom.aud[i].audper = period; - amiga_audio_period = period; + for (i = 0; i < 4; i++) + custom.aud[i].audper = period; + amiga_audio_period = period; - AmiSetTreble(50); /* recommended for newer amiga models */ + AmiSetTreble(50); /* recommended for newer amiga models */ } static int AmiSetFormat(int format) { - int size; + int size; - /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */ + /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */ - switch (format) { + switch (format) { case AFMT_QUERY: - return(sound.soft.format); + return(sound.soft.format); case AFMT_MU_LAW: case AFMT_A_LAW: case AFMT_U8: case AFMT_S8: - size = 8; - break; + size = 8; + break; case AFMT_S16_BE: case AFMT_U16_BE: case AFMT_S16_LE: case AFMT_U16_LE: - size = 16; - break; + size = 16; + break; default: /* :-) */ - size = 8; - format = AFMT_S8; - } + size = 8; + format = AFMT_S8; + } - sound.soft.format = format; - sound.soft.size = size; - if (sound.minDev == SND_DEV_DSP) { - sound.dsp.format = format; - sound.dsp.size = sound.soft.size; - } - AmiInit(); + sound.soft.format = format; + sound.soft.size = size; + if (sound.minDev == SND_DEV_DSP) { + sound.dsp.format = format; + sound.dsp.size = sound.soft.size; + } + AmiInit(); - return(format); + return(format); } @@ -2190,22 +2822,22 @@ static int AmiSetFormat(int format) static int AmiSetVolume(int volume) { - sound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff); - custom.aud[0].audvol = sound.volume_left; - sound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8); - custom.aud[1].audvol = sound.volume_right; - return(VOLUME_AMI_TO_VOXWARE(sound.volume_left) | - (VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8)); + sound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff); + custom.aud[0].audvol = sound.volume_left; + sound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8); + custom.aud[1].audvol = sound.volume_right; + return(VOLUME_AMI_TO_VOXWARE(sound.volume_left) | + (VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8)); } static int AmiSetTreble(int treble) { - sound.treble = treble; - if (treble < 50) - ciaa.pra &= ~0x02; - else - ciaa.pra |= 0x02; - return(treble); + sound.treble = treble; + if (treble < 50) + ciaa.pra &= ~0x02; + else + ciaa.pra |= 0x02; + return(treble); } @@ -2216,164 +2848,550 @@ static int AmiSetTreble(int treble) static void ami_sq_play_next_frame(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 = sq_block_address(sq.front); - size = (sq.count == index ? sq.rear_size : sq.block_size)>>1; - - if (sound.hard.stereo) { - ch0 = start; - ch1 = start+sq.block_size_half; - size >>= 1; - } else { - ch0 = start; - ch1 = start; - } - if (sound.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 (sound.volume_left == 64 && sound.volume_right == 64) { - /* We can play pseudo 14-bit only with the maximum volume */ - ch3 = ch0+sq.block_size_quarter; - ch2 = ch1+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.dmacon = AMI_AUDIO_8; - } - sq.front = (sq.front+1) % sq.max_count; - sq.playing |= AMI_PLAY_LOADED; + 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 = sq_block_address(sq.front); + size = (sq.count == index ? sq.rear_size : sq.block_size)>>1; + + if (sound.hard.stereo) { + ch0 = start; + ch1 = start+sq.block_size_half; + size >>= 1; + } else { + ch0 = start; + ch1 = start; + } + if (sound.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 (sound.volume_left == 64 && sound.volume_right == 64) { + /* We can play pseudo 14-bit only with the maximum volume */ + ch3 = ch0+sq.block_size_quarter; + ch2 = ch1+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.dmacon = AMI_AUDIO_8; + } + sq.front = (sq.front+1) % sq.max_count; + sq.playing |= AMI_PLAY_LOADED; } static void AmiPlay(void) { - int minframes = 1; + int minframes = 1; - custom.intena = IF_AUD0; + custom.intena = IF_AUD0; - if (sq.playing & AMI_PLAY_LOADED) { - /* There's already a frame loaded */ - custom.intena = IF_SETCLR | IF_AUD0; - return; - } + if (sq.playing & AMI_PLAY_LOADED) { + /* There's already a frame loaded */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } - if (sq.playing & AMI_PLAY_PLAYING) - /* Increase threshold: frame 1 is already being played */ - minframes = 2; + if (sq.playing & AMI_PLAY_PLAYING) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; - if (sq.count < minframes) { - /* Nothing to do */ - custom.intena = IF_SETCLR | IF_AUD0; - return; - } + if (sq.count < minframes) { + /* Nothing to do */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } - if (sq.count <= minframes && sq.rear_size < sq.block_size && !sq.syncing) { - /* hmmm, the only existing frame is not - * yet filled and we're not syncing? - */ - custom.intena = IF_SETCLR | IF_AUD0; - return; - } + if (sq.count <= minframes && sq.rear_size < sq.block_size && !sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } - ami_sq_play_next_frame(minframes); + ami_sq_play_next_frame(minframes); - custom.intena = IF_SETCLR | IF_AUD0; + custom.intena = IF_SETCLR | IF_AUD0; } static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp) { - int minframes = 1; + int minframes = 1; - if (!sq.playing) { - /* Playing was interrupted and sq_reset() has already cleared - * the sq variables, so better don't do anything here. - */ - WAKE_UP(sq.sync_queue); - return; - } + if (!sq.playing) { + /* Playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(sq.sync_queue); + return; + } - if (sq.playing & AMI_PLAY_PLAYING) { - /* We've just finished a frame */ - sq.count--; - WAKE_UP(sq.write_queue); - } + if (sq.playing & AMI_PLAY_PLAYING) { + /* We've just finished a frame */ + sq.count--; + WAKE_UP(sq.write_queue); + } - if (sq.playing & AMI_PLAY_LOADED) - /* Increase threshold: frame 1 is already being played */ - minframes = 2; + if (sq.playing & AMI_PLAY_LOADED) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; - /* Shift the flags */ - sq.playing = (sq.playing<<1) & AMI_PLAY_MASK; + /* Shift the flags */ + sq.playing = (sq.playing<<1) & AMI_PLAY_MASK; - if (!sq.playing) - /* No frame is playing, disable audio DMA */ - custom.dmacon = AMI_AUDIO_OFF; + if (!sq.playing) + /* No frame is playing, disable audio DMA */ + custom.dmacon = AMI_AUDIO_OFF; - if (sq.count >= minframes) - /* Try to play the next frame */ - AmiPlay(); + if (sq.count >= minframes) + /* Try to play the next frame */ + AmiPlay(); - if (!sq.playing) - /* Nothing to play anymore. - Wake up a process waiting for audio output to drain. */ - WAKE_UP(sq.sync_queue); + if (!sq.playing) + /* Nothing to play anymore. + Wake up a process waiting for audio output to drain. */ + WAKE_UP(sq.sync_queue); } #endif /* CONFIG_AMIGA */ +#ifdef CONFIG_PMAC + +/* + * PCI PowerMac, with AWACS and DBDMA. + */ + +static void *PMacAlloc(unsigned int size, int flags) +{ + return kmalloc(size, flags); +} + +static void PMacFree(void *ptr, unsigned int size) +{ + kfree(ptr); +} + +static int PMacIrqInit(void) +{ + if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0) + || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)) + return 0; + return 1; +} + +#ifdef MODULE +static void PMacIrqCleanup(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); + /* disable interrupts from awacs interface */ + out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff); + free_irq(awacs_irq, pmac_awacs_intr); + free_irq(awacs_tx_irq, pmac_awacs_tx_intr); + kfree(awacs_tx_cmd_space); + if (beep_buf) + kfree(beep_buf); + kd_mksound = orig_mksound; +} +#endif /* MODULE */ + +static void PMacSilence(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); +} + +static int awacs_freqs[8] = { + 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 +}; + +static void PMacInit(void) +{ + int i, tolerance; + + switch (sound.soft.format) { + case AFMT_S16_LE: + case AFMT_U16_LE: + sound.hard.format = AFMT_S16_LE; + break; + default: + sound.hard.format = AFMT_S16_BE; + break; + } + sound.hard.stereo = 1; + sound.hard.size = 16; + + /* + * If we have a sample rate which is within catchRadius percent + * of the requested value, we don't have to expand the samples. + * Otherwise choose the next higher rate. + */ + i = 8; + do { + tolerance = catchRadius * awacs_freqs[--i] / 100; + } while (sound.soft.speed > awacs_freqs[i] + tolerance && i > 0); + if (sound.soft.speed >= awacs_freqs[i] - tolerance) + sound.trans = &transAwacsNormal; + else + sound.trans = &transAwacsExpand; + sound.hard.speed = awacs_freqs[i]; + awacs_rate_index = i; + + PMacSilence(); + out_le32(&awacs->control, MASK_IEPC | MASK_IEE | (i << 8) | 0x11); + awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3); + awacs_write(awacs_reg[1] | MASK_ADDR1); + out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); + + sound.bal = -sound.soft.speed; +} + +static int PMacSetFormat(int format) +{ + int size; + + switch (format) { + case AFMT_QUERY: + return sound.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: /* :-) */ + printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", + format); + size = 8; + format = AFMT_U8; + } + + sound.soft.format = format; + sound.soft.size = size; + if (sound.minDev == SND_DEV_DSP) { + sound.dsp.format = format; + sound.dsp.size = size; + } + + PMacInit(); + + return format; +} + +#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99)) +#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15)) + +static int awacs_get_volume(int reg, int lshift) +{ + int volume; + + volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf); + volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8; + return volume; +} + +static int awacs_volume_setter(int volume, int n, int mute, int lshift) +{ + int r1, rn; + + if (mute && volume == 0) { + r1 = awacs_reg[1] | mute; + } else { + r1 = awacs_reg[1] & ~mute; + rn = awacs_reg[n] & ~(0xf | (0xf << lshift)); + rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift); + rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf; + awacs_reg[n] = rn; + awacs_write((n << 12) | rn); + volume = awacs_get_volume(rn, lshift); + } + if (r1 != awacs_reg[1]) { + awacs_reg[1] = r1; + awacs_write(r1 | MASK_ADDR1); + } + return volume; +} + +static int PMacSetVolume(int volume) +{ + return awacs_volume_setter(volume, 2, MASK_AMUTE, 6); +} + +static void PMacPlay(void) +{ + volatile struct dbdma_cmd *cp; + int i, count; + unsigned long flags; + + save_flags(flags); cli(); + if (beep_playing) { + /* sound takes precedence over beeps */ + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + || (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); + beep_playing = 0; + } + i = sq.front + sq.playing; + if (i >= sq.max_count) + i -= sq.max_count; + while (sq.playing < 2 && sq.playing < sq.count) { + count = (sq.count == sq.playing + 1)? sq.rear_size: sq.block_size; + if (count < sq.block_size && !sq.syncing) + /* last block not yet filled, and we're not syncing. */ + break; + cp = &awacs_tx_cmds[i]; + st_le16(&cp->req_count, count); + st_le16(&cp->xfer_status, 0); + if (++i >= sq.max_count) + i = 0; + out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP); + out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS); + if (sq.playing == 0) + out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); + out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + ++sq.playing; + } + restore_flags(flags); +} + +static void +pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + int i = sq.front; + int stat; + volatile struct dbdma_cmd *cp; + + while (sq.playing > 0) { + cp = &awacs_tx_cmds[i]; + stat = ld_le16(&cp->xfer_status); + if ((stat & ACTIVE) == 0) + break; /* this frame is still going */ + --sq.count; + --sq.playing; + if (++i >= sq.max_count) + i = 0; + } + if (i != sq.front) + WAKE_UP(sq.write_queue); + sq.front = i; + + PMacPlay(); + + if (!sq.playing) + WAKE_UP(sq.sync_queue); +} + +static void +pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs) +{ + int ctrl = in_le32(&awacs->control); + + if (ctrl & MASK_PORTCHG) { + /* do something when headphone is plugged/unplugged? */ + } + if (ctrl & MASK_CNTLERR) { + printk(KERN_ERR "AWACS: error, status = %x\n", + in_le32(&awacs->codec_stat)); + } + /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ + out_le32(&awacs->control, ctrl); +} + +static void +awacs_write(int val) +{ + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; /* XXX should have timeout */ + out_le32(&awacs->codec_ctrl, val); +} + +static void awacs_nosound(unsigned long xx) +{ + unsigned long flags; + + save_flags(flags); cli(); + if (beep_playing) { + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + beep_playing = 0; + } + restore_flags(flags); +} + +static struct timer_list beep_timer = { + NULL, NULL, 0, 0, awacs_nosound +}; + +static void awacs_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + int srate = awacs_freqs[BEEP_SPEED]; + int period, ncycles, nsamples; + int i, j, f; + short *p; + static int beep_hz_cache; + static int beep_nsamples_cache; + static int beep_volume_cache; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { +#if 1 + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; +#else + /* cancel beep currently playing */ + awacs_nosound(0); + return; +#endif + } + save_flags(flags); cli(); + del_timer(&beep_timer); + if (ticks) { + beep_timer.expires = jiffies + ticks; + add_timer(&beep_timer); + } + if (beep_playing || sq.playing || beep_buf == NULL) { + restore_flags(flags); + return; /* too hard, sorry :-( */ + } + beep_playing = 1; + st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); + restore_flags(flags); + + if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { + nsamples = beep_nsamples_cache; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep_buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep_volume; + j = (j + f) & 0xffff; + } + beep_hz_cache = hz; + beep_volume_cache = beep_volume; + beep_nsamples_cache = nsamples; + } + + st_le16(&beep_dbdma_cmd->req_count, nsamples*4); + st_le16(&beep_dbdma_cmd->xfer_status, 0); + st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); + st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); + + save_flags(flags); cli(); + if (beep_playing) { /* i.e. haven't been terminated already */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (BEEP_SPEED << 8)); + out_le32(&awacs->byteswap, 0); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + } + restore_flags(flags); +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * Save state when going to sleep, restore it afterwards. + */ +static int awacs_sleep_notify(struct notifier_block *this, + unsigned long code, void *x) +{ + switch (code) { + case PBOOK_SLEEP: + /* XXX we should stop any dma in progress when going to sleep + and restart it when we wake. */ + PMacSilence(); + break; + case PBOOK_WAKE: + out_le32(&awacs->control, MASK_IEPC | MASK_IEE | + (awacs_rate_index << 8) | 0x11); + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + awacs_write(awacs_reg[2] | MASK_ADDR2); + awacs_write(awacs_reg[4] | MASK_ADDR4); + out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); + } + return NOTIFY_DONE; +} +#endif /* CONFIG_PMAC_PBOOK */ + +#endif /* CONFIG_PMAC */ /*** Machine definitions *****************************************************/ #ifdef CONFIG_ATARI static MACHINE machTT = { - DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit, + DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit, #ifdef MODULE - AtaIrqCleanUp, + AtaIrqCleanUp, #endif /* MODULE */ - TTInit, TTSilence, TTSetFormat, TTSetVolume, AtaSetBass, AtaSetTreble, - TTSetGain, - AtaPlay + TTInit, TTSilence, TTSetFormat, TTSetVolume, + AtaSetBass, AtaSetTreble, TTSetGain, + AtaPlay }; static MACHINE machFalcon = { - DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit, + DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit, #ifdef MODULE - AtaIrqCleanUp, + AtaIrqCleanUp, #endif /* MODULE */ - FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, AtaSetBass, - AtaSetTreble, NULL, AtaPlay + FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, + AtaSetBass, AtaSetTreble, NULL, + AtaPlay }; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA static MACHINE machAmiga = { - DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit, + DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit, #ifdef MODULE - AmiIrqCleanUp, + AmiIrqCleanUp, #endif /* MODULE */ - AmiInit, AmiSilence, AmiSetFormat, AmiSetVolume, NULL, AmiSetTreble, - NULL, - AmiPlay + AmiInit, AmiSilence, AmiSetFormat, AmiSetVolume, + NULL, AmiSetTreble, NULL, + AmiPlay +}; +#endif /* CONFIG_AMIGA */ + +#ifdef CONFIG_PMAC +static MACHINE machPMac = { + DMASND_AWACS, PMacAlloc, PMacFree, PMacIrqInit, +#ifdef MODULE + PMacIrqCleanup, +#endif /* MODULE */ + PMacInit, PMacSilence, PMacSetFormat, PMacSetVolume, + NULL, NULL, NULL, /* bass, treble, gain */ + PMacPlay }; #endif /* CONFIG_AMIGA */ @@ -2383,117 +3401,118 @@ static MACHINE machAmiga = { static void sound_silence(void) { - /* update hardware settings one more */ - (*sound.mach.init)(); + /* update hardware settings one more */ + (*sound.mach.init)(); - (*sound.mach.silence)(); + (*sound.mach.silence)(); } static void sound_init(void) { - (*sound.mach.init)(); + (*sound.mach.init)(); } static int sound_set_format(int format) { - return(*sound.mach.setFormat)(format); + return(*sound.mach.setFormat)(format); } static int sound_set_speed(int speed) { - if (speed < 0) - return(sound.soft.speed); + if (speed < 0) + return(sound.soft.speed); - sound.soft.speed = speed; - (*sound.mach.init)(); - if (sound.minDev == SND_DEV_DSP) - sound.dsp.speed = sound.soft.speed; + sound.soft.speed = speed; + (*sound.mach.init)(); + if (sound.minDev == SND_DEV_DSP) + sound.dsp.speed = sound.soft.speed; - return(sound.soft.speed); + return(sound.soft.speed); } static int sound_set_stereo(int stereo) { - if (stereo < 0) - return(sound.soft.stereo); + if (stereo < 0) + return(sound.soft.stereo); - stereo = !!stereo; /* should be 0 or 1 now */ + stereo = !!stereo; /* should be 0 or 1 now */ - sound.soft.stereo = stereo; - if (sound.minDev == SND_DEV_DSP) - sound.dsp.stereo = stereo; - (*sound.mach.init)(); + sound.soft.stereo = stereo; + if (sound.minDev == SND_DEV_DSP) + sound.dsp.stereo = stereo; + (*sound.mach.init)(); - return(stereo); + return(stereo); } static int sound_set_volume(int volume) { - return(*sound.mach.setVolume)(volume); + return(*sound.mach.setVolume)(volume); } #ifdef CONFIG_ATARI static int sound_set_bass(int bass) { - return(sound.mach.setBass ? (*sound.mach.setBass)(bass) : 50); + return(sound.mach.setBass ? (*sound.mach.setBass)(bass) : 50); } static int sound_set_gain(int gain) { - return sound.mach.setGain ? sound.mach.setGain(gain) : 100; + return sound.mach.setGain ? sound.mach.setGain(gain) : 100; } #endif /* CONFIG_ATARI */ - +#if defined(CONFIG_ATARI) || defined(CONFIG_AMIGA) static int sound_set_treble(int treble) { - return(sound.mach.setTreble ? (*sound.mach.setTreble)(treble) : 50); + return(sound.mach.setTreble ? (*sound.mach.setTreble)(treble) : 50); } +#endif /* CONFIG_ATARI || CONFIG_AMIGA */ -static long sound_copy_translate(const u_char *userPtr, - unsigned long userCount, - u_char frame[], long *frameUsed, - long frameLeft) +static ssize_t sound_copy_translate(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) { - long (*ct_func)(const u_char *, unsigned long, u_char *, long *, long) = NULL; + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; - switch (sound.soft.format) { + switch (sound.soft.format) { case AFMT_MU_LAW: - ct_func = sound.trans->ct_ulaw; - break; + ct_func = sound.trans->ct_ulaw; + break; case AFMT_A_LAW: - ct_func = sound.trans->ct_alaw; - break; + ct_func = sound.trans->ct_alaw; + break; case AFMT_S8: - ct_func = sound.trans->ct_s8; - break; + ct_func = sound.trans->ct_s8; + break; case AFMT_U8: - ct_func = sound.trans->ct_u8; - break; + ct_func = sound.trans->ct_u8; + break; case AFMT_S16_BE: - ct_func = sound.trans->ct_s16be; - break; + ct_func = sound.trans->ct_s16be; + break; case AFMT_U16_BE: - ct_func = sound.trans->ct_u16be; - break; + ct_func = sound.trans->ct_u16be; + break; case AFMT_S16_LE: - ct_func = sound.trans->ct_s16le; - break; + ct_func = sound.trans->ct_s16le; + break; case AFMT_U16_LE: - ct_func = sound.trans->ct_u16le; - break; - } - if (ct_func) - return(ct_func(userPtr, userCount, frame, frameUsed, frameLeft)); - else - return(0); + ct_func = sound.trans->ct_u16le; + break; + } + if (ct_func) + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); + else + return 0; } @@ -2507,940 +3526,1080 @@ static long sound_copy_translate(const u_char *userPtr, #define RECLEVEL_GAIN_TO_VOXWARE(v) (((v) * 20 + 2) / 3) -static void mixer_init(void) +static int mixer_open(struct inode *inode, struct file *file) { - mixer.busy = 0; - sound.treble = 0; - sound.bass = 0; - switch (sound.mach.type) { -#ifdef CONFIG_ATARI - case DMASND_TT: - atari_microwire_cmd(MW_LM1992_VOLUME(0)); - sound.volume_left = 0; - atari_microwire_cmd(MW_LM1992_BALLEFT(0)); - sound.volume_right = 0; - atari_microwire_cmd(MW_LM1992_BALRIGHT(0)); - atari_microwire_cmd(MW_LM1992_TREBLE(0)); - atari_microwire_cmd(MW_LM1992_BASS(0)); - break; - case DMASND_FALCON: - sound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8; - sound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4; - break; -#endif /* CONFIG_ATARI */ -#ifdef CONFIG_AMIGA - case DMASND_AMIGA: - sound.volume_left = 64; - sound.volume_right = 64; - custom.aud[0].audvol = sound.volume_left; - custom.aud[3].audvol = 1; /* For pseudo 14bit */ - custom.aud[1].audvol = sound.volume_right; - custom.aud[2].audvol = 1; /* For pseudo 14bit */ - sound.treble = 50; - break; -#endif /* CONFIG_AMIGA */ - } -} - - -static int mixer_open(int open_mode) -{ - if (mixer.busy) - return(-EBUSY); - mixer.busy = 1; - return(0); + MOD_INC_USE_COUNT; + mixer.busy = 1; + return 0; } -static int mixer_release(void) +static int mixer_release(struct inode *inode, struct file *file) { - mixer.busy = 0; - return(0); + mixer.busy = 0; + MOD_DEC_USE_COUNT; + return 0; } static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { - int data; - switch (sound.mach.type) { + int data; + switch (sound.mach.type) { #ifdef CONFIG_ATARI case DMASND_FALCON: - switch (cmd) { + switch (cmd) { case SOUND_MIXER_READ_DEVMASK: - return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER)); + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER); case SOUND_MIXER_READ_RECMASK: - return(IOCTL_OUT(arg, SOUND_MASK_MIC)); + return IOCTL_OUT(arg, SOUND_MASK_MIC); case SOUND_MIXER_READ_STEREODEVS: - return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC)); + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC); case SOUND_MIXER_READ_CAPS: - return(IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT)); + return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT); case SOUND_MIXER_READ_VOLUME: - return(IOCTL_OUT(arg, - VOLUME_ATT_TO_VOXWARE(sound.volume_left) | - VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8)); + return IOCTL_OUT(arg, + VOLUME_ATT_TO_VOXWARE(sound.volume_left) | + VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8); 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 */ + 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 IOCTL_OUT(arg, + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) | + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8); case SOUND_MIXER_READ_SPEAKER: - { - int porta; - cli(); - sound_ym.rd_data_reg_sel = 14; - porta = sound_ym.rd_data_reg_sel; - sti(); - return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100)); - } + { + int porta; + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = sound_ym.rd_data_reg_sel; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_volume(data))); - case SOUND_MIXER_WRITE_SPEAKER: - { - int porta; IOCTL_IN(arg, data); - cli(); - sound_ym.rd_data_reg_sel = 14; - porta = (sound_ym.rd_data_reg_sel & ~0x40) | - (data < 50 ? 0x40 : 0); - sound_ym.wd_data = porta; - sti(); - return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100)); - } - } - break; + return IOCTL_OUT(arg, sound_set_volume(data)); + case SOUND_MIXER_WRITE_SPEAKER: + { + int porta; + IOCTL_IN(arg, data); + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = (sound_ym.rd_data_reg_sel & ~0x40) | + (data < 50 ? 0x40 : 0); + sound_ym.wd_data = porta; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + } + break; case DMASND_TT: - switch (cmd) { + switch (cmd) { 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))); + return IOCTL_OUT(arg, + SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS | + (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0)); case SOUND_MIXER_READ_RECMASK: - return(IOCTL_OUT(arg, 0)); + return IOCTL_OUT(arg, 0); case SOUND_MIXER_READ_STEREODEVS: - return(IOCTL_OUT(arg, SOUND_MASK_VOLUME)); + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); case SOUND_MIXER_READ_VOLUME: - return(IOCTL_OUT(arg, - VOLUME_DB_TO_VOXWARE(sound.volume_left) | - (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8))); + return IOCTL_OUT(arg, + VOLUME_DB_TO_VOXWARE(sound.volume_left) | + (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8)); case SOUND_MIXER_READ_BASS: - return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.bass))); + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.bass)); case SOUND_MIXER_READ_TREBLE: - return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.treble))); + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.treble)); case SOUND_MIXER_READ_OGAIN: - return(IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(sound.gain))); + return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(sound.gain)); case SOUND_MIXER_READ_SPEAKER: - { - int porta; - if (MACH_IS_TT) { - cli(); - sound_ym.rd_data_reg_sel = 14; - porta = sound_ym.rd_data_reg_sel; - sti(); - return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100)); - } else - return(-EINVAL); - } + { + int porta; + if (MACH_IS_TT) { + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = sound_ym.rd_data_reg_sel; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + } + break; case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_volume(data))); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_volume(data)); case SOUND_MIXER_WRITE_BASS: - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_bass(data))); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_bass(data)); case SOUND_MIXER_WRITE_TREBLE: - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_treble(data))); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_treble(data)); case SOUND_MIXER_WRITE_OGAIN: - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_gain(data))); - case SOUND_MIXER_WRITE_SPEAKER: - if (MACH_IS_TT) { - int porta; IOCTL_IN(arg, data); - cli(); - sound_ym.rd_data_reg_sel = 14; - porta = (sound_ym.rd_data_reg_sel & ~0x40) | - (data < 50 ? 0x40 : 0); - sound_ym.wd_data = porta; - sti(); - return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100)); - } else - return(-EINVAL); - } - break; + return IOCTL_OUT(arg, sound_set_gain(data)); + case SOUND_MIXER_WRITE_SPEAKER: + if (MACH_IS_TT) { + int porta; + IOCTL_IN(arg, data); + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = (sound_ym.rd_data_reg_sel & ~0x40) | + (data < 50 ? 0x40 : 0); + sound_ym.wd_data = porta; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + } + break; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA case DMASND_AMIGA: - switch (cmd) { + switch (cmd) { case SOUND_MIXER_READ_DEVMASK: - return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE)); + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE); case SOUND_MIXER_READ_RECMASK: - return(IOCTL_OUT(arg, 0)); + return IOCTL_OUT(arg, 0); case SOUND_MIXER_READ_STEREODEVS: - return(IOCTL_OUT(arg, SOUND_MASK_VOLUME)); + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); case SOUND_MIXER_READ_VOLUME: - return(IOCTL_OUT(arg, - VOLUME_AMI_TO_VOXWARE(sound.volume_left) | - VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8)); + return IOCTL_OUT(arg, + VOLUME_AMI_TO_VOXWARE(sound.volume_left) | + VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8); case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_volume(data))); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_volume(data)); case SOUND_MIXER_READ_TREBLE: - return(IOCTL_OUT(arg, sound.treble)); + return IOCTL_OUT(arg, sound.treble); case SOUND_MIXER_WRITE_TREBLE: - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_treble(data))); - } - break; + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_treble(data)); + } + break; #endif /* CONFIG_AMIGA */ - } - - return(-EINVAL); -} - +#ifdef CONFIG_PMAC + case DMASND_AWACS: + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD | SOUND_MASK_RECLEV + | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(awacs_reg[0] | MASK_ADDR0); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_VOLUME: + data = (awacs_reg[1] & MASK_AMUTE)? 0: + awacs_get_volume(awacs_reg[2], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_volume(data)); + case SOUND_MIXER_READ_SPEAKER: + data = (awacs_reg[1] & MASK_CMUTE)? 0: + awacs_get_volume(awacs_reg[4], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_AUDIN; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_AUDIN; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + data &= 0xff; + awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); + if (data >= 25) { + awacs_reg[0] |= MASK_MUX_MIC; + if (data >= 75) + awacs_reg[0] |= MASK_GAINLINE; + } + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = (awacs_reg[0] & MASK_MUX_MIC)? + (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_CD; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + } + break; +#endif + } -/* - * Sound queue stuff, the heart of the driver - */ + return -EINVAL; +} -static void sq_init(int numBufs, int bufSize, char **buffers) +static struct file_operations mixer_fops = { - sq.max_count = numBufs; - sq.block_size = bufSize; - sq.buffers = buffers; - - sq.front = sq.count = 0; - sq.rear = -1; - sq.write_queue = sq.open_queue = sq.sync_queue = 0; - sq.busy = 0; - sq.syncing = 0; + sound_lseek, + NULL, /* mixer_read */ + NULL, /* mixer_write */ + NULL, /* mixer_readdir */ + NULL, /* mixer_poll */ + mixer_ioctl, + NULL, /* mixer_mmap */ + mixer_open, + mixer_release, +}; - sq.playing = 0; +__initfunc(static void mixer_init(void)) +{ +#ifndef MODULE + int mixer_unit; +#endif + mixer_unit = register_sound_mixer(&mixer_fops); + if (mixer_unit < 0) + return; + + mixer.busy = 0; + sound.treble = 0; + sound.bass = 0; + switch (sound.mach.type) { #ifdef CONFIG_ATARI - sq.ignore_int = 0; + case DMASND_TT: + atari_microwire_cmd(MW_LM1992_VOLUME(0)); + sound.volume_left = 0; + atari_microwire_cmd(MW_LM1992_BALLEFT(0)); + sound.volume_right = 0; + atari_microwire_cmd(MW_LM1992_BALRIGHT(0)); + atari_microwire_cmd(MW_LM1992_TREBLE(0)); + atari_microwire_cmd(MW_LM1992_BASS(0)); + break; + case DMASND_FALCON: + sound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8; + sound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4; + break; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA - sq.block_size_half = sq.block_size>>1; - sq.block_size_quarter = sq.block_size_half>>1; + case DMASND_AMIGA: + sound.volume_left = 64; + sound.volume_right = 64; + custom.aud[0].audvol = sound.volume_left; + custom.aud[3].audvol = 1; /* For pseudo 14bit */ + custom.aud[1].audvol = sound.volume_right; + custom.aud[2].audvol = 1; /* For pseudo 14bit */ + sound.treble = 50; + break; #endif /* CONFIG_AMIGA */ + } +} + + +/* + * Sound queue stuff, the heart of the driver + */ + - sound_silence(); +static void sq_setup(int numBufs, int bufSize, char **buffers) +{ +#ifdef CONFIG_PMAC + int i; + volatile struct dbdma_cmd *cp; +#endif /* CONFIG_PMAC */ + + sq.max_count = numBufs; + sq.max_active = numBufs; + sq.block_size = bufSize; + sq.buffers = buffers; + + sq.front = sq.count = 0; + sq.rear = -1; + sq.write_queue = sq.open_queue = sq.sync_queue = 0; + sq.busy = 0; + sq.syncing = 0; - /* whatever you like as startup mode for /dev/dsp, - * (/dev/audio hasn't got a startup mode). note that - * once changed a new open() will *not* restore these! - */ - sound.dsp.format = AFMT_S8; - sound.dsp.stereo = 0; - sound.dsp.size = 8; + sq.playing = 0; - /* set minimum rate possible without expanding */ - switch (sound.mach.type) { #ifdef CONFIG_ATARI - case DMASND_TT: - sound.dsp.speed = 6258; - break; - case DMASND_FALCON: - sound.dsp.speed = 8195; - break; + sq.ignore_int = 0; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA - case DMASND_AMIGA: - sound.dsp.speed = 8000; - break; + sq.block_size_half = sq.block_size>>1; + sq.block_size_quarter = sq.block_size_half>>1; #endif /* CONFIG_AMIGA */ - } - - /* before the first open to /dev/dsp this wouldn't be set */ - sound.soft = sound.dsp; - sound.hard = sound.dsp; +#ifdef CONFIG_PMAC + cp = awacs_tx_cmds; + memset((void *) cp, 0, (numBufs + 1) * sizeof(struct dbdma_cmd)); + for (i = 0; i < numBufs; ++i, ++cp) { + st_le32(&cp->phy_addr, virt_to_bus(buffers[i])); + } + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds)); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds)); +#endif /* CONFIG_PMAC */ } - static void sq_play(void) { - (*sound.mach.play)(); + (*sound.mach.play)(); } /* ++TeSche: radically changed this one too */ -static long sq_write(const char *src, unsigned long uLeft) -{ - long uWritten = 0; - u_char *dest; - long uUsed, bUsed, bLeft; - - /* ++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); - - /* The interrupt doesn't start to play the last, incomplete frame. - * Thus we can append to it without disabling the interrupts! (Note - * also that sq.rear isn't affected by the interrupt.) - */ - - if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) { - dest = sq_block_address(sq.rear); - bUsed = sq.rear_size; - uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); - src += uUsed; - uWritten += uUsed; - uLeft -= uUsed; - sq.rear_size = bUsed; - } - - do { - while (sq.count == sq.max_count) { - sq_play(); - if (NON_BLOCKING(sq.open_mode)) - return(uWritten > 0 ? uWritten : -EAGAIN); - SLEEP(sq.write_queue, ONE_SECOND); - if (SIGNAL_RECEIVED) - return(uWritten > 0 ? uWritten : -EINTR); - } - - /* Here, we can avoid disabling the interrupt by first - * copying and translating the data, and then updating - * the sq variables. Until this is done, the interrupt - * won't see the new frame and we can work on it - * undisturbed. +static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, + loff_t *ppos) +{ + ssize_t uWritten = 0; + u_char *dest; + ssize_t uUsed, bUsed, bLeft; + + /* ++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; + + /* The interrupt doesn't start to play the last, incomplete frame. + * Thus we can append to it without disabling the interrupts! (Note + * also that sq.rear isn't affected by the interrupt.) */ - dest = sq_block_address((sq.rear+1) % sq.max_count); - bUsed = 0; - bLeft = sq.block_size; - uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); - src += uUsed; - uWritten += uUsed; - uLeft -= uUsed; - if (bUsed) { - sq.rear = (sq.rear+1) % sq.max_count; - sq.rear_size = bUsed; - sq.count++; + if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) { + dest = sq_block_address(sq.rear); + bUsed = sq.rear_size; + uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + sq.rear_size = bUsed; } - } while (bUsed); /* uUsed may have been 0 */ - sq_play(); + do { + while (sq.count == sq.max_active) { + sq_play(); + if (NON_BLOCKING(sq.open_mode)) + return uWritten > 0 ? uWritten : -EAGAIN; + SLEEP(sq.write_queue, ONE_SECOND); + if (SIGNAL_RECEIVED) + return uWritten > 0 ? uWritten : -EINTR; + } + + /* Here, we can avoid disabling the interrupt by first + * copying and translating the data, and then updating + * the sq variables. Until this is done, the interrupt + * won't see the new frame and we can work on it + * undisturbed. + */ + + dest = sq_block_address((sq.rear+1) % sq.max_count); + bUsed = 0; + bLeft = sq.block_size; + uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); + if (uUsed <= 0) + break; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + if (bUsed) { + sq.rear = (sq.rear+1) % sq.max_count; + sq.rear_size = bUsed; + sq.count++; + } + } while (bUsed); /* uUsed may have been 0 */ + + sq_play(); - return(uWritten); + return uUsed < 0? uUsed: uWritten; } -static int sq_open(int open_mode) +static int sq_open(struct inode *inode, struct file *file) { - if (sq.busy) { - if (NON_BLOCKING(open_mode)) - return(-EBUSY); - while (sq.busy) { - SLEEP(sq.open_queue, ONE_SECOND); - if (SIGNAL_RECEIVED) - return(-EINTR); + int rc = 0; + + MOD_INC_USE_COUNT; + if (sq.busy) { + rc = -EBUSY; + if (NON_BLOCKING(file->f_flags)) + goto err_out; + rc = -EINTR; + while (sq.busy) { + SLEEP(sq.open_queue, ONE_SECOND); + if (SIGNAL_RECEIVED) + goto err_out; + } + rc = 0; } - } - sq.open_mode = open_mode; - sq.busy = 1; + sq_setup(numBufs, bufSize << 10, sound_buffers); + sq.open_mode = file->f_flags; + sq.busy = 1; #ifdef CONFIG_ATARI - sq.ignore_int = 1; + sq.ignore_int = 1; #endif /* CONFIG_ATARI */ - return(0); + sound.minDev = MINOR(inode->i_rdev) & 0x0f; + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_init(); + if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { + sound_set_speed(8000); + sound_set_stereo(0); + sound_set_format(AFMT_MU_LAW); + } + return 0; +err_out: + MOD_DEC_USE_COUNT; + return rc; } static void sq_reset(void) { - sound_silence(); - sq.playing = 0; - sq.count = 0; - sq.front = (sq.rear+1) % sq.max_count; + sound_silence(); + sq.playing = 0; + sq.count = 0; + sq.front = (sq.rear+1) % sq.max_count; } -static int sq_sync(void) +static int sq_fsync(struct file *filp, struct dentry *dentry) { - int rc = 0; + int rc = 0; + + sq.syncing = 1; + sq_play(); /* there may be an incomplete frame waiting */ + + while (sq.playing) { + SLEEP(sq.sync_queue, ONE_SECOND); + if (SIGNAL_RECEIVED) { + /* While waiting for audio output to drain, an + * interrupt occurred. Stop audio output immediately + * and clear the queue. */ + sq_reset(); + rc = -EINTR; + break; + } + } - sq.syncing = 1; - sq_play(); /* there may be an incomplete frame waiting */ + sq.syncing = 0; + return rc; +} - while (sq.playing) { - SLEEP(sq.sync_queue, ONE_SECOND); - if (SIGNAL_RECEIVED) { - /* While waiting for audio output to drain, an interrupt occurred. - Stop audio output immediately and clear the queue. */ - sq_reset(); - rc = -EINTR; - break; +static int sq_release(struct inode *inode, struct file *file) +{ + int rc = 0; + if (sq.busy) { + rc = sq_fsync(file, file->f_dentry); + sq.busy = 0; + WAKE_UP(sq.open_queue); + /* Wake up a process waiting for the queue being released. + * Note: There may be several processes waiting for a call + * to open() returning. */ } - } - - sq.syncing = 0; - return(rc); + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_silence(); + if (rc == 0) + MOD_DEC_USE_COUNT; + return rc; } -static int sq_release(void) +static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) { - int rc = 0; - if (sq.busy) { - rc = sq_sync(); - sq.busy = 0; - WAKE_UP(sq.open_queue); - /* Wake up a process waiting for the queue being released. - Note: There may be several processes waiting for a call to open() - returning. */ - } - return(rc); + u_long fmt; + int data; + int size, nbufs; + + switch (cmd) { + case SNDCTL_DSP_RESET: + sq_reset(); + return 0; + case SNDCTL_DSP_POST: + case SNDCTL_DSP_SYNC: + return sq_fsync(file, file->f_dentry); + + /* ++TeSche: before changing any of these it's + * probably wise to wait until sound playing has + * settled down. */ + case SNDCTL_DSP_SPEED: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_speed(data)); + case SNDCTL_DSP_STEREO: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data)); + case SOUND_PCM_WRITE_CHANNELS: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); + case SNDCTL_DSP_SETFMT: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_format(data)); + case SNDCTL_DSP_GETFMTS: + fmt = 0; + if (sound.trans) { + if (sound.trans->ct_ulaw) + fmt |= AFMT_MU_LAW; + if (sound.trans->ct_alaw) + fmt |= AFMT_A_LAW; + if (sound.trans->ct_s8) + fmt |= AFMT_S8; + if (sound.trans->ct_u8) + fmt |= AFMT_U8; + if (sound.trans->ct_s16be) + fmt |= AFMT_S16_BE; + if (sound.trans->ct_u16be) + fmt |= AFMT_U16_BE; + if (sound.trans->ct_s16le) + fmt |= AFMT_S16_LE; + if (sound.trans->ct_u16le) + fmt |= AFMT_U16_LE; + } + return IOCTL_OUT(arg, fmt); + case SNDCTL_DSP_GETBLKSIZE: + size = sq.block_size + * sound.soft.size * (sound.soft.stereo + 1) + / (sound.hard.size * (sound.hard.stereo + 1)); + return IOCTL_OUT(arg, size); + case SNDCTL_DSP_SUBDIVIDE: + break; + case SNDCTL_DSP_SETFRAGMENT: + if (sq.count || sq.playing || sq.syncing) + return -EINVAL; + IOCTL_IN(arg, size); + nbufs = size >> 16; + if (nbufs < 2 || nbufs > numBufs) + nbufs = numBufs; + size &= 0xffff; + if (size >= 8 && size <= 30) { + size = 1 << size; + size *= sound.hard.size * (sound.hard.stereo + 1); + size /= sound.soft.size * (sound.soft.stereo + 1); + if (size > (bufSize << 10)) + size = bufSize << 10; + } else + size = bufSize << 10; + sq_setup(numBufs, size, sound_buffers); + sq.max_active = nbufs; + return 0; + + default: + return mixer_ioctl(inode, file, cmd, arg); + } + return -EINVAL; } -/* - * /dev/sndstat - */ +static struct file_operations sq_fops = +{ + sound_lseek, + NULL, /* sq_read */ + sq_write, + NULL, /* sq_readdir */ + NULL, /* sq_poll */ + sq_ioctl, + NULL, /* sq_mmap */ + sq_open, + sq_release, +}; -static void state_init(void) +__initfunc(static void sq_init(void)) { - state.busy = 0; +#ifndef MODULE + int sq_unit; +#endif + sq_unit = register_sound_dsp(&sq_fops); + if (sq_unit < 0) + return; + + /* whatever you like as startup mode for /dev/dsp, + * (/dev/audio hasn't got a startup mode). note that + * once changed a new open() will *not* restore these! + */ + sound.dsp.format = AFMT_U8; + sound.dsp.stereo = 0; + sound.dsp.size = 8; + + /* set minimum rate possible without expanding */ + switch (sound.mach.type) { +#ifdef CONFIG_ATARI + case DMASND_TT: + sound.dsp.speed = 6258; + break; + case DMASND_FALCON: + sound.dsp.speed = 8195; + break; +#endif /* CONFIG_ATARI */ +#ifdef CONFIG_AMIGA + case DMASND_AMIGA: + sound.dsp.speed = 8000; + break; +#endif /* CONFIG_AMIGA */ +#ifdef CONFIG_PMAC + case DMASND_AWACS: + sound.dsp.speed = 8000; + break; +#endif /* CONFIG_PMAC */ + } + + /* before the first open to /dev/dsp this wouldn't be set */ + sound.soft = sound.dsp; + sound.hard = sound.dsp; + + sound_silence(); } +/* + * /dev/sndstat + */ + /* state.buf should not overflow! */ -static int state_open(int open_mode) +static int state_open(struct inode *inode, struct file *file) { - char *buffer = state.buf, *mach = ""; - int len = 0; + char *buffer = state.buf, *mach = ""; + int len = 0; - if (state.busy) - return(-EBUSY); + if (state.busy) + return -EBUSY; - state.ptr = 0; - state.busy = 1; + MOD_INC_USE_COUNT; + state.ptr = 0; + state.busy = 1; - switch (sound.mach.type) { + switch (sound.mach.type) { #ifdef CONFIG_ATARI case DMASND_TT: case DMASND_FALCON: - mach = "Atari "; - break; + mach = "Atari "; + break; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA case DMASND_AMIGA: - mach = "Amiga "; - break; + mach = "Amiga "; + break; #endif /* CONFIG_AMIGA */ - } - len += sprintf(buffer+len, "%sDMA sound driver:\n", mach); +#ifdef CONFIG_PMAC + case DMASND_AWACS: + mach = "PowerMac "; + break; +#endif /* CONFIG_PMAC */ + } + len += sprintf(buffer+len, "%sDMA sound driver:\n", mach); - len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format); - switch (sound.soft.format) { + len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format); + switch (sound.soft.format) { case AFMT_MU_LAW: - len += sprintf(buffer+len, " (mu-law)"); - break; + len += sprintf(buffer+len, " (mu-law)"); + break; case AFMT_A_LAW: - len += sprintf(buffer+len, " (A-law)"); - break; + len += sprintf(buffer+len, " (A-law)"); + break; case AFMT_U8: - len += sprintf(buffer+len, " (unsigned 8 bit)"); - break; + len += sprintf(buffer+len, " (unsigned 8 bit)"); + break; case AFMT_S8: - len += sprintf(buffer+len, " (signed 8 bit)"); - break; + len += sprintf(buffer+len, " (signed 8 bit)"); + break; case AFMT_S16_BE: - len += sprintf(buffer+len, " (signed 16 bit big)"); - break; + len += sprintf(buffer+len, " (signed 16 bit big)"); + break; case AFMT_U16_BE: - len += sprintf(buffer+len, " (unsigned 16 bit big)"); - break; + len += sprintf(buffer+len, " (unsigned 16 bit big)"); + break; case AFMT_S16_LE: - len += sprintf(buffer+len, " (signed 16 bit little)"); - break; + len += sprintf(buffer+len, " (signed 16 bit little)"); + break; case AFMT_U16_LE: - len += sprintf(buffer+len, " (unsigned 16 bit little)"); - break; - } - len += sprintf(buffer+len, "\n"); - len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", - sound.soft.speed, sound.hard.speed); - len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", - sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono"); - switch (sound.mach.type) { + len += sprintf(buffer+len, " (unsigned 16 bit little)"); + break; + } + len += sprintf(buffer+len, "\n"); + len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", + sound.soft.speed, sound.hard.speed); + len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", + sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono"); + switch (sound.mach.type) { #ifdef CONFIG_ATARI case DMASND_TT: - len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n", - sound.volume_left); - len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n", - sound.volume_right); - len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n", - sound.bass); - len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n", - sound.treble); - break; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n", + sound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n", + sound.volume_right); + len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n", + sound.bass); + len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n", + sound.treble); + break; case DMASND_FALCON: - len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n", - sound.volume_left); - len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n", - sound.volume_right); - break; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n", + sound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n", + sound.volume_right); + break; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA case DMASND_AMIGA: - len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n", - sound.volume_left); - len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n", - sound.volume_right); - break; + len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n", + sound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n", + sound.volume_right); + break; #endif /* CONFIG_AMIGA */ - } - len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d\n", - sq.block_size, sq.max_count); - len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, - sq.rear_size); - len += sprintf(buffer+len, "\tsq.playing = %d sq.syncing = %d\n", - sq.playing, sq.syncing); - state.len = len; - return(0); -} - - -static int state_release(void) -{ - state.busy = 0; - return(0); + } + len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" + " sq.max_active = %d\n", + sq.block_size, sq.max_count, sq.max_active); + len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, + sq.rear_size); + len += sprintf(buffer+len, "\tsq.playing = %d sq.syncing = %d\n", + sq.playing, sq.syncing); + state.len = len; + return 0; } -static long state_read(char *dest, unsigned long count) +static int state_release(struct inode *inode, struct file *file) { - int n = state.len-state.ptr; - if (n > count) - n = count; - if (n <= 0) - return(0); - copy_to_user(dest, &state.buf[state.ptr], n); - state.ptr += n; - return(n); + state.busy = 0; + MOD_DEC_USE_COUNT; + return 0; } - -/*** High level stuff ********************************************************/ - - -static int sound_open(struct inode *inode, struct file *file) +static ssize_t state_read(struct file *file, char *buf, size_t count, + loff_t *ppos) { - int dev = MINOR(inode->i_rdev) & 0x0f; - int rc = 0; - - switch (dev) { - case SND_DEV_STATUS: - rc = state_open(file->f_flags); - break; - case SND_DEV_CTL: - rc = mixer_open(file->f_flags); - break; - case SND_DEV_DSP: - case SND_DEV_AUDIO: - rc = sq_open(file->f_flags); - if (rc == 0) { - sound.minDev = dev; - sound.soft = sound.dsp; - sound.hard = sound.dsp; - sound_init(); - if (dev == SND_DEV_AUDIO) { - sound_set_speed(8000); - sound_set_stereo(0); - sound_set_format(AFMT_MU_LAW); - } - } - break; - default: - rc = -ENXIO; - } -#ifdef MODULE - if (rc >= 0) - MOD_INC_USE_COUNT; -#endif - return(rc); + 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 int sound_fsync(struct file *filp, struct dentry *dentry) +static struct file_operations state_fops = { - int dev = MINOR(dentry->d_inode->i_rdev) & 0x0f; - - switch (dev) { - case SND_DEV_STATUS: - case SND_DEV_CTL: - return(0); - case SND_DEV_DSP: - case SND_DEV_AUDIO: - return(sq_sync()); - default: - return(unknown_minor_dev("sound_fsync", dev)); - } -} + sound_lseek, + state_read, + NULL, /* state_write */ + NULL, /* state_readdir */ + NULL, /* state_poll */ + NULL, /* state_ioctl */ + NULL, /* state_mmap */ + state_open, + state_release, +}; -static int sound_release(struct inode *inode, struct file *file) +__initfunc(static void state_init(void)) { - int dev = MINOR(inode->i_rdev); - - switch (dev & 0x0f) { - case SND_DEV_STATUS: - state_release(); - break; - case SND_DEV_CTL: - mixer_release(); - break; - case SND_DEV_DSP: - case SND_DEV_AUDIO: - sq_release(); - sound.soft = sound.dsp; - sound.hard = sound.dsp; - sound_silence(); - break; - default: - return unknown_minor_dev("sound_release", dev); - } -#ifdef MODULE - MOD_DEC_USE_COUNT; +#ifndef MODULE + int state_unit; #endif - return 0; -} - - -static long long sound_lseek(struct file *file, long long offset, int orig) -{ - return -ESPIPE; + state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); + if (state_unit < 0) + return; + state.busy = 0; } -static ssize_t sound_read(struct file *file, char *buf, size_t count, - loff_t *ppos) -{ - struct inode *inode = file->f_dentry->d_inode; - int dev = MINOR(inode->i_rdev); +/*** Common stuff ********************************************************/ - switch (dev & 0x0f) { - case SND_DEV_STATUS: - return(state_read(buf, count)); - case SND_DEV_CTL: - case SND_DEV_DSP: - case SND_DEV_AUDIO: - return(-EPERM); - default: - return(unknown_minor_dev("sound_read", dev)); - } -} - - -static ssize_t sound_write(struct file *file, const char *buf, size_t count, - loff_t *ppos) -{ - struct inode *inode = file->f_dentry->d_inode; - int dev = MINOR(inode->i_rdev); - - switch (dev & 0x0f) { - case SND_DEV_STATUS: - case SND_DEV_CTL: - return(-EPERM); - case SND_DEV_DSP: - case SND_DEV_AUDIO: - return(sq_write(buf, count)); - default: - return(unknown_minor_dev("sound_write", dev)); - } -} - - -static int unknown_minor_dev(char *fname, int dev) +static long long sound_lseek(struct file *file, long long offset, int orig) { - /* printk("%s: Unknown minor device %d\n", fname, dev); */ - return(-ENXIO); + return -ESPIPE; } -static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) -{ - int dev = MINOR(inode->i_rdev); - u_long fmt; - int data; - - switch (dev & 0x0f) { - case SND_DEV_STATUS: - return(-EPERM); - case SND_DEV_CTL: - return(mixer_ioctl(inode, file, cmd, arg)); - case SND_DEV_AUDIO: - case SND_DEV_DSP: - switch (cmd) { - case SNDCTL_DSP_RESET: - sq_reset(); - return(0); - case SNDCTL_DSP_POST: - case SNDCTL_DSP_SYNC: - return(sound_fsync(file, file->f_dentry)); - - /* ++TeSche: before changing any of these it's probably wise to - * wait until sound playing has settled down - */ - case SNDCTL_DSP_SPEED: - sound_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_speed(data))); - case SNDCTL_DSP_STEREO: - sound_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_stereo(data))); - case SOUND_PCM_WRITE_CHANNELS: - sound_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_stereo(data-1)+1)); - case SNDCTL_DSP_SETFMT: - sound_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return(IOCTL_OUT(arg, sound_set_format(data))); - case SNDCTL_DSP_GETFMTS: - fmt = 0; - if (sound.trans) { - if (sound.trans->ct_ulaw) - fmt |= AFMT_MU_LAW; - if (sound.trans->ct_alaw) - fmt |= AFMT_A_LAW; - if (sound.trans->ct_s8) - fmt |= AFMT_S8; - if (sound.trans->ct_u8) - fmt |= AFMT_U8; - if (sound.trans->ct_s16be) - fmt |= AFMT_S16_BE; - if (sound.trans->ct_u16be) - fmt |= AFMT_U16_BE; - if (sound.trans->ct_s16le) - fmt |= AFMT_S16_LE; - if (sound.trans->ct_u16le) - fmt |= AFMT_U16_LE; - } - return(IOCTL_OUT(arg, fmt)); - case SNDCTL_DSP_GETBLKSIZE: - return(IOCTL_OUT(arg, 10240)); - case SNDCTL_DSP_SUBDIVIDE: - case SNDCTL_DSP_SETFRAGMENT: - break; - - default: - return(mixer_ioctl(inode, file, cmd, arg)); - } - break; - - default: - return(unknown_minor_dev("sound_ioctl", dev)); - } - return(-EINVAL); -} - - -static struct file_operations sound_fops = -{ - sound_lseek, - sound_read, - sound_write, - NULL, - NULL, /* select */ - sound_ioctl, - NULL, - sound_open, - sound_release, - sound_fsync -}; - - - /*** Config & Setup **********************************************************/ -void soundcard_init(void) +__initfunc(void dmasound_init(void)) { - int has_sound = 0; - int i; + int has_sound = 0; + int i; +#ifdef CONFIG_PMAC + struct device_node *np; +#endif - switch (m68k_machtype) { +#ifdef __mc68000__ + switch (m68k_machtype) { #ifdef CONFIG_ATARI case MACH_ATARI: - if (ATARIHW_PRESENT(PCM_8BIT)) { - if (ATARIHW_PRESENT(CODEC)) - sound.mach = machFalcon; - else if (ATARIHW_PRESENT(MICROWIRE)) - sound.mach = machTT; - else - break; - if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0) - has_sound = 1; - else - printk("DMA sound driver: Timer A interrupt already in use\n"); - } - break; + if (ATARIHW_PRESENT(PCM_8BIT)) { + if (ATARIHW_PRESENT(CODEC)) + sound.mach = machFalcon; + else if (ATARIHW_PRESENT(MICROWIRE)) + sound.mach = machTT; + else + break; + if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0) + has_sound = 1; + else + printk("DMA sound driver: Timer A interrupt already in use\n"); + } + break; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA case MACH_AMIGA: - if (AMIGAHW_PRESENT(AMI_AUDIO)) { - sound.mach = machAmiga; - has_sound = 1; - } - break; + if (AMIGAHW_PRESENT(AMI_AUDIO)) { + sound.mach = machAmiga; + has_sound = 1; + } + break; #endif /* CONFIG_AMIGA */ - } - if (!has_sound) - return; + } +#endif /* __mc68000__ */ + +#ifdef CONFIG_PMAC + awacs_subframe = 0; + np = find_devices("awacs"); + if (np == 0) { + /* + * powermac G3 models have a node called "davbus" + * with a child called "sound". + */ + struct device_node *sound; + np = find_devices("davbus"); + sound = find_devices("sound"); + if (sound != 0 && sound->parent == np) { + int *sfprop; + sfprop = (int *) get_property(sound, "sub-frame", 0); + if (sfprop != 0 && *sfprop >= 0 && *sfprop < 16) + awacs_subframe = *sfprop; + } + } + if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { + int vol; + sound.mach = machPMac; + has_sound = 1; + awacs = (volatile struct awacs_regs *) + ioremap(np->addrs[0].address, 0x80); + awacs_txdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[1].address, 0x100); + awacs_rxdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[2].address, 0x100); + awacs_irq = np->intrs[0].line; + awacs_tx_irq = np->intrs[1].line; + awacs_rx_irq = np->intrs[2].line; + awacs_tx_cmd_space = kmalloc((numBufs + 4) * sizeof(struct dbdma_cmd), + GFP_KERNEL); + if (awacs_tx_cmd_space == NULL) + goto out_of_memory; + awacs_tx_cmds = (volatile struct dbdma_cmd *) + DBDMA_ALIGN(awacs_tx_cmd_space); + awacs_reg[0] = MASK_MUX_CD; + awacs_reg[1] = MASK_LOOPTHRU | MASK_PAROUT; + /* get default volume from nvram */ + vol = (~nvram_read_byte(0x1308) & 7) << 1; + awacs_reg[2] = vol + (vol << 6); + awacs_reg[4] = vol + (vol << 6); + out_le32(&awacs->control, 0x11); + awacs_write(awacs_reg[0] + MASK_ADDR0); + awacs_write(awacs_reg[1] + MASK_ADDR1); + awacs_write(awacs_reg[2] + MASK_ADDR2); + awacs_write(awacs_reg[4] + MASK_ADDR4); + + /* Initialize beep stuff */ + beep_dbdma_cmd = awacs_tx_cmds + (numBufs + 1); + orig_mksound = kd_mksound; + kd_mksound = awacs_mksound; + beep_buf = (short *) kmalloc(BEEP_BUFLEN * 2, GFP_KERNEL); + if (beep_buf == NULL) + printk(KERN_WARNING "dmasound: no memory for " + "beep buffer\n"); +#ifdef CONFIG_PMAC_PBOOK + notifier_chain_register(&sleep_notifier_list, + &awacs_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + } +#endif /* CONFIG_PMAC */ - /* Set up sound queue, /dev/audio and /dev/dsp. */ - sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); - if (!sound_buffers) { -out_of_memory: - printk("DMA sound driver: Not enough buffer memory, driver disabled!\n"); - return; - } - for (i = 0; i < numBufs; i++) { - sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); - if (!sound_buffers[i]) { - while (i--) - sound.mach.dma_free (sound_buffers[i], bufSize << 10); - kfree (sound_buffers); - sound_buffers = 0; - goto out_of_memory; - } - } + if (!has_sound) + return; -#ifndef MODULE - /* Register driver with the VFS. */ - register_chrdev(SOUND_MAJOR, "sound", &sound_fops); -#endif + /* Set up sound queue, /dev/audio and /dev/dsp. */ + sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); + if (!sound_buffers) { + out_of_memory: + printk("DMA sound driver: Not enough buffer memory, driver disabled!\n"); + return; + } + for (i = 0; i < numBufs; i++) { + sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); + if (!sound_buffers[i]) { + while (i--) + sound.mach.dma_free (sound_buffers[i], bufSize << 10); + kfree (sound_buffers); + sound_buffers = 0; + goto out_of_memory; + } + } - sq_init(numBufs, bufSize << 10, sound_buffers); + sq_setup(numBufs, bufSize << 10, sound_buffers); - /* Set up /dev/sndstat. */ - state_init(); + /* Set default settings. */ + sq_init(); - /* Set up /dev/mixer. */ - mixer_init(); + /* Set up /dev/sndstat. */ + state_init(); - if (!sound.mach.irqinit()) { - printk("DMA sound driver: Interrupt initialization failed\n"); - return; - } + /* Set up /dev/mixer. */ + mixer_init(); + + if (!sound.mach.irqinit()) { + printk("DMA sound driver: Interrupt initialization failed\n"); + return; + } #ifdef MODULE - irq_installed = 1; + irq_installed = 1; #endif - printk("DMA sound driver installed, using %d buffers of %dk.\n", numBufs, - bufSize); - - return; -} + printk("DMA sound driver installed, using %d buffers of %dk.\n", numBufs, + bufSize); -void sound_setup(char *str, int *ints) -{ - /* ++Martin: stub, could possibly be merged with soundcard.c et al later */ + return; } #define MAXARGS 8 /* Should be sufficient for now */ -void dmasound_setup(char *str, int *ints) +__initfunc(void dmasound_setup(char *str, int *ints)) { - /* check the bootstrap parameter for "dmasound=" */ + /* check the bootstrap parameter for "dmasound=" */ - switch (ints[0]) { + switch (ints[0]) { case 3: - if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) - printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius); - else - catchRadius = ints[3]; - /* fall through */ + if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) + printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius); + else + catchRadius = ints[3]; + /* fall through */ case 2: - if (ints[1] < MIN_BUFFERS) - printk("dmasound_setup: illegal number of buffers, using default = %d\n", numBufs); - else - numBufs = ints[1]; - if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) - printk("dmasound_setup: illegal buffer size, using default = %d\n", bufSize); - else - bufSize = ints[2]; - break; + if (ints[1] < MIN_BUFFERS) + printk("dmasound_setup: illegal number of buffers, using default = %d\n", numBufs); + else + numBufs = ints[1]; + if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) + printk("dmasound_setup: illegal buffer size, using default = %d\n", bufSize); + else + bufSize = ints[2]; + break; case 0: - break; + break; default: - printk("dmasound_setup: illegal number of arguments\n"); - } + printk("dmasound_setup: illegal number of arguments\n"); + } } #ifdef MODULE -static int dmasound[MAXARGS] = { 0 }; - int init_module(void) { - int err, i = 0; - int ints[MAXARGS+1]; - - while (i < MAXARGS && dmasound[i]) - ints[i + 1] = dmasound[i++]; - ints[0] = i; - - if (i) - dmasound_setup("dmasound=", ints); - - err = register_chrdev(SOUND_MAJOR, "sound", &sound_fops); - if (err) { - printk("dmasound: driver already loaded/included in kernel\n"); - return err; - } - chrdev_registered = 1; - soundcard_init(); - - return 0; + dmasound_init(); + return 0; } void cleanup_module(void) { - int i; + int i; - if (MOD_IN_USE) - return; + if (irq_installed) { + sound_silence(); + sound.mach.irqcleanup(); + } - if (chrdev_registered) - unregister_chrdev(SOUND_MAJOR, "sound"); + if (sound_buffers) { + for (i = 0; i < numBufs; i++) + sound.mach.dma_free(sound_buffers[i], bufSize << 10); + kfree(sound_buffers); + } - if (irq_installed) { - sound_silence(); - sound.mach.irqcleanup(); - } - - if (sound_buffers) { - for (i = 0; i < numBufs; i++) - sound.mach.dma_free(sound_buffers[i], bufSize << 10); - kfree(sound_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); } #endif /* MODULE */ diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c new file mode 100644 index 000000000..5a9b4ec9e --- /dev/null +++ b/drivers/sound/es1370.c @@ -0,0 +1,2375 @@ +/*****************************************************************************/ + +/* + * es1370.c -- Ensoniq ES1370/Ashai Kasei AK4531 audio driver. + * + * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * joystick if 1 enables the joystick interface on the card; but it still + * needs a separate joystick driver (presumably PC standard, although + * the chip doc doesn't say anything and it looks slightly fishy from + * the PCI standpoint...) + * lineout if 1 the LINE jack is used as an output instead of an input. + * LINE then contains the unmixed dsp output. This can be used + * to make the card a four channel one: use dsp to output two + * channels to LINE and dac to output the other two channels to + * SPKR. Set the mixer to only output synth to SPKR. + * micz it looks like this changes the MIC input impedance. I don't know + * any detail though. + * + * Note: sync mode is not yet supported (i.e. running dsp and dac from the same + * clock source) + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but output only, + * only 5512, 11025, 22050 and 44100 samples/s, + * outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 26.03.98 0.1 Initial release + * 31.03.98 0.2 Fix bug in GETOSPACE + * 04.04.98 0.3 Make it work (again) under 2.0.33 + * Fix mixer write operation not returning the actual + * settings + * 05.04.98 0.4 First attempt at using the new PCI stuff + * 29.04.98 0.5 Fix hang when ^C is pressed on amp + * 07.05.98 0.6 Don't double lock around stop_*() in *_release() + * 10.05.98 0.7 First stab at a simple midi interface (no bells&whistles) + * 14.05.98 0.8 Don't allow excessive interrupt rates + * 08.06.98 0.9 First release using Alan Cox' soundcore instead of + * miscdevice + * 05.07.98 0.10 Fixed the driver to correctly maintin OSS style volume + * settings (not sure if this should be standard) + * Fixed many references: f_flags should be f_mode + * -- Gerald Britton <gbritton@mit.edu> + * + * some important things missing in Ensoniq documentation: + * + * Experimental PCLKDIV results: play the same waveforms on both DAC1 and DAC2 + * and vary PCLKDIV to obtain zero beat. + * 5512sps: 254 + * 44100sps: 30 + * seems to be fs = 1411200/(PCLKDIV+2) + * + * should find out when curr_sample_ct is cleared and + * where exactly the CCB fetches data + * + * The card uses a 22.5792 MHz crystal. + * The LINEIN jack may be converted to an AOUT jack by + * setting pin 47 (XCTL0) of the ES1370 to high. + * Pin 48 (XCTL1) of the ES1370 presumably changes the input impedance of the + * MIC jack. + * + */ + +/*****************************************************************************/ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/modversions.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/sound.h> +#include <linux/malloc.h> +#include <linux/soundcard.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <asm/spinlock.h> +#include <asm/uaccess.h> +#include <asm/hardirq.h> + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#endif + +#define ES1370_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370) + +#define ES1370_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_STATUS 0x04 +#define ES1370_REG_UART_DATA 0x08 +#define ES1370_REG_UART_STATUS 0x09 +#define ES1370_REG_UART_CONTROL 0x09 +#define ES1370_REG_UART_TEST 0x0a +#define ES1370_REG_MEMPAGE 0x0c +#define ES1370_REG_CODEC 0x10 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1370_REG_DAC1_SCOUNT 0x24 +#define ES1370_REG_DAC2_SCOUNT 0x28 +#define ES1370_REG_ADC_SCOUNT 0x2c + +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 + +#define ES1370_FMT_U8_MONO 0 +#define ES1370_FMT_U8_STEREO 1 +#define ES1370_FMT_S16_MONO 2 +#define ES1370_FMT_S16_STEREO 3 +#define ES1370_FMT_STEREO 1 +#define ES1370_FMT_S16 2 +#define ES1370_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; + +#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) +#define DAC2_DIVTOSR(x) (1411200/((x)+2)) + +#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ +#define CTRL_XCTL1 0x40000000 /* ? mic impedance */ +#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ +#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ +#define CTRL_SH_PCLKDIV 16 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ +#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ +#define CTRL_SH_WTSRSEL 12 +#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ +#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ +#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ +#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ +#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ +#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 5 +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* misc stuff */ + +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define SND_DEV_DSP16 5 + +/* --------------------------------------------------------------------- */ + +struct es1370_state { + /* magic */ + unsigned int magic; + + /* we keep sb cards in a linked list */ + struct es1370_state *next; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned int io, irq; + + /* mixer registers; there is no HW readback */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + struct wait_queue *open_wait; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + struct wait_queue *wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + struct wait_queue *iwait; + struct wait_queue *owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; +}; + +/* --------------------------------------------------------------------- */ + +struct es1370_state *devs = NULL; + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data) +{ + unsigned long tmo = jiffies + HZ/10; + + do { + if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) { + outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC); + return; + } + schedule(); + } while ((signed)(tmo-jiffies) > 0); + printk(KERN_ERR "es1370: write to codec register timeout\n"); +} + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac1(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac2(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER 8 +#define DMABUF_MINORDER 1 + + +extern inline void dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + unsigned long map, mapend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + } + fmt &= ES1370_FMT_MASK; + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(virt_to_bus(db->rawbuf), s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->ready = 1; + return 0; +} + +extern inline int prog_dmabuf_adc(struct es1370_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR); +} + +extern inline int prog_dmabuf_dac2(struct es1370_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR); +} + +extern inline int prog_dmabuf_dac1(struct es1370_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], + (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR); +} + +extern inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +extern inline 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; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1370_update_ptr(struct es1370_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count < s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count < s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1370_handle_midi(struct es1370_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1370_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL); +} + +static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1370_state *s = (struct es1370_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1370_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + es1370_update_ptr(s); + es1370_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1370_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static const struct { + unsigned volidx:4; + unsigned left:4; + unsigned right:4; + unsigned stereo:1; + unsigned recmask:13; + unsigned avail:1; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, /* master */ + [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, /* voice */ + [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, /* FM */ + [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, /* CD */ + [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, /* Line */ + [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, /* AUX */ + [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 1, 0x0100, 1 }, /* Mono1 */ + [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 1, 0x0200, 1 }, /* Mono2 */ + [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 1, 0x0001, 1 }, /* Mic */ + [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 1, 0x0000, 1 } /* mono out */ +}; + +static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) +{ + int i, val, j; + unsigned char l, r, rl, rr, sr, sl; + + VALIDATE_STATE(s); + + if (cmd == SOUND_MIXER_PRIVATE1) { + get_user_ret(val, (int *)arg, -EFAULT); + if (val != -1) { + s->mix.micpreamp = !!val; + wrcodec(s, 0x19, s->mix.micpreamp); + } + return put_user(s->mix.micpreamp, (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", 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; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", 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' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(s->mix.recsrc, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].avail) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].recmask) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].stereo) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].recmask) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].recmask; + } + s->mix.recsrc = val; + wrcodec(s, 0x12, j & 0xd5); + wrcodec(s, 0x13, j & 0xaa); + wrcodec(s, 0x14, (j >> 8) & 0x17); + wrcodec(s, 0x15, (j >> 8) & 0x0f); + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc30; + wrcodec(s, 0x10, i); + wrcodec(s, 0x11, i >> 8); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + l = val & 0xff; + if (l > 100) + l = 100; + sl = sr = l; + if (mixtable[i].stereo) { + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + sr = r; + if (l < 10) { + rl = 0x80; + l = 0; + } else { + rl = 15 - ((l - 10) / 6); + l = (15 - rl) * 6 + 10; + } + if (r < 10) { + rr = 0x80; + r = 0; + } else { + rr = 15 - ((r - 10) / 6); + r = (15 - rr) * 6 + 10; + } + wrcodec(s, mixtable[i].right, rr); + } else { + if (mixtable[i].left == 15) { + if (l < 2) { + rr = rl = 0x80; + r = l = 0; + } else { + rl = 7 - ((l - 2) / 14); + r = l = (7 - rl) * 14 + 2; + } + } else { + if (l < 10) { + rl = 0x80; + r = l = 0; + } else { + rl = 15 - ((l - 10) / 6); + r = l = (15 - rl) * 6 + 10; + } + } + } + wrcodec(s, mixtable[i].left, rl); + s->mix.vol[mixtable[i].volidx] = ((unsigned int)sr << 8) | sl; + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static loff_t es1370_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int es1370_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + +static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations es1370_mixer_fops = { + &es1370_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &es1370_ioctl_mixdev, + NULL, /* mmap */ + &es1370_open_mixdev, + &es1370_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1370_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1370: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1370_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1370: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + 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; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + return ret; +} + +static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac2.mapped) + return -ENXIO; + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac2(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac2.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac2(s); + } + return ret; +} + +static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac2.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac2.dmasize > s->dma_dac2.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) + return ret; + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + vma->vm_file = file; + file->f_count++; + return 0; +} + +static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(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: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; + if (val < 4000) + val = 4000; + if (val > 50000) + val = 50000; + stop_adc(s); + stop_dac2(s); + s->dma_adc.ready = s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -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_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + start_dac2(s); + } else + stop_dac2(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + 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->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + 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: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + val = s->dma_dac2.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; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + 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)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_READ|FMODE_WRITE))) + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + dealloc_dmabuf(&s->dma_dac2); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1370_audio_fops = { + &es1370_llseek, + &es1370_read, + &es1370_write, + NULL, /* readdir */ + &es1370_poll, + &es1370_ioctl, + &es1370_mmap, + &es1370_open, + &es1370_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac1(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac1.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac1(s); + } + return ret; +} + +static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac1.dmasize > s->dma_dac1.count) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + if ((ret = prog_dmabuf_dac1(s)) != 0) + return ret; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + s->dma_dac1.mapped = 1; + vma->vm_file = file; + file->f_count++; + return 0; +} + +static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + unsigned ctrl; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + for (ctrl = 0; ctrl <= 2; ctrl++) + if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2) + break; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (s->dma_dac1.mapped) + return -EINVAL; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + start_dac1(s); + } else + stop_dac1(s); + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + val = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open_dac(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_dac ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_release_dac(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(&s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1370_dac_fops = { + &es1370_llseek, + NULL, /* read */ + &es1370_write_dac, + NULL, /* readdir */ + &es1370_poll_dac, + &es1370_ioctl_dac, + &es1370_mmap_dac, + &es1370_open_dac, + &es1370_release_dac, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + +static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_midi_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + struct wait_queue wait = { current, NULL }; + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + current->timeout = tmo ? jiffies + tmo : 0; + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1370: midi timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1370_midi_fops = { + &es1370_llseek, + &es1370_midi_read, + &es1370_midi_write, + NULL, /* readdir */ + &es1370_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &es1370_midi_open, + &es1370_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; +static int lineout[NR_DEVICE] = { 0, }; +static int micz[NR_DEVICE] = { 0, }; + +/* --------------------------------------------------------------------- */ + +static const 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_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_LINE3, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 } +}; + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_es1370(void)) +#endif +{ + struct es1370_state *s; + struct pci_dev *pcidev = NULL; + mm_segment_t fs; + int i, val, index = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1370: version v0.10 time " __TIME__ " " __DATE__ "\n"); + while (index < NR_DEVICE && + (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { + if (pcidev->base_address[0] == 0 || + (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->irq == 0) + continue; + if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1370: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct es1370_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac1.wait); + init_waitqueue(&s->dma_dac2.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = ES1370_MAGIC; + s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->irq = pcidev->irq; + if (check_region(s->io, ES1370_EXTENT)) { + printk(KERN_ERR "es1370: io ports %#x-%#x in use\n", s->io, s->io+ES1370_EXTENT-1); + goto err_region; + } + request_region(s->io, ES1370_EXTENT, "es1370"); + if (request_irq(s->irq, es1370_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1370", s)) { + printk(KERN_ERR "es1370: irq %u in use\n", s->irq); + goto err_irq; + } + /* initialize codec registers */ + s->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (joystick[index]) { + if (check_region(0x200, JOY_EXTENT)) + printk(KERN_ERR "es1370: io port 0x200 in use\n"); + else + s->ctrl |= CTRL_JYSTK_EN; + } + if (lineout[index]) + s->ctrl |= CTRL_XCTL0; + if (micz[index]) + s->ctrl |= CTRL_XCTL1; + s->sctrl = 0; + printk(KERN_INFO "es1370: found adapter at io %#06x irq %u\n" + KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n", + s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off", + (s->ctrl & CTRL_XCTL0) ? "out" : "in", + (s->ctrl & CTRL_XCTL1) ? "1" : "0"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops)) < 0) + goto err_dev3; + if ((s->dev_midi = register_sound_midi(&es1370_midi_fops)) < 0) + goto err_dev4; + if (s->ctrl & CTRL_JYSTK_EN) + request_region(0x200, JOY_EXTENT, "es1370"); + /* initialize the chips */ + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + wrcodec(s, 0x16, 3); /* no RST, PD */ + wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ + wrcodec(s, 0x18, 0); /* recording source is mixer */ + wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + index++; + continue; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1370: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1370_EXTENT); + err_region: + kfree_s(s, sizeof(struct es1370_state)); + } + if (!devs) + return -ENODEV; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micz, "changes (??) the microphone impedance"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); + +void cleanup_module(void) +{ + struct es1370_state *s; + + while ((s = devs)) { + devs = devs->next; + outl(CTRL_SERR_DIS, s->io+ES1370_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, ES1370_EXTENT); + if (s->ctrl & CTRL_JYSTK_EN) + release_region(0x200, JOY_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree_s(s, sizeof(struct es1370_state)); + } + printk(KERN_INFO "es1370: unloading\n"); +} + +#endif /* MODULE */ diff --git a/drivers/sound/es1371.c b/drivers/sound/es1371.c new file mode 100644 index 000000000..8fcc03541 --- /dev/null +++ b/drivers/sound/es1371.c @@ -0,0 +1,2852 @@ +/*****************************************************************************/ + +/* + * es1371.c -- Creative Ensoniq ES1371. + * + * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to Ensoniq + * + * + * Module command line parameters: + * joystick if 1 enables the joystick interface on the card; but it still + * needs a separate joystick driver (presumably PC standard, although + * the chip doc doesn't say anything and it looks slightly fishy from + * the PCI standpoint...) + * + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 04.06.98 0.1 Initial release + * Mixer stuff should be overhauled; especially optional AC97 mixer bits + * should be detected. This results in strange behaviour of some mixer + * settings, like master volume and mic. + * 08.06.98 0.2 First release using Alan Cox' soundcore instead of miscdevice + * + * + * + */ + +/*****************************************************************************/ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/modversions.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/sound.h> +#include <linux/malloc.h> +#include <linux/soundcard.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <asm/spinlock.h> +#include <asm/uaccess.h> +#include <asm/hardirq.h> + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 +#endif + +#define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) + +#define ES1371_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1371_REG_CONTROL 0x00 +#define ES1371_REG_STATUS 0x04 +#define ES1371_REG_UART_DATA 0x08 +#define ES1371_REG_UART_STATUS 0x09 +#define ES1371_REG_UART_CONTROL 0x09 +#define ES1371_REG_UART_TEST 0x0a +#define ES1371_REG_MEMPAGE 0x0c +#define ES1371_REG_SRCONV 0x10 +#define ES1371_REG_CODEC 0x14 +#define ES1371_REG_LEGACY 0x18 +#define ES1371_REG_SERIAL_CONTROL 0x20 +#define ES1371_REG_DAC1_SCOUNT 0x24 +#define ES1371_REG_DAC2_SCOUNT 0x28 +#define ES1371_REG_ADC_SCOUNT 0x2c + +#define ES1371_REG_DAC1_FRAMEADR 0xc30 +#define ES1371_REG_DAC1_FRAMECNT 0xc34 +#define ES1371_REG_DAC2_FRAMEADR 0xc38 +#define ES1371_REG_DAC2_FRAMECNT 0xc3c +#define ES1371_REG_ADC_FRAMEADR 0xd30 +#define ES1371_REG_ADC_FRAMECNT 0xd34 + +#define ES1371_FMT_U8_MONO 0 +#define ES1371_FMT_U8_STEREO 1 +#define ES1371_FMT_S16_MONO 2 +#define ES1371_FMT_S16_STEREO 3 +#define ES1371_FMT_STEREO 1 +#define ES1371_FMT_S16 2 +#define ES1371_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define CTRL_JOY_SHIFT 24 +#define CTRL_JOY_MASK 3 +#define CTRL_JOY_200 0x00000000 /* joystick base address */ +#define CTRL_JOY_208 0x01000000 +#define CTRL_JOY_210 0x02000000 +#define CTRL_JOY_218 0x03000000 +#define CTRL_GPIO_IN0 0x00100000 /* general purpose inputs/outputs */ +#define CTRL_GPIO_IN1 0x00200000 +#define CTRL_GPIO_IN2 0x00400000 +#define CTRL_GPIO_IN3 0x00800000 +#define CTRL_GPIO_OUT0 0x00010000 +#define CTRL_GPIO_OUT1 0x00020000 +#define CTRL_GPIO_OUT2 0x00040000 +#define CTRL_GPIO_OUT3 0x00080000 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_SYNCRES 0x00004000 /* AC97 warm reset */ +#define CTRL_ADCSTOP 0x00002000 /* stop ADC transfers */ +#define CTRL_PWR_INTRM 0x00001000 /* 1 = power level ints enabled */ +#define CTRL_M_CB 0x00000800 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_PDLEV0 0x00000000 /* power down level */ +#define CTRL_PDLEV1 0x00000100 +#define CTRL_PDLEV2 0x00000200 +#define CTRL_PDLEV3 0x00000300 +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port */ +#define CTRL_XTALCLKDIS 0x00000002 /* 1 = disable crystal clock input */ +#define CTRL_PCICLKDIS 0x00000001 /* 1 = disable PCI clock distribution */ + + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_SYNC_ERR 0x00000100 /* 1 = codec sync error */ +#define STAT_VC 0x000000c0 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 6 +#define STAT_MPWR 0x00000020 /* power level interrupt */ +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +/* sample rate converter */ +#define SRC_RAMADDR_MASK 0xfe000000 +#define SRC_RAMADDR_SHIFT 25 +#define SRC_WE 0x01000000 /* read/write control for SRC RAM */ +#define SRC_BUSY 0x00800000 /* SRC busy */ +#define SRC_DIS 0x00400000 /* 1 = disable SRC */ +#define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */ +#define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */ +#define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */ +#define SRC_RAMDATA_MASK 0x0000ffff +#define SRC_RAMDATA_SHIFT 0 + +#define SRCREG_ADC 0x78 +#define SRCREG_DAC1 0x70 +#define SRCREG_DAC2 0x74 +#define SRCREG_VOL_ADC 0x6c +#define SRCREG_VOL_DAC1 0x7c +#define SRCREG_VOL_DAC2 0x7e + +#define SRCREG_TRUNC_N 0x00 +#define SRCREG_INT_REGS 0x01 +#define SRCREG_ACCUM_FRAC 0x02 +#define SRCREG_VFREQ_FRAC 0x03 + +#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_PIADD_MASK 0x007f0000 +#define CODEC_PIADD_SHIFT 16 +#define CODEC_PIDAT_MASK 0x0000ffff +#define CODEC_PIDAT_SHIFT 0 + +#define CODEC_RDY 0x80000000 /* AC97 read data valid */ +#define CODEC_WIP 0x40000000 /* AC97 write in progress */ +#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_POADD_MASK 0x007f0000 +#define CODEC_POADD_SHIFT 16 +#define CODEC_PODAT_MASK 0x0000ffff +#define CODEC_PODAT_SHIFT 0 + + +#define LEGACY_JFAST 0x80000000 /* fast joystick timing */ +#define LEGACY_FIRQ 0x01000000 /* force IRQ */ + +#define SCTRL_DACTEST 0x00400000 /* 1 = DAC test, test vector generation purposes */ +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* codec constants */ + +#define CODEC_ID_DEDICATEDMIC 0x001 +#define CODEC_ID_MODEMCODEC 0x002 +#define CODEC_ID_BASSTREBLE 0x004 +#define CODEC_ID_SIMULATEDSTEREO 0x008 +#define CODEC_ID_HEADPHONEOUT 0x010 +#define CODEC_ID_LOUDNESS 0x020 +#define CODEC_ID_18BITDAC 0x040 +#define CODEC_ID_20BITDAC 0x080 +#define CODEC_ID_18BITADC 0x100 +#define CODEC_ID_20BITADC 0x200 + +#define CODEC_ID_SESHIFT 10 +#define CODEC_ID_SEMASK 0x1f + + +/* misc stuff */ + +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define SND_DEV_DSP16 5 + +/* --------------------------------------------------------------------- */ + +static const char *stereo_enhancement[] __initdata = +{ + "no 3D stereo enhancement", + "Analog Devices Phat Stereo", + "Creative Stereo Enhancement", + "National Semiconductor 3D Stereo Enhancement", + "YAMAHA Ymersion", + "BBE 3D Stereo Enhancement", + "Crystal Semiconductor 3D Stereo Enhancement", + "Qsound QXpander", + "Spatializer 3D Stereo Enhancement", + "SRS 3D Stereo Enhancement", + "Platform Technologies 3D Stereo Enhancement", + "AKM 3D Audio", + "Aureal Stereo Enhancement", + "AZTECH 3D Enhancement", + "Binaura 3D Audio Enhancement", + "ESS Technology Stereo Enhancement", + "Harman International VMAx", + "NVidea 3D Stereo Enhancement", + "Philips Incredible Sound", + "Texas Instruments 3D Stereo Enhancement", + "VLSI Technology 3D Stereo Enhancement" +}; + +/* --------------------------------------------------------------------- */ + +struct es1371_state { + /* magic */ + unsigned int magic; + + /* we keep sb cards in a linked list */ + struct es1371_state *next; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned int io, irq; + + /* mixer registers; there is no HW readback */ + struct { + unsigned short codec_id; + unsigned int modcnt; + } mix; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + unsigned dac1rate, dac2rate, adcrate; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + struct wait_queue *open_wait; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + struct wait_queue *wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + struct wait_queue *iwait; + struct wait_queue *owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; +}; + +/* --------------------------------------------------------------------- */ + +struct es1371_state *devs = NULL; + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +extern __inline__ unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* --------------------------------------------------------------------- */ + +static unsigned wait_src_ready(struct es1371_state *s) +{ + unsigned int t, r; + + for (t = 0; t < 1000; t++) { + if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY)) + return r; + udelay(1); + } + printk(KERN_DEBUG "es1371: sample rate converter timeout r = 0x%08x\n", r); + return r; +} + +static unsigned src_read(struct es1371_state *s, unsigned reg) +{ + unsigned int r; + + r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); + r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; + outl(r, s->io + ES1371_REG_SRCONV); + return (wait_src_ready(s) & SRC_RAMDATA_MASK) >> SRC_RAMDATA_SHIFT; +} + + +static void src_write(struct es1371_state *s, unsigned reg, unsigned data) +{ + unsigned int r; + + r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); + r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; + r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK; + outl(r | SRC_WE, s->io + ES1371_REG_SRCONV); +} + +/* --------------------------------------------------------------------- */ + +/* most of the following here is black magic */ + +static void set_adc_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int n, truncm, freq; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + s->adcrate = (48000UL << 15) / (freq / n); + spin_lock_irqsave(&s->lock, flags); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + src_write(s, SRCREG_ADC+SRCREG_INT_REGS, + (src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff); + src_write(s, SRCREG_VOL_ADC, n << 8); + src_write(s, SRCREG_VOL_ADC+1, n << 8); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac1_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = (rate << 15) / 3000; + s->dac1rate = (freq * 3000) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac2_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = (rate << 15) / 3000; + s->dac2rate = (freq * 3000) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct es1371_state *s, unsigned addr, unsigned data) +{ + unsigned long flags; + unsigned t, x; + + for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + spin_lock_irqsave(&s->lock, flags); + /* save the current state for later */ + x = inl(s->io+ES1371_REG_SRCONV); + /* enable SRC state data in SRC mux */ + outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + s->io+ES1371_REG_SRCONV); + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < 0x1000; t++) + if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) + break; + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | + ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC); + /* restore SRC reg */ + wait_src_ready(s); + outl(x, s->io+ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static unsigned rdcodec(struct es1371_state *s, unsigned addr) +{ + unsigned long flags; + unsigned t, x; + + for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + spin_lock_irqsave(&s->lock, flags); + /* save the current state for later */ + x = inl(s->io+ES1371_REG_SRCONV); + /* enable SRC state data in SRC mux */ + outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + s->io+ES1371_REG_SRCONV); + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < 0x1000; t++) + if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) + break; + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC); + /* restore SRC reg */ + wait_src_ready(s); + outl(x, s->io+ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < 0x1000; t++) + if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) + break; + return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); +} + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac1(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac2(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER 8 +#define DMABUF_MINORDER 1 + + +extern inline void dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + unsigned long map, mapend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + } + fmt &= ES1371_FMT_MASK; + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + outl(virt_to_bus(db->rawbuf), s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->ready = 1; + return 0; +} + +extern inline int prog_dmabuf_adc(struct es1371_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK, + ES1371_REG_ADC_FRAMEADR); +} + +extern inline int prog_dmabuf_dac2(struct es1371_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC2_FRAMEADR); +} + +extern inline int prog_dmabuf_dac1(struct es1371_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC1_FRAMEADR); +} + +extern inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +extern inline 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; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1371_update_ptr(struct es1371_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count < s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count < s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1371_handle_midi(struct es1371_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1371_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL); +} + +static void es1371_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1371_state *s = (struct es1371_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1371_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + es1371_update_ptr(s); + es1371_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "es1371: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1371_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define AC97_PESSIMISTIC + +/* + * this define causes the driver to assume that all optional + * AC97 bits are missing. This is what Ensoniq does too in their + * Windows driver. Maybe we should one day autoprobe for these + * bits. But anyway I have to see an AC97 codec that implements + * one of those optional (volume) bits. + */ + +static const unsigned int recsrc[8] = +{ + SOUND_MASK_MIC, + SOUND_MASK_CD, + SOUND_MASK_VIDEO, + SOUND_MASK_LINE1, + SOUND_MASK_LINE, + SOUND_MASK_VOLUME, + SOUND_MASK_PHONEOUT, + SOUND_MASK_PHONEIN +}; + +static const unsigned char volreg[] = +{ + /* 5 bit stereo */ + [SOUND_MIXER_LINE] = 0x10, + [SOUND_MIXER_CD] = 0x12, + [SOUND_MIXER_VIDEO] = 0x14, + [SOUND_MIXER_LINE1] = 0x16, + [SOUND_MIXER_PCM] = 0x18, + /* 6 bit stereo */ + [SOUND_MIXER_VOLUME] = 0x02, + [SOUND_MIXER_PHONEOUT] = 0x04, + /* 6 bit mono */ + [SOUND_MIXER_OGAIN] = 0x06, + [SOUND_MIXER_PHONEIN] = 0x0c, + /* 4 bit mono but shifted by 1 */ + [SOUND_MIXER_SPEAKER] = 0x08, + /* 6 bit mono + preamp */ + [SOUND_MIXER_MIC] = 0x0e, + /* 4 bit stereo */ + [SOUND_MIXER_RECLEV] = 0x1c, + /* 4 bit mono */ + [SOUND_MIXER_IGAIN] = 0x1e +}; + +#define swab(x) ((((x) >> 8) & 0xff) | (((x) << 8) & 0xff00)) + +static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) +{ + int j; + + switch (ch) { + case SOUND_MIXER_MIC: + j = rdcodec(s, 0x0e); + if (j & 0x8000) + return put_user(0, (int *)arg); +#ifdef AC97_PESSIMISTIC + return put_user(0x4949 - 0x202 * (j & 0x1f) + ((j & 0x40) ? 0x1b1b : 0), (int *)arg); +#else /* AC97_PESSIMISTIC */ + return put_user(0x5757 - 0x101 * ((j & 0x3f) * 5 / 4) + ((j & 0x40) ? 0x0d0d : 0), (int *)arg); +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_OGAIN: + case SOUND_MIXER_PHONEIN: + j = rdcodec(s, volreg[ch]); + if (j & 0x8000) + return put_user(0, (int *)arg); +#ifdef AC97_PESSIMISTIC + return put_user(0x6464 - 0x303 * (j & 0x1f), (int *)arg); +#else /* AC97_PESSIMISTIC */ + return put_user((0x6464 - 0x303 * (j & 0x3f) / 2) & 0x7f7f, (int *)arg); +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_PHONEOUT: + if (!(s->mix.codec_id & CODEC_ID_HEADPHONEOUT)) + return -EINVAL; + /* fall through */ + case SOUND_MIXER_VOLUME: + j = rdcodec(s, volreg[ch]); + if (j & 0x8000) + return put_user(0, (int *)arg); +#ifdef AC97_PESSIMISTIC + return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); +#else /* AC97_PESSIMISTIC */ + return put_user((0x6464 - (swab(j) & 0x3f3f) * 3 / 2) & 0x7f7f, (int *)arg); +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_SPEAKER: + j = rdcodec(s, 0x0a); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg); + + case SOUND_MIXER_LINE: + case SOUND_MIXER_CD: + case SOUND_MIXER_VIDEO: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_PCM: + j = rdcodec(s, volreg[ch]); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); + + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + if (!(s->mix.codec_id & CODEC_ID_BASSTREBLE)) + return -EINVAL; + j = rdcodec(s, 0x08); + if (ch == SOUND_MIXER_BASS) + j >>= 8; + return put_user((((j & 15) * 100) / 15) * 0x101, (int *)arg); + + /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ + case SOUND_MIXER_RECLEV: + j = rdcodec(s, 0x1c); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user((swab(j) & 0xf0f) * 6 + 0xa0a, (int *)arg); + + case SOUND_MIXER_IGAIN: + if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) + return -EINVAL; + j = rdcodec(s, 0x1e); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user((j & 0xf) * 0x606 + 0xa0a, (int *)arg); + + default: + return -EINVAL; + } +} + +static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) +{ + int i; + unsigned l1, r1; + + l1 = val & 0xff; + r1 = (val >> 8) & 0xff; + if (l1 > 100) + l1 = 100; + if (r1 > 100) + r1 = 100; + switch (ch) { + case SOUND_MIXER_LINE: + case SOUND_MIXER_CD: + case SOUND_MIXER_VIDEO: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_PCM: + if (l1 < 7 && r1 < 7) { + wrcodec(s, volreg[ch], 0x8000); + return 0; + } + if (l1 < 7) + l1 = 7; + if (r1 < 7) + r1 = 7; + wrcodec(s, volreg[ch], (((100 - l1) / 3) << 8) | ((100 - r1) / 3)); + return 0; + + case SOUND_MIXER_PHONEOUT: + if (!(s->mix.codec_id & CODEC_ID_HEADPHONEOUT)) + return -EINVAL; + /* fall through */ + case SOUND_MIXER_VOLUME: +#ifdef AC97_PESSIMISTIC + if (l1 < 7 && r1 < 7) { + wrcodec(s, volreg[ch], 0x8000); + return 0; + } + if (l1 < 7) + l1 = 7; + if (r1 < 7) + r1 = 7; + wrcodec(s, volreg[ch], (((100 - l1) / 3) << 8) | ((100 - r1) / 3)); + return 0; +#else /* AC97_PESSIMISTIC */ + if (l1 < 4 && r1 < 4) { + wrcodec(s, volreg[ch], 0x8000); + return 0; + } + if (l1 < 4) + l1 = 4; + if (r1 < 4) + r1 = 4; + wrcodec(s, volreg[ch], ((2 * (100 - l1) / 3) << 8) | (2 * (100 - r1) / 3)); + return 0; +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_OGAIN: + case SOUND_MIXER_PHONEIN: +#ifdef AC97_PESSIMISTIC + wrcodec(s, volreg[ch], (l1 < 7) ? 0x8000 : (100 - l1) / 3); + return 0; +#else /* AC97_PESSIMISTIC */ + wrcodec(s, volreg[ch], (l1 < 4) ? 0x8000 : (2 * (100 - l1) / 3)); + return 0; +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_SPEAKER: + wrcodec(s, 0x0a, (l1 < 10) ? 0x8000 : ((100 - l1) / 6) << 1); + return 0; + + case SOUND_MIXER_MIC: +#ifdef AC97_PESSIMISTIC + if (l1 < 11) { + wrcodec(s, 0x0e, 0x8000); + return 0; + } + i = 0; + if (l1 >= 27) { + l1 -= 27; + i = 0x40; + } + if (l1 < 11) + l1 = 11; + wrcodec(s, 0x0e, ((73 - l1) / 2) | i); + return 0; +#else /* AC97_PESSIMISTIC */ + if (l1 < 9) { + wrcodec(s, 0x0e, 0x8000); + return 0; + } + i = 0; + if (l1 >= 13) { + l1 -= 13; + i = 0x40; + } + if (l1 < 9) + l1 = 9; + wrcodec(s, 0x0e, (((87 - l1) * 4) / 5) | i); + return 0; +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_BASS: + val = ((l1 * 15) / 100) & 0xf; + wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0x00ff) | (val << 8)); + return 0; + + case SOUND_MIXER_TREBLE: + val = ((l1 * 15) / 100) & 0xf; + wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0xff00) | val); + return 0; + + /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ + case SOUND_MIXER_RECLEV: + if (l1 < 10 || r1 < 10) { + wrcodec(s, 0x1c, 0x8000); + return 0; + } + if (l1 < 10) + l1 = 10; + if (r1 < 10) + r1 = 10; + wrcodec(s, 0x1c, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6)); + return 0; + + case SOUND_MIXER_IGAIN: + if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) + return -EINVAL; + wrcodec(s, 0x1e, (l1 < 10) ? 0x8000 : ((l1 - 10) / 6) & 0xf); + return 0; + + default: + return -EINVAL; + } +} + +static int mixer_ioctl(struct es1371_state *s, unsigned int cmd, unsigned long arg) +{ + int i, val; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + if (!(s->mix.codec_id & (CODEC_ID_SEMASK << CODEC_ID_SESHIFT))) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val & 1) + wrcodec(s, 0x22, ((val << 3) & 0xf00) | ((val >> 1) & 0xf)); + val = rdcodec(s, 0x22); + return put_user(((val & 0xf) << 1) | ((val & 0xf00) >> 3), (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1371", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1371", 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; + strncpy(info.id, "ES1371", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1371", 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' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(recsrc[rdcodec(s, 0x1a) & 7], (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO | + SOUND_MASK_LINE1 | SOUND_MASK_PCM | SOUND_MASK_VOLUME | + SOUND_MASK_OGAIN | SOUND_MASK_PHONEIN | SOUND_MASK_SPEAKER | + SOUND_MASK_MIC | SOUND_MASK_RECLEV | + ((s->mix.codec_id & CODEC_ID_BASSTREBLE) ? (SOUND_MASK_BASS | SOUND_MASK_TREBLE) : 0) | + ((s->mix.codec_id & CODEC_ID_HEADPHONEOUT) ? SOUND_MASK_PHONEOUT : 0) | + ((s->mix.codec_id & CODEC_ID_DEDICATEDMIC) ? SOUND_MASK_IGAIN : 0), (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + return put_user(SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VIDEO | SOUND_MASK_LINE1 | + SOUND_MASK_LINE | SOUND_MASK_VOLUME | SOUND_MASK_PHONEOUT | + SOUND_MASK_PHONEIN, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO | + SOUND_MASK_LINE1 | SOUND_MASK_PCM | SOUND_MASK_VOLUME | + SOUND_MASK_PHONEOUT | 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) + return -EINVAL; + return mixer_rdch(s, i, (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s);*/ + else if (i > 1) + val &= ~recsrc[rdcodec(s, 0x1a) & 7]; + for (i = 0; i < 8; i++) { + if (val & recsrc[i]) { + wrcodec(s, 0x1a, 0x101 * i); + return 0; + } + } + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (mixer_wrch(s, i, val)) + return -EINVAL; + return mixer_rdch(s, i, (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static loff_t es1371_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int es1371_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + +static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct es1371_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations es1371_mixer_fops = { + &es1371_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &es1371_ioctl_mixdev, + NULL, /* mmap */ + &es1371_open_mixdev, + &es1371_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1371_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->dac1rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1371: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1371_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->dac2rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1371: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + 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; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + return ret; +} + +static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac2.mapped) + return -ENXIO; + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac2(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac2.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac2(s); + } + return ret; +} + +static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->dma_dac2.wait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (file->f_flags & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_flags & FMODE_WRITE) { + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac2.dmasize > s->dma_dac2.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) + return ret; + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + vma->vm_file = file; + file->f_count++; + return 0; +} + +static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(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: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + set_dac2_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -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_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + start_dac2(s); + } else + stop_dac2(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + 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->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + 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: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + val = s->dma_dac2.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; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + 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)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1371_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + set_dac2_rate(s, 8000); + } + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_flags & FMODE_WRITE) { + stop_dac2(s); + dealloc_dmabuf(&s->dma_dac2); + } + if (file->f_flags & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1371_audio_fops = { + &es1371_llseek, + &es1371_read, + &es1371_write, + NULL, /* readdir */ + &es1371_poll, + &es1371_ioctl, + &es1371_mmap, + &es1371_open, + &es1371_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac1(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac1.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac1(s); + } + return ret; +} + +static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac1.dmasize > s->dma_dac1.count) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + if ((ret = prog_dmabuf_dac1(s)) != 0) + return ret; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + s->dma_dac1.mapped = 1; + vma->vm_file = file; + file->f_count++; + return 0; +} + +static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + set_dac1_rate(s, val); + } + return put_user(s->dac1rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + start_dac1(s); + } else + stop_dac1(s); + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + val = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1371_open_dac(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_dac ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + set_dac1_rate(s, 8000); + spin_lock_irqsave(&s->lock, flags); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_release_dac(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(&s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1371_dac_fops = { + &es1371_llseek, + NULL, /* read */ + &es1371_write_dac, + NULL, /* readdir */ + &es1371_poll_dac, + &es1371_ioctl_dac, + &es1371_mmap_dac, + &es1371_open_dac, + &es1371_release_dac, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + +static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_midi_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + struct wait_queue wait = { current, NULL }; + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + current->timeout = tmo ? jiffies + tmo : 0; + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1371: midi timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1371_midi_fops = { + &es1371_llseek, + &es1371_midi_read, + &es1371_midi_write, + NULL, /* readdir */ + &es1371_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &es1371_midi_open, + &es1371_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; + +/* --------------------------------------------------------------------- */ + +static const struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_IGAIN, 0x4040 } +}; + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_es1371(void)) +#endif +{ + struct es1371_state *s; + struct pci_dev *pcidev = NULL; + mm_segment_t fs; + int i, val, val2, index = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1371: version v0.2 time " __TIME__ " " __DATE__ "\n"); + while (index < NR_DEVICE && + (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { + if (pcidev->base_address[0] == 0 || + (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->irq == 0) + continue; + if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1371: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct es1371_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac1.wait); + init_waitqueue(&s->dma_dac2.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = ES1371_MAGIC; + s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->irq = pcidev->irq; + if (check_region(s->io, ES1371_EXTENT)) { + printk(KERN_ERR "es1371: io ports %#x-%#x in use\n", s->io, s->io+ES1371_EXTENT-1); + goto err_region; + } + request_region(s->io, ES1371_EXTENT, "es1371"); + if (request_irq(s->irq, es1371_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1371", s)) { + printk(KERN_ERR "es1371: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "es1371: found adapter at io %#06x irq %u\n" + KERN_INFO "es1371: features: joystick 0x%x\n", s->io, s->irq, joystick[index]); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops)) < 0) + goto err_dev3; + if ((s->dev_midi = register_sound_midi(&es1371_midi_fops)) < 0) + goto err_dev4; + /* initialize codec registers */ + s->ctrl = 0; + if ((joystick[index] & ~0x18) == 0x200) { + if (check_region(joystick[index], JOY_EXTENT)) + printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]); + else { + s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + request_region(joystick[index], JOY_EXTENT, "es1371"); + } + } + s->sctrl = 0; + /* initialize the chips */ + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(0, s->io+ES1371_REG_LEGACY); + /* AC97 warm reset to start the bitclk */ + outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); + udelay(2); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + /* init the sample rate converter */ + outl(SRC_DIS, s->io + ES1371_REG_SRCONV); + for (val = 0; val < 0x80; val++) + src_write(s, val, 0); + src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_VOL_ADC, 1 << 12); + src_write(s, SRCREG_VOL_ADC+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC2, 1 << 12); + src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); + set_adc_rate(s, 22050); + set_dac1_rate(s, 22050); + set_dac2_rate(s, 22050); + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) + */ + outl(0, s->io+ES1371_REG_SRCONV); + /* codec init */ + wrcodec(s, 0x00, 0); /* reset codec */ + s->mix.codec_id = rdcodec(s, 0x00); /* get codec ID */ + val = rdcodec(s, 0x7c); + val2 = rdcodec(s, 0x7e); + printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", + (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff); + printk(KERN_INFO "es1371: codec features"); + if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC) + printk(" dedicated MIC PCM in"); + if (s->mix.codec_id & CODEC_ID_MODEMCODEC) + printk(" Modem Line Codec"); + if (s->mix.codec_id & CODEC_ID_BASSTREBLE) + printk(" Bass & Treble"); + if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO) + printk(" Simulated Stereo"); + if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT) + printk(" Headphone out"); + if (s->mix.codec_id & CODEC_ID_LOUDNESS) + printk(" Loudness"); + if (s->mix.codec_id & CODEC_ID_18BITDAC) + printk(" 18bit DAC"); + if (s->mix.codec_id & CODEC_ID_20BITDAC) + printk(" 20bit DAC"); + if (s->mix.codec_id & CODEC_ID_18BITADC) + printk(" 18bit ADC"); + if (s->mix.codec_id & CODEC_ID_20BITADC) + printk(" 20bit ADC"); + printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none"); + val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK; + printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown"); + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + index++; + continue; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1371: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1371_EXTENT); + err_region: + kfree_s(s, sizeof(struct es1371_state)); + } + if (!devs) + return -ENODEV; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micz, "changes (??) the microphone impedance"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver"); + +void cleanup_module(void) +{ + struct es1371_state *s; + + while ((s = devs)) { + devs = devs->next; + outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, ES1371_EXTENT); + if (s->ctrl & CTRL_JYSTK_EN) + release_region(((((s->ctrl >> CTRL_JOY_SHIFT) & CTRL_JOY_MASK) << 3) | 0x200), JOY_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree_s(s, sizeof(struct es1371_state)); + } + printk(KERN_INFO "es1371: unloading\n"); +} + +#endif /* MODULE */ diff --git a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c index 62d10257d..3c46d7ada 100644 --- a/drivers/sound/gus_card.c +++ b/drivers/sound/gus_card.c @@ -26,7 +26,7 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_GUS) || defined(MODULE) +#ifdef CONFIG_GUS #include "gus_hw.h" @@ -57,7 +57,7 @@ void attach_gus_card(struct address_info *hw_config) if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) if (sound_alloc_dma(hw_config->dma2, "GUS(2)")) printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2); -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI gus_midi_init(hw_config); #endif } @@ -152,13 +152,13 @@ void gusintr(int irq, void *dev_id, struct pt_regs *dummy) } if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) { -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI gus_midi_interrupt(0); #endif } if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) { -#if defined(CONFIG_SEQUENCER) || defined(CONFIG_SEQUENCER_MODULE) +#ifdef CONFIG_SEQUENCER if (gus_timer_enabled) sound_timer_interrupt(); gus_write8(0x45, 0); /* Ack IRQ */ @@ -187,7 +187,7 @@ int probe_gus_db16(struct address_info *hw_config) void attach_gus_db16(struct address_info *hw_config) { -#if defined(CONFIG_GUS) || defined(MODULE) +#ifdef CONFIG_GUS gus_pcm_volume = 100; gus_wave_volume = 90; #endif diff --git a/drivers/sound/gus_midi.c b/drivers/sound/gus_midi.c index 1055aa509..7f82d3997 100644 --- a/drivers/sound/gus_midi.c +++ b/drivers/sound/gus_midi.c @@ -17,7 +17,8 @@ #include "gus_hw.h" -#if ( defined(CONFIG_GUS) && defined(CONFIG_MIDI) ) || defined (MODULE) +#ifdef CONFIG_GUS +#ifdef CONFIG_MIDI static int midi_busy = 0, input_opened = 0; static int my_dev; @@ -266,3 +267,4 @@ void gus_midi_interrupt(int dummy) } #endif +#endif diff --git a/drivers/sound/gus_vol.c b/drivers/sound/gus_vol.c index 0e03aa685..3e5752d8f 100644 --- a/drivers/sound/gus_vol.c +++ b/drivers/sound/gus_vol.c @@ -12,7 +12,7 @@ #include <linux/config.h> #include "sound_config.h" -#if defined(CONFIG_GUS) || defined(MODULE) +#ifdef CONFIG_GUS #include "gus_linearvol.h" #define GUS_VOLUME gus_wave_volume diff --git a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c index 6dc52aef6..c15bced88 100644 --- a/drivers/sound/gus_wave.c +++ b/drivers/sound/gus_wave.c @@ -25,7 +25,7 @@ #include <linux/ultrasound.h> #include "gus_hw.h" -#if defined(CONFIG_GUS) || defined(MODULE) +#ifdef CONFIG_GUS #define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) @@ -384,7 +384,7 @@ static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + (frac << 5)); - /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */ + /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ gus_delay(); gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) @@ -499,13 +499,15 @@ static void gus_set_voice_pos(int voice, long position) { int sample_no; - if ((sample_no = sample_map[voice]) != -1) - if (position < samples[sample_no].len) + if ((sample_no = sample_map[voice]) != -1) { + if (position < samples[sample_no].len) { if (voices[voice].volume_irq_mode == VMODE_START_NOTE) voices[voice].offset_pending = position; else gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0, samples[sample_no].mode & WAVE_16_BITS); + } + } } static void gus_voice_init(int voice) @@ -3115,7 +3117,7 @@ void gus_wave_init(struct address_info *hw_config) hw_config->slots[0] = sdev; synth_devs[sdev] = &guswave_operations; sequencer_init(); -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER gus_tmr_install(gus_base + 8); #endif } @@ -3126,30 +3128,24 @@ void gus_wave_init(struct address_info *hw_config) if ((gus_mem_size > 0) & !gus_no_wave_dma) { - if ((dev = sound_alloc_audiodev()) != -1) + hw_config->slots[4] = -1; + if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "Ultrasound", + &gus_audio_driver, + sizeof(struct audio_driver), + NEEDS_RESTART | + ((!iw_mode && dma2 != dma && dma2 != -1) ? + DMA_DUPLEX : 0), + AFMT_U8 | AFMT_S16_LE, + NULL, dma, dma2)) < 0) { - hw_config->slots[4] = dev; - if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - "Ultrasound", - &gus_audio_driver, - sizeof(struct audio_driver), - NEEDS_RESTART | - ((!iw_mode && dma2 != dma && dma2 != -1) ? - DMA_DUPLEX : 0), - AFMT_U8 | AFMT_S16_LE, - NULL, - dma, - dma2)) < 0) - { - return; - } + return; + } - audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ - audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ - audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ - audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; - } else - printk(KERN_WARNING "GUS: Too many audio devices available\n"); + audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ + audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ + audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ + audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; } /* @@ -3442,7 +3438,7 @@ void guswave_dma_irq(void) } } -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER /* * Timer stuff diff --git a/drivers/sound/hex2hex.c b/drivers/sound/hex2hex.c index 4b182625b..8f6cd11b1 100644 --- a/drivers/sound/hex2hex.c +++ b/drivers/sound/hex2hex.c @@ -81,7 +81,7 @@ int main( int argc, const char * argv [] ) fprintf(stderr,"hex2hex: [-i] filename\n"); exit(1); } - varline = argv[1; + varline = argv[1]; l = loadhex(stdin, buf); printf("/*\n *\t Computer generated file. Do not edit.\n */\n"); diff --git a/drivers/sound/ics2101.c b/drivers/sound/ics2101.c index 35b82ac2c..dffb83265 100644 --- a/drivers/sound/ics2101.c +++ b/drivers/sound/ics2101.c @@ -17,7 +17,7 @@ #include "sound_config.h" -#if defined(CONFIG_GUS) || defined(MODULE) +#ifdef CONFIG_GUS #include <linux/ultrasound.h> #include "gus_hw.h" diff --git a/drivers/sound/legacy.h b/drivers/sound/legacy.h new file mode 100644 index 000000000..c9b39b1c9 --- /dev/null +++ b/drivers/sound/legacy.h @@ -0,0 +1,48 @@ +#ifndef _SOUND_LEGACY_H_ +#define _SOUND_LEGACY_H_ + +/* + * Force on additional support + */ + +#define __SGNXPRO__ +#define DESKPROXL +/* #define SM_GAMES */ +#define SM_WAVE + +/* + * Define legacy options. + */ + +#define SELECTED_SOUND_OPTIONS 0x0 + +#define HAVE_MAUI_BOOT +#define PSS_HAVE_LD +#define INCLUDE_TRIX_BOOT + +#define CONFIG_CS4232 +#define CONFIG_GUS +#define CONFIG_MAD16 +#define CONFIG_MAUI +#define CONFIG_MPU401 +#define CONFIG_MSS +#define CONFIG_OPL3SA1 +#define CONFIG_PAS +#define CONFIG_PSS +#define CONFIG_SB +#define CONFIG_SOFTOSS +#define CONFIG_SSCAPE +#define CONFIG_TRIX +#define CONFIG_VMIDI +#define CONFIG_YM3812 + +#define CONFIG_AUDIO +#define CONFIG_MIDI +#define CONFIG_SEQUENCER + +#define CONFIG_AD1848 +#define CONFIG_MPU_EMU +#define CONFIG_SBDSP +#define CONFIG_UART401 + +#endif /* _SOUND_LEGACY_H */ diff --git a/drivers/sound/local.h.master b/drivers/sound/local.h.master deleted file mode 100644 index 65d8c0259..000000000 --- a/drivers/sound/local.h.master +++ /dev/null @@ -1,118 +0,0 @@ -/* Computer generated file. Please don't edit! */ - -#include <linux/config.h> - -#define KERNEL_COMPATIBLE_CONFIG - -#define SELECTED_SOUND_OPTIONS 0x00000000 - -#if \ - defined(CONFIG_PSS) || defined(CONFIG_SSCAPE) || \ - defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \ - defined(CONFIG_PSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \ - defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE) -# define CONFIG_MPU_EMU -#endif - -#if \ - defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \ - defined(CONFIG_GUSMAX) || defined(CONFIG_MSS) || \ - defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \ - defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \ - defined(CONFIG_OPL3SA1) || \ - defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \ - defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_MSS_MODULE) || \ - defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \ - defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE) || \ - defined(CONFIG_OPL3SA1_MODULE) -# define CONFIG_AD1848 -#endif - -#if \ - defined(CONFIG_PAS) || defined(CONFIG_SB) || \ - defined(CONFIG_GUS) || defined(CONFIG_PSS) || \ - defined(CONFIG_GUS16) || defined(CONFIG_GUSMAX) || \ - defined(CONFIG_MSS) || defined(CONFIG_SSCAPE) || \ - defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \ - defined(CONFIG_CS4232) || defined(CONFIG_OPL3SA1) || \ - defined(CONFIG_SOFTOSS) || \ - defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \ - defined(CONFIG_GUS_MODULE) || defined(CONFIG_PSS_MODULE) || \ - defined(CONFIG_GUS16_MODULE) || defined(CONFIG_GUSMAX_MODULE) || \ - defined(CONFIG_MSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \ - defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \ - defined(CONFIG_CS4232_MODULE) || defined(CONFIG_OPL3SA1_MODULE) || \ - defined(CONFIG_SOFTOSS_MODULE) || defined(CONFIG_VIDC_SOUND) -# define CONFIG_AUDIO -#endif - -#if \ - defined(CONFIG_PAS) || defined(CONFIG_SB) || \ - defined(CONFIG_GUS) || defined(CONFIG_MPU401) || \ - defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \ - defined(CONFIG_GUSMAX) || defined(CONFIG_SSCAPE) || \ - defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \ - defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \ - defined(CONFIG_OPL3SA1) || defined(CONFIG_SOFTOSS) || \ - defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \ - defined(CONFIG_GUS_MODULE) || defined(CONFIG_MPU401_MODULE) || \ - defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \ - defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \ - defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \ - defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE) || \ - defined(CONFIG_OPL3SA1_MODULE) || defined(CONFIG_SOFTOSS_MODULE) -# define CONFIG_MIDI -#endif - -#if \ - defined(CONFIG_SB) || defined(CONFIG_TRIX) || \ - defined(CONFIG_MAD16) || \ - defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \ - defined(CONFIG_MAD16_MODULE) -# define CONFIG_SBDSP -#endif -#if \ - defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \ - defined(CONFIG_MAD16_MODULE) -# define CONFIG_SBDSP_MODULE -#endif - -#if \ - defined(CONFIG_SB) || defined(CONFIG_TRIX) || \ - defined(CONFIG_MAD16) || defined(CONFIG_SB_MODULE) || \ - defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) -# define CONFIG_UART401 -#endif - -#if \ - defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \ - defined(CONFIG_MAD16_MODULE) -#ifndef CONFIG_UART401_MODULE -#define CONFIG_UART401_MODULE -#endif -#endif - -#if \ - defined(CONFIG_PAS) || defined(CONFIG_SB) || \ - defined(CONFIG_ADLIB) || defined(CONFIG_GUS) || \ - defined(CONFIG_MPU401) || defined(CONFIG_PSS) || \ - defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \ - defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \ - defined(CONFIG_MAUI) || defined(CONFIG_OPL3SA1) || \ - defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \ - defined(CONFIG_ADLIB_MODULE) || defined(CONFIG_GUS_MODULE) || \ - defined(CONFIG_MPU401_MODULE) || defined(CONFIG_PSS_MODULE) || \ - defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \ - defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE) || \ - defined(CONFIG_MAUI_MODULE) || defined(CONFIG_OPL3SA1_MODULE) -# define CONFIG_SEQUENCER -#endif - -/* - * Force on additional support - */ - -#define SM_WAVE -#define __SGNXPRO__ -/* #define SM_GAMES */ -#define DESKPROXL diff --git a/drivers/sound/lowlevel/Config.in b/drivers/sound/lowlevel/Config.in new file mode 100644 index 000000000..36928af08 --- /dev/null +++ b/drivers/sound/lowlevel/Config.in @@ -0,0 +1,56 @@ +dep_tristate 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND_OSS + +dep_tristate 'AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND_OSS + +if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND" = "m" ]; then + dep_tristate 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16 $CONFIG_SOUND_OSS + if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then + hex ' I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 + fi + + if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then + comment 'SC-6600 Audio Cards have no jumper switches at all' + bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 + if [ "$CONFIG_SC6600" = "y" ]; then + comment 'SC-6600 specific configuration' + bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY + int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4 + hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0 + fi + + if [ "$CONFIG_SOUND_SB" = "y" -o "$CONFIG_SOUND_SB" = "m" ]; then + if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then + bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO + if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then + comment 'Audio Excel DSP 16 [Sound Blaster Pro]' + hex 'I/O base for Audio Excel DSP 16 220, 240' CONFIG_AEDSP16_BASE $CONFIG_SB_BASE + int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_SB_IRQ $CONFIG_SB_IRQ + int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_SB_DMA $CONFIG_SB_DMA + fi + fi + fi + + if [ "$CONFIG_SOUND_MSS" = "y" -o "$CONFIG_SOUND_MSS" = "m" ]; then + if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then + bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS + if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then + comment 'Audio Excel DSP 16 [Microsoft Sound System]' + hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 + int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_MSS_IRQ $CONFIG_MSS_IRQ + int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_MSS_DMA $CONFIG_MSS_DMA + fi + fi + fi + + if [ "$CONFIG_SOUND_MPU401" = "y" -o "$CONFIG_SOUND_MPU401" = "m" ]; then + bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401 + if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then + comment 'Audio Excel DSP 16 [MPU-401]' + if [ "$CONFIG_AEDSP16_SBPRO" != "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then + hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 + fi + int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' CONFIG_AEDSP16_MPU_IRQ $CONFIG_MPU_IRQ + fi + fi + fi +fi diff --git a/drivers/sound/lowlevel/Config.tmpl b/drivers/sound/lowlevel/Config.tmpl deleted file mode 100644 index e3ad81dbb..000000000 --- a/drivers/sound/lowlevel/Config.tmpl +++ /dev/null @@ -1,57 +0,0 @@ -bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND - -if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then - bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER - bool 'AWE32 synth' CONFIG_AWE32_SYNTH - bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16 - - if [ "$CONFIG_AEDSP16" = "y" ]; then - comment 'SC-6600 Audio Cards have no jumper switches at all' - bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 - - if [ "$CONFIG_SB" = "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then - bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO - if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then - comment 'Audio Excel DSP 16 [Sound Blaster Pro]' - hex 'I/O base for Audio Excel DSP 16 220 or 240' \ - CONFIG_AEDSP16_BASE $CONFIG_SB_BASE - int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \ - CONFIG_AEDSP16_SB_IRQ $CONFIG_SB_IRQ - int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_SB_DMA $CONFIG_SB_DMA - fi - fi - - if [ "$CONFIG_MSS" = "y" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then - bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS - if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then - comment 'Audio Excel DSP 16 [Microsoft Sound System]' - hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 - int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \ - CONFIG_AEDSP16_MSS_IRQ $CONFIG_MSS_IRQ - int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_MSS_DMA $CONFIG_MSS_DMA - fi - fi - - if [ "$CONFIG_MPU401" = "y" ]; then - bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401 - if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then - comment 'Audio Excel DSP 16 [MPU-401]' - if [ "$CONFIG_AEDSP16_SBPRO" != "y" \ - -a "$CONFIG_AEDSP16_MSS" != "y" ]; then - hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 - fi - int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \ - CONFIG_AEDSP16_MPU_IRQ $CONFIG_MPU_IRQ - fi - fi - - if [ "$CONFIG_SC6600" = "y" ]; then - comment 'SC-6600 specific configuration' - bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY - int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \ - CONFIG_SC6600_CDROM 4 - hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0 - fi - - fi -fi diff --git a/drivers/sound/lowlevel/Makefile b/drivers/sound/lowlevel/Makefile index 0340b37b9..921f10362 100644 --- a/drivers/sound/lowlevel/Makefile +++ b/drivers/sound/lowlevel/Makefile @@ -1,67 +1,24 @@ -all: lowlevel.o - -ALLOBJS = init.o aci.o awe_wave.o aedsp16.o -OBJS = init.o - -ifeq ($(CONFIG_LOWLEVEL_SOUND),y) -ifeq ($(CONFIG_ACI_MIXER),y) - OBJS := $(OBJS) aci.o -endif -ifeq ($(CONFIG_AWE32_SYNTH),y) -OBJS := $(OBJS) awe_wave.o -else - ifeq ($(CONFIG_AWE32_SYNTH),m) - MX_OBJS := $(MX_OBJS) awe_wave.o - endif -endif -ifeq ($(CONFIG_AEDSP16),y) - OBJS := $(OBJS) aedsp16.o -else - ifeq ($(CONFIG_AEDSP16),m) - MX_OBJS := $(MX_OBJS) aedsp16.o - endif -endif -endif - -ifndef TOPDIR -TOPDIR=/usr/src/linux -endif - -lowlevel.o: $(OBJS) - $(LD) -r -o lowlevel.o $(OBJS) +# Makefile for the Linux low-level sound card drivers. +# +# 11 Feb 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net> +# Rewritten to use lists instead of if statements. -module: manual_config.h - rm -f lowlevel.o - make CFLAGS="$(CFLAGS) -DLOWLEVEL_MODULE" $(ALLOBJS) - $(LD) -r -o lowlevel.o $(ALLOBJS) - touch module +export-objs := soundlow.o -manual_config.h: - @echo You should create `pwd`/manual_config.h. - @echo See `pwd`/README for more info. - @exit 1 +list-y := +list-m := +list-n := +list- := -clean: - rm -f core x y z *~ *.o module .depend +obj-$(CONFIG_SOUND_OSS) += soundlow.o +obj-$(CONFIG_ACI_MIXER) += aci.o +obj-$(CONFIG_AEDSP16) += aedsp16.o +obj-$(CONFIG_AWE32_SYNTH) += awe_wave.o -dep: - $(CPP) -M $(CFLAGS) -I. *.c > .depend +O_TARGET := lowlevel.o +O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) -ifndef HOSTCC -# -# Running outside the kernel build. -# -CC = gcc -HOSTCC = gcc -CFLAGS = -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486 -USE_DEPEND=y -else include $(TOPDIR)/Rules.make -endif - -ifdef USE_DEPEND -# include a dependency file if one exists -ifeq (.depend,$(wildcard .depend)) -include .depend -endif -endif diff --git a/drivers/sound/lowlevel/README.awe b/drivers/sound/lowlevel/README.awe index a0281710a..1765d4b15 100644 --- a/drivers/sound/lowlevel/README.awe +++ b/drivers/sound/lowlevel/README.awe @@ -1,12 +1,12 @@ ================================================================ - AWE32 Sound Driver for Linux / FreeBSD + AWE32 Sound Driver for Linux and FreeBSD version 0.4.2c; Oct. 7, 1997 ================================================================ * GENERAL NOTES -This is a sound driver extension for SoundBlaster AWE32 and other -compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable +This is a sound driver extension for the Sound Blaster AWE32 and other +compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64, etc.) to enable the wave synth operations. The driver is provided for both Linux 1.2.x and 2.[01].x kernels, and also FreeBSD on Intel x86 and DEC Alpha systems. See INSTALL.awe (or INSTALL.fbsd) document for @@ -120,8 +120,8 @@ Define them only when the driver couldn't detect the card properly. [Sample Table Size] From ver.0.4.0, sample tables are allocated dynamically (except Linux-1.2.x system), so you need NOT to touch these parameters. -Linux-1.2.x users may need to increase these values to apropriate size -if larger DRAM is equipped with the soundcard. +Linux-1.2.x users may need to increase these values for sound +cards equipped with more DRAM. - AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS @@ -135,7 +135,7 @@ if larger DRAM is equipped with the soundcard. passthrough channels. - AWE_DEBUG_ON (default: defined) - turns on debuggin messages if defined. + turns on debugging messages if defined. - AWE_HAS_GUS_COMPATIBILITY (default: defined) Enables GUS compatibility mode if defined, reading GUS patches and @@ -166,26 +166,26 @@ if larger DRAM is equipped with the soundcard. * ACKNOWLEDGMENTS -Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for many advices -to programming of AWE32. Many codes are brought from his AWE32-native +Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice +on programming of AWE32. Much code is brought from his AWE32-native MOD player, ALMP. -The port of awedrv to FreeBSD is done by Randall Hopper +The port of awedrv to FreeBSD was done by Randall Hopper (rhh@ct.picker.com). I also thank linux-awe-ml members for their efforts -to reboot their system many times :-) +to reboot their systems many times. :-) * BUGS & TODO'S -- Can't detect DRAM size on some card -- More smart patch management -- More smart DRAM memory control -- etc, etc, etc. +- can't detect DRAM size on some cards +- smarter patch management +- smarter DRAM memory control +- etc., etc., etc. * COPYRIGHT -Copyright (C) 1996,1997 Takashi Iwai +Copyright (C) 1996, 1997 Takashi Iwai 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 diff --git a/drivers/sound/lowlevel/aci.c b/drivers/sound/lowlevel/aci.c index 84d5c73c2..d46088b8e 100644 --- a/drivers/sound/lowlevel/aci.c +++ b/drivers/sound/lowlevel/aci.c @@ -13,7 +13,7 @@ * software. * * This Voxware ACI driver currently only supports the ACI functions - * on the miroSOUND PCM12 card. Support for miro soundcards with + * on the miroSOUND PCM12 card. Support for miro sound cards with * additional ACI functions can easily be added later. * * Revision history: @@ -76,6 +76,7 @@ static int aci_present = 0; #ifdef MODULE /* Whether the aci mixer is to be reset. */ int aci_reset = 0; /* Default: don't reset if the driver is a */ +MODULE_PARM(aci_reset,"i"); #else /* module; use "insmod sound.o aci_reset=1" */ int aci_reset = 1; /* to override. */ #endif @@ -88,7 +89,7 @@ int aci_reset = 1; /* to override. */ /* * Wait until the ACI microcontroller has set the READYFLAG in the * Busy/IRQ Source Register to 0. This is required to avoid - * overrunning the soundcard microcontroller. We do a busy wait here, + * overrunning the sound card microcontroller. We do a busy wait here, * because the microcontroller is not supposed to signal a busy * condition for more than a few clock cycles. In case of a time-out, * this function returns -1. @@ -491,7 +492,7 @@ int attach_aci(void) } if (aci_idcode[0] == 0x6d) { - /* it looks like a miro soundcard */ + /* It looks like a miro sound card. */ switch (aci_idcode[1]) { case 0x41: boardname = "PCM1 pro / early PCM12"; diff --git a/drivers/sound/lowlevel/awe_wave.c b/drivers/sound/lowlevel/awe_wave.c index a9ac8850e..111b0c5be 100644 --- a/drivers/sound/lowlevel/awe_wave.c +++ b/drivers/sound/lowlevel/awe_wave.c @@ -48,7 +48,7 @@ #endif #ifdef AWE_HAS_GUS_COMPATIBILITY -/* include finetune table */ +/* include fine tuning table */ #ifdef AWE_OBSOLETE_VOXWARE # ifdef __FreeBSD__ # define SEQUENCER_C diff --git a/drivers/sound/lowlevel/init.c b/drivers/sound/lowlevel/soundlow.c index d2e9242e4..64ac00ccd 100644 --- a/drivers/sound/lowlevel/init.c +++ b/drivers/sound/lowlevel/soundlow.c @@ -4,10 +4,9 @@ #include "lowlevel.h" #include <linux/config.h> +#include <linux/module.h> #include "../soundvers.h" -#ifdef CONFIG_LOWLEVEL_SOUND - #ifdef LOWLEVEL_MODULE char *lowlevel_version = SOUND_VERSION_STRING; #endif @@ -61,4 +60,7 @@ sound_unload_lowlevel_drivers(void) #endif } -#endif + +EXPORT_SYMBOL(sound_init_lowlevel_drivers); +EXPORT_SYMBOL(sound_unload_lowlevel_drivers); +EXPORT_SYMBOL(sound_preinit_lowlevel_drivers); diff --git a/drivers/sound/mad16.c b/drivers/sound/mad16.c index 30f366964..607955d23 100644 --- a/drivers/sound/mad16.c +++ b/drivers/sound/mad16.c @@ -16,7 +16,7 @@ * OAK OTI-601D Mozart * OPTi 82C929 MAD16 Pro * OPTi 82C930 - * OPTi 82C924 (in non PnP mode) + * OPTi 82C924 * * These audio interface chips don't produce sound themselves. They just * connect some other components (OPL-[234] and a WSS compatible codec) @@ -60,6 +60,11 @@ * Changes * * Alan Cox Clean up, added module selections. + * + * A. Wik Added support for Opti924 PnP. + * Improved debugging support. 16-May-1998 + * Fixed bug. 16-Jun-1998 + * */ #include "sound_config.h" @@ -74,7 +79,7 @@ static int mad16_cdsel; #endif -#if defined(CONFIG_MAD16) || defined(MODULE) +#ifdef CONFIG_MAD16 #include "sb.h" @@ -116,11 +121,14 @@ static int already_initialized = 0; static int board_type = C928; static int *mad16_osp; -static int c931_detected; /* minor diferences from C930 */ +static int c931_detected; /* minor differences from C930 */ +static char c924pnp = 0; /* " " " C924 */ +static int debug = 0; /* debugging output */ -#ifndef DDB -#define DDB(x) +#ifdef DDB +#undef DDB #endif +#define DDB(x) {if (debug) x;} static unsigned char mad_read(int port) { @@ -146,7 +154,11 @@ static unsigned char mad_read(int port) break; case C924: - outb((0xE5), PASSWD_REG); + /* the c924 has its ports relocated by -128 if + PnP is enabled -aw */ + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); break; } @@ -156,7 +168,9 @@ static unsigned char mad_read(int port) tmp = inb(0xe0f); /* Read from data reg */ } else - tmp = inb(port); + if (!c924pnp) + tmp = inb(port); else + tmp = inb(port-0x80); restore_flags(flags); return tmp; @@ -185,7 +199,9 @@ static void mad_write(int port, int value) break; case C924: - outb((0xE5), PASSWD_REG); + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); break; } @@ -195,7 +211,9 @@ static void mad_write(int port, int value) outb(((unsigned char) (value & 0xff)), 0xe0f); } else - outb(((unsigned char) (value & 0xff)), port); + if (!c924pnp) + outb(((unsigned char) (value & 0xff)), port); else + outb(((unsigned char) (value & 0xff)), port-0x80); restore_flags(flags); } @@ -231,7 +249,7 @@ static int detect_c930(void) } tmp = mad_read(MC0_PORT+18); - if (tmp == 0xff) + if (tmp == 0xff || tmp == 0x00) return 1; /* We probably have a C931 */ DDB(printk("Detected C931 config=0x%02x\n", tmp)); @@ -261,7 +279,7 @@ static int detect_c930(void) if ((mad_read(MC0_PORT+13) & 0x80) == 0) return 1; - /* Force off PnP mode, This is not recommended because + /* Force off PnP mode. This is not recommended because * the PnP bios will not recognize the chip on the next * warm boot and may assignd different resources to other * PnP/PCI cards. @@ -278,8 +296,8 @@ static int detect_mad16(void) /* * Check that reading a register doesn't return bus float (0xff) * when the card is accessed using password. This may fail in case - * the card is in low power mode. Normally at least the power saving mode - * bit should be 0. + * the card is in low power mode. Normally at least the power saving + * mode bit should be 0. */ if ((tmp = mad_read(MC1_PORT)) == 0xff) @@ -288,7 +306,9 @@ static int detect_mad16(void) return 0; } for (i = 0xf8d; i <= 0xf98; i++) - DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))); + if (!c924pnp) + DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))) else + DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i))); if (board_type == C930) return detect_c930(); @@ -405,13 +425,17 @@ static int init_c930(struct address_info *hw_config) /* MC2 is CD configuration. Don't touch it. */ mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ + + /* bit 2 of MC4 reverses it's meaning between the C930 + and the C931. */ + cfg = c931_detected ? 0x04 : 0x00; #ifdef MAD16_CDSEL if(MAD16_CDSEL & 0x20) - mad_write(MC4_PORT, 0x66); /* opl4 */ + mad_write(MC4_PORT, 0x62|cfg); /* opl4 */ else - mad_write(MC4_PORT, 0x56); /* opl3 */ + mad_write(MC4_PORT, 0x52|cfg); /* opl3 */ #else - mad_write(MC4_PORT, 0x56); + mad_write(MC4_PORT, 0x52|cfg); #endif mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ @@ -431,9 +455,15 @@ static int chip_detect(void) board_type = C924; DDB(printk("Detect using password = 0xE5\n")); + + if (!detect_mad16()) { + c924pnp++; + DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n")); + } if (!detect_mad16()) /* No luck. Try different model */ { + c924pnp=0; board_type = C928; DDB(printk("Detect using password = 0xE2\n")); @@ -467,23 +497,15 @@ static int chip_detect(void) return 0; DDB(printk("mad16.c: 82C930 detected\n")); - } - else - { + } else DDB(printk("mad16.c: 82C929 detected\n")); - } - } - else - { + } else { unsigned char model; - if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) - { + if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) { DDB(printk("mad16.c: Mozart detected\n")); board_type = MOZART; - } - else - { + } else { DDB(printk("mad16.c: 82C928 detected???\n")); board_type = C928; } @@ -523,7 +545,9 @@ int probe_mad16(struct address_info *hw_config) for (i = 0xf8d; i <= 0xf93; i++) - DDB(printk("port %03x = %02x\n", i, mad_read(i))); + if (!c924pnp) + DDB(printk("port %03x = %02x\n", i, mad_read(i))) else + DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i))); /* * Set the WSS address @@ -592,9 +616,9 @@ int probe_mad16(struct address_info *hw_config) mad_write(MC5_PORT, 0x30 | cs4231_mode); } - for (i = 0xf8d; i <= 0xf93; i++) - DDB(printk("port %03x after init = %02x\n", i, mad_read(i))); - + for (i = 0xf8d; i <= 0xf93; i++) if (!c924pnp) + DDB(printk("port %03x after init = %02x\n", i, mad_read(i))) else + DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i))); wss_init(hw_config); return 1; @@ -810,13 +834,13 @@ int probe_mad16_mpu(struct address_info *hw_config) void unload_mad16(struct address_info *hw_config) { + int mixer = audio_devs[hw_config->slots[0]]->mixer_dev; ad1848_unload(hw_config->io_base + 4, hw_config->irq, hw_config->dma, hw_config->dma2, 0); release_region(hw_config->io_base, 4); sound_unload_audiodev(hw_config->slots[0]); - } void @@ -863,6 +887,7 @@ MODULE_PARM(cdport,"i"); MODULE_PARM(cddma,"i"); MODULE_PARM(opl4,"i"); MODULE_PARM(joystick,"i"); +MODULE_PARM(debug,"i"); EXPORT_NO_SYMBOLS; @@ -1005,10 +1030,10 @@ int init_module(void) config_mpu.io_base = mpu_io; config_mpu.irq = mpu_irq; - found_mpu = probe_mad16_mpu(&config_mpu); - attach_mad16(&config); + found_mpu = probe_mad16_mpu(&config_mpu); + if (found_mpu) attach_mad16_mpu(&config_mpu); @@ -1019,7 +1044,7 @@ int init_module(void) void cleanup_module(void) { if (found_mpu) - unload_mad16_mpu(&config); + unload_mad16_mpu(&config_mpu); unload_mad16(&config); SOUND_LOCK_END; } diff --git a/drivers/sound/maui.c b/drivers/sound/maui.c index 4d2076273..baf770a96 100644 --- a/drivers/sound/maui.c +++ b/drivers/sound/maui.c @@ -29,7 +29,7 @@ #include "soundmodule.h" #include "sound_firmware.h" -#if defined(CONFIG_MAUI) || defined(MODULE) +#ifdef CONFIG_MAUI static int maui_base = 0x330; @@ -50,7 +50,7 @@ static int *maui_osp; static int (*orig_load_patch) (int dev, int format, const char *addr, int offs, int count, int pmgr_flag) = NULL; -#ifdef CONFIG_MAUI_HAVE_BOOT +#ifdef HAVE_MAUI_BOOT #include "maui_boot.h" #else static unsigned char *maui_os = NULL; diff --git a/drivers/sound/midi_syms.c b/drivers/sound/midi_syms.c new file mode 100644 index 000000000..6a692d914 --- /dev/null +++ b/drivers/sound/midi_syms.c @@ -0,0 +1,31 @@ +/* + * Exported symbols for midi driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include <linux/module.h> + +char midi_syms_symbol; + +#include "sound_config.h" +#define _MIDI_SYNTH_C_ +#include "midi_synth.h" + +EXPORT_SYMBOL(do_midi_msg); +EXPORT_SYMBOL(midi_synth_open); +EXPORT_SYMBOL(midi_synth_close); +EXPORT_SYMBOL(midi_synth_ioctl); +EXPORT_SYMBOL(midi_synth_kill_note); +EXPORT_SYMBOL(midi_synth_start_note); +EXPORT_SYMBOL(midi_synth_set_instr); +EXPORT_SYMBOL(midi_synth_reset); +EXPORT_SYMBOL(midi_synth_hw_control); +EXPORT_SYMBOL(midi_synth_aftertouch); +EXPORT_SYMBOL(midi_synth_controller); +EXPORT_SYMBOL(midi_synth_panning); +EXPORT_SYMBOL(midi_synth_setup_voice); +EXPORT_SYMBOL(midi_synth_send_sysex); +EXPORT_SYMBOL(midi_synth_bender); +EXPORT_SYMBOL(midi_synth_load_patch); +EXPORT_SYMBOL(MIDIbuf_avail); diff --git a/drivers/sound/midi_synth.c b/drivers/sound/midi_synth.c index 4fb23261b..8848f410e 100644 --- a/drivers/sound/midi_synth.c +++ b/drivers/sound/midi_synth.c @@ -20,7 +20,7 @@ #include "sound_config.h" -#if defined(CONFIG_MIDI) || defined (MODULE) +#ifdef CONFIG_MIDI #define _MIDI_SYNTH_C_ @@ -31,7 +31,7 @@ static int sysex_state[MAX_MIDI_DEV] = {0}; static unsigned char prev_out_status[MAX_MIDI_DEV]; -#if !defined(CONFIG_SEQUENCER) && !defined(MODULE) +#ifndef CONFIG_SEQUENCER #define STORE(cmd) #else #define STORE(cmd) \ diff --git a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c index 50d595d2f..23780b5a2 100644 --- a/drivers/sound/midibuf.c +++ b/drivers/sound/midibuf.c @@ -411,6 +411,11 @@ unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) void MIDIbuf_init(void) { + /* drag in midi_syms.o */ + { + extern char midi_syms_symbol; + midi_syms_symbol = 0; + } } int MIDIbuf_avail(int dev) diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c index ce92010fc..c73366b10 100644 --- a/drivers/sound/mpu401.c +++ b/drivers/sound/mpu401.c @@ -24,11 +24,11 @@ #include "sound_config.h" #include "soundmodule.h" -#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) || defined(MODULE) +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) #include "coproc.h" -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; #endif @@ -159,7 +159,7 @@ static unsigned char len_tab[] = /* # of data bytes following a status 0 /* Fx */ }; -#if !defined(CONFIG_SEQUENCER) && !defined(MODULE) +#ifndef CONFIG_SEQUENCER #define STORE(cmd) #else #define STORE(cmd) \ @@ -1216,7 +1216,7 @@ void unload_mpu401(struct address_info *hw_config) * Timer stuff ****************************************************/ -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#if defined(CONFIG_SEQUENCER) static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; static volatile int curr_tempo, curr_timebase, hw_timebase; diff --git a/drivers/sound/msnd.c b/drivers/sound/msnd.c new file mode 100644 index 000000000..8c0f99ed8 --- /dev/null +++ b/drivers/sound/msnd.c @@ -0,0 +1,412 @@ +/********************************************************************* + * + * 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. + * + * $Id: msnd.c,v 1.5 1998/07/18 00:12:15 andrewtv Exp $ + * + ********************************************************************/ + +#include <linux/version.h> +#if LINUX_VERSION_CODE < 0x020101 +# define LINUX20 +#endif +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/mm.h> +#ifdef LINUX20 +# include <linux/major.h> +# include <linux/fs.h> +# include <linux/sound.h> +# include <asm/segment.h> +# include "sound_config.h" +#else +# include <linux/init.h> +# include <asm/io.h> +# include <asm/uaccess.h> +# include <asm/spinlock.h> +#endif +#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; + + MOD_INC_USE_COUNT; + + 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; + + MOD_DEC_USE_COUNT; +} + +int msnd_get_num_devs(void) +{ + return num_devs; +} + +multisound_dev_t *msnd_get_dev(int j) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS && j; ++i) + if (devs[i] != NULL) + --j; + + if (i == MSND_MAX_DEVS || j != 0) + return NULL; + + return devs[i]; +} + +void msnd_fifo_init(msnd_fifo *f) +{ + f->data = NULL; +} + +void msnd_fifo_free(msnd_fifo *f) +{ + if (f->data) { + vfree(f->data); + f->data = NULL; + } +} + +int msnd_fifo_alloc(msnd_fifo *f, size_t n) +{ + msnd_fifo_free(f); + f->data = (char *)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(msnd_fifo *f, const char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == f->n) + return 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; + } + + if (user) { +#ifdef LINUX20 + if (verify_area(VERIFY_READ, buf , nwritten)) + return nwritten; + + memcpy_fromfs(f->data + f->tail, buf, nwritten); +#else + if (copy_from_user(f->data + f->tail, buf, nwritten)) + return -EFAULT; +#endif + + } else + 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(msnd_fifo *f, char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == 0) + return f->len; + + 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; + } + + if (user) { +#ifdef LINUX20 + if (verify_area(VERIFY_WRITE, buf, nread)) + return nread; + + memcpy_tofs(buf, f->data + f->head, nread); +#else + if (copy_to_user(buf, f->data + f->head, nread)) + return -EFAULT; +#endif + } else + memcpy(buf, f->data + f->head, nread); + + count += nread; + buf += nread; + f->len -= nread; + f->head += nread; + f->head %= f->n; + } + + return count; +} + +int msnd_wait_TXDE(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 100; + + while(timeout-- > 0) + if (inb(io + HP_ISR) & HPISR_TXDE) + return 0; + + return -EIO; +} + +int msnd_wait_HC0(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 100; + + while(timeout-- > 0) + if (!(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) { + + outb(cmd, dev->io + HP_CVR); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + printk(KERN_WARNING 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) { + + outb(high, io + HP_TXH); + outb(mid, io + HP_TXM); + outb(low, io + HP_TXL); + return 0; + } + + printk(KERN_WARNING 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; + + inb(dev->io + HP_RXL); + inb(dev->io + HP_CVR); + + return 0; +} + +int msnd_enable_irq(multisound_dev_t *dev) +{ + printk(KERN_DEBUG LOGNAME ": enable_irq: count %d\n", dev->irq_ref); + + if (dev->irq_ref++ != 0) + return 0; + + printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n"); + + if (msnd_wait_TXDE(dev) == 0) { + + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); + + if (dev->type == msndClassic) + outb(dev->irqid, dev->io + HP_IRQM); + + outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); + outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; + } + + return -EIO; +} + +int msnd_disable_irq(multisound_dev_t *dev) +{ + unsigned long flags; + + printk(KERN_DEBUG LOGNAME ": disable_irq: count %d\n", dev->irq_ref); + + if (--dev->irq_ref > 0) + return 0; + + if (dev->irq_ref < 0) + dev->irq_ref = 0; + + printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n"); + + udelay(50); + + spin_lock_irqsave(&dev->lock, flags); + outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); + + if (dev->type == msndClassic) + outb(HPIRQ_NONE, dev->io + HP_IRQM); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +#ifndef LINUX20 +EXPORT_SYMBOL(msnd_register); +EXPORT_SYMBOL(msnd_unregister); +EXPORT_SYMBOL(msnd_get_num_devs); +EXPORT_SYMBOL(msnd_get_dev); + +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); +EXPORT_SYMBOL(msnd_fifo_read); + +EXPORT_SYMBOL(msnd_wait_TXDE); +EXPORT_SYMBOL(msnd_wait_HC0); +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"); + +int init_module(void) +{ + return 0; +} + +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/sound/msnd.h b/drivers/sound/msnd.h new file mode 100644 index 000000000..b0a330ca4 --- /dev/null +++ b/drivers/sound/msnd.h @@ -0,0 +1,287 @@ +/********************************************************************* + * + * 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. + * + * $Id: msnd.h,v 1.6 1998/07/18 00:12:15 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_H +#define __MSND_H + +#define VERSION "0.7.0" + +#define DEFSAMPLERATE DSP_DEFAULT_SPEED +#define DEFSAMPLESIZE 8 +#define DEFCHANNELS 1 + +#define DEFFIFOSIZE 64 + +#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) + +#ifdef SLOWIO +# undef outb +# undef inb +# define outb outb_p +# define inb inb_p +#endif + +#ifdef LINUX20 +# define __initfunc(f) f +# define __initdata /* nothing */ +# define spin_lock_irqsave(junk,flags) do { save_flags(flags); cli(); } while (0) +# define spin_unlock_irqrestore(junk,flags) do { restore_flags(flags); } while (0) +#endif + +typedef unsigned char BYTE; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned int DWORD; +typedef +struct DAQueueDataStruct * LPDAQD; + +#define GCC_PACKED __attribute__ ((packed)) + +struct JobQueueStruct { + WORD wStart; + WORD wSize; + WORD wHead; + WORD wTail; +} GCC_PACKED; + +struct DAQueueDataStruct { + WORD wStart; + WORD wSize; + WORD wFormat; + WORD wSampleSize; + WORD wChannels; + WORD wSampleRate; + WORD wIntMsg; + WORD wFlags; +} GCC_PACKED; + +typedef struct { + size_t n, len; + char *data; + int head, tail; +} msnd_fifo; + +typedef struct multisound_dev { + + char *name; + int dsp_minor, mixer_minor; + + /* Hardware resources */ + unsigned int io, numio; + int memid, irqid; + int irq, irq_ref; + unsigned char info; + char *base; +#ifndef LINUX20 + spinlock_t lock; +#endif + + /* MultiSound DDK variables */ + enum { msndClassic, msndPinnacle } type; + struct SMA0_CommonData *SMA; /* diff. structure for classic vs. pinnacle */ + struct DAQueueDataStruct *CurDAQD; + struct DAQueueDataStruct *CurDARQD; + volatile WORD *pwDSPQData , *pwMIDQData , *pwMODQData; + volatile struct JobQueueStruct *DAPQ , *DARQ , *MODQ , *MIDQ , *DSPQ; + BYTE bCurrentMidiPatch; + + /* State variables */ + mode_t mode; + unsigned long flags; +#define F_BANKONE 0 +#define F_INTERRUPT 1 +#define F_WRITING 2 +#define F_WRITEBLOCK 3 +#define F_READING 4 +#define F_READBLOCK 5 +#define F_AUDIO_INUSE 6 +#define F_EXT_MIDI_INUSE 7 +#define F_INT_MIDI_INUSE 8 +#define F_WRITEFLUSH 9 + + struct wait_queue *writeblock, *readblock; + struct wait_queue *writeflush; + unsigned long recsrc; + int left_levels[16]; + int right_levels[16]; + int calibrate_signal; + int sample_size; + int sample_rate; + int channels; + void (*inc_ref)(void); + void (*dec_ref)(void); + + /* Digital audio FIFOs */ + int fifosize; + msnd_fifo DAPF, DARF; + int lastbank; + + /* 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); +int msnd_get_num_devs(void); +multisound_dev_t * msnd_get_dev(int i); + +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(msnd_fifo *f, const char *buf, size_t len, int user); +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user); + +int msnd_wait_TXDE(multisound_dev_t *dev); +int msnd_wait_HC0(multisound_dev_t *dev); +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/drivers/sound/msnd_classic.c b/drivers/sound/msnd_classic.c new file mode 100644 index 000000000..3b23a096f --- /dev/null +++ b/drivers/sound/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/drivers/sound/msnd_classic.h b/drivers/sound/msnd_classic.h new file mode 100644 index 000000000..3ae0e0b3b --- /dev/null +++ b/drivers/sound/msnd_classic.h @@ -0,0 +1,188 @@ +/********************************************************************* + * + * 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. + * + * $Id: msnd_classic.h,v 1.5 1998/07/18 00:12:15 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_CLASSIC_H +#define __MSND_CLASSIC_H + +#include <linux/config.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_PROTEUS 0x10 +#define MOP_EXTOUT 0x32 +#define MOP_EXTTHRU 0x02 +#define MOP_OUTMASK 0x01 + +#define MIP_EXTIN 0x01 +#define MIP_PROTEUS 0x00 +#define MIP_INMASK 0x32 + +struct SMA0_CommonData { + WORD wCurrPlayBytes; + WORD wCurrRecordBytes; + WORD wCurrPlayVolLeft; + WORD wCurrPlayVolRight; + WORD wCurrInVolLeft; + WORD wCurrInVolRight; + WORD wUser_3; + WORD wUser_4; + DWORD dwUser_5; + DWORD dwUser_6; + WORD wUser_7; + WORD wReserved_A; + WORD wReserved_B; + WORD wReserved_C; + WORD wReserved_D; + WORD wReserved_E; + WORD wReserved_F; + WORD wReserved_G; + WORD wReserved_H; + WORD wCurrDSPStatusFlags; + WORD wCurrHostStatusFlags; + WORD wCurrInputTagBits; + WORD wCurrLeftPeak; + WORD wCurrRightPeak; + WORD wExtDSPbits; + BYTE bExtHostbits; + BYTE bBoardLevel; + BYTE bInPotPosRight; + BYTE bInPotPosLeft; + BYTE bAuxPotPosRight; + BYTE bAuxPotPosLeft; + WORD wCurrMastVolLeft; + WORD wCurrMastVolRight; + BYTE bUser_12; + BYTE bUser_13; + WORD wUser_14; + WORD wUser_15; + WORD wCalFreqAtoD; + WORD wUser_16; + WORD wUser_17; +} GCC_PACKED; + +#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/drivers/sound/msnd_pinnacle.c b/drivers/sound/msnd_pinnacle.c new file mode 100644 index 000000000..ac33dcab8 --- /dev/null +++ b/drivers/sound/msnd_pinnacle.c @@ -0,0 +1,1519 @@ +/********************************************************************* + * + * 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. + * + * $Id: msnd_pinnacle.c,v 1.5 1998/07/18 00:12:16 andrewtv Exp $ + * + ********************************************************************/ + +#include <linux/config.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE < 0x020101 +# define LINUX20 +#endif +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/types.h> +#include <linux/delay.h> +#ifndef LINUX20 +# include <linux/init.h> +#endif +#include "sound_config.h" +#include "sound_firmware.h" +#ifdef MSND_CLASSIC +# define SLOWIO +#endif +#include "msnd.h" +#ifdef MSND_CLASSIC +# include "msnd_classic.h" +# define LOGNAME "msnd_classic" +#else +# include "msnd_pinnacle.h" +# define LOGNAME "msnd_pinnacle" +#endif + +#define DEVNAME dev.name +#define MIXERMINOR dev.mixer_minor +#define DSPMINOR dev.dsp_minor + +multisound_dev_t dev; + +#ifndef HAVE_DSPCODEH +static char *dspini, *permini; +static int sizeof_dspini, sizeof_permini; +#endif + +static void reset_play_queue(void) +{ + int n; + LPDAQD lpDAQ; + + msnd_fifo_make_empty(&dev.DAPF); + dev.DAPQ->wHead = 0; + dev.DAPQ->wTail = PCTODSP_OFFSET(2 * DAPQ_STRUCT_SIZE); + dev.CurDAQD = (LPDAQD)(dev.base + 1 * DAPQ_DATA_BUFF); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + memset_io(dev.base, 0, DAP_BUFF_SIZE * 3); + + for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart); + writew(DAP_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } + + dev.lastbank = -1; +} + +static void reset_record_queue(void) +{ + int n; + LPDAQD lpDAQ; + + msnd_fifo_make_empty(&dev.DARF); + dev.DARQ->wHead = 0; + dev.DARQ->wTail = PCTODSP_OFFSET(2 * DARQ_STRUCT_SIZE); + dev.CurDARQD = (LPDAQD)(dev.base + 1 * DARQ_DATA_BUFF); + outb(HPBLKSEL_1, dev.io + HP_BLKS); + memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart); + writew(DAR_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } +} + +static void reset_queues(void) +{ + dev.DSPQ->wHead = dev.DSPQ->wTail = 0; + reset_play_queue(); + reset_record_queue(); +} + +static int dsp_ioctl(unsigned int cmd, unsigned long arg) +{ + int val, i, data, tmp; + LPDAQD lpDAQ, lpDARQ; + + lpDAQ = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + lpDARQ = (LPDAQD)(dev.base + DARQ_DATA_BUFF); + + switch (cmd) { + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return -EINVAL; + + case SNDCTL_DSP_SYNC: + case SNDCTL_DSP_RESET: + + reset_play_queue(); + reset_record_queue(); + + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + + tmp = dev.fifosize / 4; + if (put_user(tmp, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_NONBLOCK: + + dev.mode |= O_NONBLOCK; + + return 0; + + case SNDCTL_DSP_GETCAPS: + + val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_SAMPLESIZE: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 16: + case 8: + data = val; + break; + default: + data = DEFSAMPLESIZE; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wSampleSize = data; + lpDARQ->wSampleSize = data; + } + + dev.sample_size = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_SPEED: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val < 8000) + val = 8000; + + if (val > 48000) + val = 48000; + + data = val; + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wSampleRate = data; + lpDARQ->wSampleRate = data; + } + + dev.sample_rate = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_CHANNELS: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 1: + case 2: + data = val; + break; + default: + val = data = 2; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wChannels = data; + lpDARQ->wChannels = data; + } + + dev.channels = data; + + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_STEREO: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 0: + data = 1; + break; + default: + val = 1; + case 1: + data = 2; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wChannels = data; + lpDARQ->wChannels = data; + } + + dev.channels = data; + + if (put_user(val, (int *)arg)) + 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_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: +#ifndef MSND_CLASSIC + case SOUND_MIXER_MIC: +#endif + case SOUND_MIXER_IMIX: + case SOUND_MIXER_LINE1: + return (dev.left_levels[d] >> 8) * 100 / 0xff | + (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); + default: + return 0; + } +} + +#define update_vol(a,b,s) \ + writew(dev.left_levels[a] * readw(&dev.SMA->wCurrMastVolLeft) / 0xffff / s, \ + &dev.SMA->b##Left); \ + writew(dev.right_levels[a] * readw(&dev.SMA->wCurrMastVolRight) / 0xffff / s, \ + &dev.SMA->b##Right); + +static int mixer_set(int d, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int bLeft, bRight; + int wLeft, wRight; + + 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) { + case SOUND_MIXER_VOLUME: /* master volume */ + writew(wLeft / 2, &dev.SMA->wCurrMastVolLeft); + writew(wRight / 2, &dev.SMA->wCurrMastVolRight); + break; + + /* pot controls */ + case SOUND_MIXER_LINE: /* aux pot control */ + writeb(bLeft, &dev.SMA->bInPotPosLeft); + writeb(bRight, &dev.SMA->bInPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; + +#ifndef MSND_CLASSIC + case SOUND_MIXER_MIC: /* mic pot control */ + writeb(bLeft, &dev.SMA->bMicPotPosLeft); + writeb(bRight, &dev.SMA->bMicPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; +#endif + + case SOUND_MIXER_LINE1: /* line pot control */ + writeb(bLeft, &dev.SMA->bAuxPotPosLeft); + writeb(bRight, &dev.SMA->bAuxPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_AUX_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; + + /* 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) */ + break; + + default: + return 0; + } + + /* update digital controls for master volume */ + update_vol(SOUND_MIXER_PCM, wCurrPlayVol, 1); + update_vol(SOUND_MIXER_IMIX, wCurrInVol, 1); +#ifndef MSND_CLASSIC + update_vol(SOUND_MIXER_SYNTH, wCurrMHdrVol, 1); +#endif + + return mixer_get(d); +} + +static unsigned long set_recsrc(unsigned long recsrc) +{ +#ifdef HAVE_NORECSRC + if (recsrc == 0) + dev.recsrc = 0; + else +#endif + dev.recsrc ^= recsrc; + +#ifndef MSND_CLASSIC + if (dev.recsrc & SOUND_MASK_LINE) { + + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + msnd_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) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + + } + else { +#ifdef HAVE_NORECSRC + /* Select no input (?) */ + dev.recsrc = 0; +#else + dev.recsrc = SOUND_MASK_LINE; + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); +#endif + } +#endif /* MSND_CLASSIC */ + + return dev.recsrc; +} + +static int mixer_ioctl(unsigned int cmd, unsigned long arg) +{ + int val = 0; + + if (((cmd >> 8) & 0xff) == 'M') { + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = set_recsrc(val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = mixer_set(cmd & 0xff, val); + break; + } + + return put_user(val, (int *)arg); + } + else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + val = dev.recsrc; + break; + + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_VOLUME | +#ifndef MSND_CLASSIC + SOUND_MASK_SYNTH | + SOUND_MASK_MIC | +#endif + SOUND_MASK_PCM | + SOUND_MASK_LINE | + SOUND_MASK_IMIX; + break; + + case SOUND_MIXER_RECMASK: +#ifdef MSND_CLASSIC + val = 0; +#else + val = SOUND_MASK_LINE | + SOUND_MASK_SYNTH; +#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 *)arg); + } + + return -EINVAL; +} + +static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) + return dsp_ioctl(cmd, arg); + else if (minor == MIXERMINOR) + return mixer_ioctl(cmd, arg); + + return -EINVAL; +} + +static void dsp_halt(void) +{ + mdelay(1); +#ifdef LINUX20 + if (test_bit(F_READING, &dev.flags)) { + clear_bit(F_READING, &dev.flags); +#else + if (test_and_clear_bit(F_READING, &dev.flags)) { +#endif + msnd_send_dsp_cmd(&dev, HDEX_RECORD_STOP); + msnd_disable_irq(&dev); + + } + mdelay(1); +#ifdef LINUX20 + if (test_bit(F_WRITING, &dev.flags)) { + clear_bit(F_WRITING, &dev.flags); +#else + if (test_and_clear_bit(F_WRITING, &dev.flags)) { +#endif + set_bit(F_WRITEFLUSH, &dev.flags); + interruptible_sleep_on(&dev.writeflush); + current->state = TASK_INTERRUPTIBLE; + current->timeout = + jiffies + DAP_BUFF_SIZE / 2 * HZ / + dev.sample_rate / dev.channels; + schedule(); + current->timeout = 0; + msnd_send_dsp_cmd(&dev, HDEX_PLAY_STOP); + msnd_disable_irq(&dev); + memset_io(dev.base, 0, DAP_BUFF_SIZE * 3); + + } + mdelay(1); + reset_queues(); +} + +static int dsp_open(struct file *file) +{ + dev.mode = file->f_mode; + set_bit(F_AUDIO_INUSE, &dev.flags); + reset_queues(); + return 0; +} + +static int dsp_close(void) +{ + dsp_halt(); + clear_bit(F_AUDIO_INUSE, &dev.flags); + return 0; +} + +static int dev_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int err = 0; + + if (minor == DSPMINOR) { + + if (test_bit(F_AUDIO_INUSE, &dev.flags)) + return -EBUSY; + + err = dsp_open(file); + } + else if (minor == MIXERMINOR) { + /* nothing */ + } else + err = -EINVAL; + + if (err >= 0) + MOD_INC_USE_COUNT; + + return err; +} + +#ifdef LINUX20 +static void dev_close(struct inode *inode, struct file *file) +#else +static int dev_close(struct inode *inode, struct file *file) +#endif +{ + int minor = MINOR(inode->i_rdev); +#ifndef LINUX20 + int err = 0; +#endif + + if (minor == DSPMINOR) { +#ifndef LINUX20 + err = +#endif + dsp_close(); + } + else if (minor == MIXERMINOR) { + /* nothing */ + } +#ifndef LINUX20 + else + err = -EINVAL; + + if (err >= 0) +#endif + MOD_DEC_USE_COUNT; + +#ifndef LINUX20 + return err; +#endif +} + +static int DAPF_to_bank(int bank) +{ + return msnd_fifo_read(&dev.DAPF, dev.base + bank * DAP_BUFF_SIZE, DAP_BUFF_SIZE, 0); +} + +static int bank_to_DARF(int bank) +{ + return msnd_fifo_write(&dev.DARF, dev.base + bank * DAR_BUFF_SIZE, DAR_BUFF_SIZE, 0); +} + +static int dsp_read(char *buf, size_t len) +{ + int err = 0; + int count = len; + + while (count > 0) { + + int n; + + if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) { + + printk(KERN_WARNING LOGNAME ": FIFO read error\n"); + return n; + } + + buf += n; + count -= n; + +#ifdef LINUX20 + if (!test_bit(F_READING, &dev.flags) && (dev.mode & FMODE_READ)) { + set_bit(F_READING, &dev.flags); +#else + if (!test_and_set_bit(F_READING, &dev.flags) && (dev.mode & FMODE_READ)) { +#endif + reset_record_queue(); + msnd_enable_irq(&dev); + msnd_send_dsp_cmd(&dev, HDEX_RECORD_START); + + } + + if (dev.mode & O_NONBLOCK) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + + set_bit(F_READBLOCK, &dev.flags); + interruptible_sleep_on(&dev.readblock); + clear_bit(F_READBLOCK, &dev.flags); + + if (signal_pending(current)) + err = -EINTR; + + } + + if (err != 0) + return err; + } + + return len - count; +} + +static int dsp_write(const char *buf, size_t len) +{ + int err = 0; + int count = len; + + while (count > 0) { + + int n; + + if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) { + + printk(KERN_WARNING LOGNAME ": FIFO write error\n"); + return n; + } + + buf += n; + count -= n; + +#ifdef LINUX20 + if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { + set_bit(F_WRITING, &dev.flags); +#else + if (!test_and_set_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { +#endif + reset_play_queue(); + msnd_enable_irq(&dev); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + + } + + if (dev.mode & O_NONBLOCK) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + + set_bit(F_WRITEBLOCK, &dev.flags); + interruptible_sleep_on(&dev.writeblock); + clear_bit(F_WRITEBLOCK, &dev.flags); + + if (signal_pending(current)) + err = -EINTR; + + } + + if (err != 0) + return err; + } + + return len - count; +} + +#ifdef LINUX20 +static int dev_read(struct inode *inode, struct file *file, char *buf, int count) +{ + int minor = MINOR(inode->i_rdev); +#else +static ssize_t dev_read(struct file *file, char *buf, size_t count, loff_t *off) +{ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); +#endif + + if (minor == DSPMINOR) { + + return dsp_read(buf, count); + + } else + return -EINVAL; +} + +#ifdef LINUX20 +static int dev_write(struct inode *inode, struct file *file, const char *buf, int count) +{ + int minor = MINOR(inode->i_rdev); +#else +static ssize_t dev_write(struct file *file, const char *buf, size_t count, loff_t *off) +{ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); +#endif + + if (minor == DSPMINOR) { + + return dsp_write(buf, count); + + } else + return -EINVAL; +} + +static void eval_dsp_msg(WORD wMessage) +{ + switch (HIBYTE(wMessage)) { + case HIMT_PLAY_DONE: + + if (dev.lastbank == LOBYTE(wMessage)) + break; + + dev.lastbank = LOBYTE(wMessage); + + dev.CurDAQD->wSize = DAP_BUFF_SIZE; + + if ((dev.DAPQ->wTail += PCTODSP_OFFSET(DAPQ_STRUCT_SIZE)) > dev.DAPQ->wSize) + dev.DAPQ->wTail = 0; + + if (++dev.CurDAQD > (LPDAQD)(dev.base + DAPQ_DATA_BUFF + 2 * DAPQ_STRUCT_SIZE)) + dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + + if (dev.lastbank < 3) { + + if (DAPF_to_bank(dev.lastbank) > 0) { + + mdelay(1); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + + } + else if (!test_bit(F_WRITEBLOCK, &dev.flags)) { + + clear_bit(F_WRITING, &dev.flags); +#ifdef LINUX20 + if (test_bit(F_WRITEFLUSH, &dev.flags)) { + clear_bit(F_WRITEFLUSH, &dev.flags); + wake_up_interruptible(&dev.writeflush); + } +#else + if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags)) + wake_up_interruptible(&dev.writeflush); +#endif + msnd_disable_irq(&dev); + + } + } + + if (test_bit(F_WRITEBLOCK, &dev.flags)) + wake_up_interruptible(&dev.writeblock); + + break; + + case HIMT_RECORD_DONE: { + + WORD wTemp; + + wTemp = dev.DARQ->wTail + (DARQ_STRUCT_SIZE / 2); + + if (wTemp > dev.DARQ->wSize) + wTemp = 0; + + while (wTemp == dev.DARQ->wHead); + + dev.DARQ->wTail = wTemp; + + outb(HPBLKSEL_1, dev.io + HP_BLKS); + if (bank_to_DARF(LOBYTE(wMessage)) == 0 && + !test_bit(F_READBLOCK, &dev.flags)) { + + memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + clear_bit(F_READING, &dev.flags); + msnd_disable_irq(&dev); + + } + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + 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_INFO LOGNAME ": Write underflow\n"); + reset_play_queue(); + break; + + case HIDSP_INT_RECORD_OVER: + printk(KERN_INFO LOGNAME ": Read overflow\n"); + reset_record_queue(); + break; + + default: + printk(KERN_DEBUG LOGNAME ": DSP message %u\n", LOBYTE(wMessage)); + break; + } + break; + + case HIMT_MIDI_IN_UCHAR: + if (dev.midi_in_interrupt) + (*dev.midi_in_interrupt)(&dev); + break; + + case HIMT_MIDI_OUT: + printk(KERN_DEBUG LOGNAME ": MIDI out event\n"); + break; + + default: + printk(KERN_DEBUG LOGNAME ": HIMT message %u\n", HIBYTE(wMessage)); + break; + } +} + +static void intr(int irq, void *dev_id, struct pt_regs *regs) +{ + if (test_bit(F_INTERRUPT, &dev.flags)) + return; + + set_bit(F_INTERRUPT, &dev.flags); + + if (test_bit(F_BANKONE, &dev.flags)) + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + inb(dev.io + HP_RXL); + + while (dev.DSPQ->wTail != dev.DSPQ->wHead) { + + eval_dsp_msg(*(dev.pwDSPQData + dev.DSPQ->wHead)); + + if (++dev.DSPQ->wHead > dev.DSPQ->wSize) + dev.DSPQ->wHead = 0; + } + + if (test_bit(F_BANKONE, &dev.flags)) + outb(HPBLKSEL_1, dev.io + HP_BLKS); + + clear_bit(F_INTERRUPT, &dev.flags); +} + +static struct file_operations dev_fileops = { + NULL, + dev_read, + dev_write, + NULL, + NULL, + dev_ioctl, + NULL, + dev_open, + dev_close, +}; + +__initfunc(static int reset_dsp(void)) +{ + int timeout = 20000; + + outb(HPDSPRESET_ON, dev.io + HP_DSPR); + + mdelay(1); + + dev.info = inb(dev.io + HP_INFO); + + outb(HPDSPRESET_OFF, dev.io + HP_DSPR); + + mdelay(1); + + while (timeout-- > 0) { + + if (inb(dev.io + HP_CVR) == HP_CVR_DEF) + return 0; + + mdelay(1); + } + + printk(KERN_ERR LOGNAME ": Cannot reset DSP\n"); + + return -EIO; +} + +__initfunc(static int probe_multisound(void)) +{ +#ifndef MSND_CLASSIC + char *xv, *rev = NULL; + char *pin = "Pinnacle", *fiji = "Fiji"; + char *pinfiji = "Pinnacle/Fiji"; +#endif + + if (check_region(dev.io, dev.numio)) { + + printk(KERN_ERR LOGNAME ": I/O port conflict\n"); + return -ENODEV; + } + + request_region(dev.io, dev.numio, "probing"); + + if (reset_dsp() < 0) { + + release_region(dev.io, dev.numio); + return -ENODEV; + } + + printk(KERN_INFO LOGNAME ": DSP reset successful\n"); + +#ifdef MSND_CLASSIC + dev.name = "Classic/Tahiti/Monterey"; + printk(KERN_INFO LOGNAME ": Turtle Beach %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 ": Turtle Beach %s revision %s, Xilinx version %s, " +#endif /* MSND_CLASSIC */ + "I/O 0x%x-0x%x, IRQ %d, memory mapped to 0x%p-0x%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; +} + +__initfunc(static int init_sma(void)) +{ + int n; + LPDAQD lpDAQ; + +#ifdef MSND_CLASSIC + outb(dev.memid, dev.io + HP_MEMM); +#endif + outb(HPBLKSEL_0, dev.io + HP_BLKS); + memset_io(dev.base, 0, 0x8000); + + outb(HPBLKSEL_1, dev.io + HP_BLKS); + memset_io(dev.base, 0, 0x8000); + + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + dev.DAPQ = (struct JobQueueStruct *)(dev.base + DAPQ_OFFSET); + dev.DARQ = (struct JobQueueStruct *)(dev.base + DARQ_OFFSET); + dev.MODQ = (struct JobQueueStruct *)(dev.base + MODQ_OFFSET); + dev.MIDQ = (struct JobQueueStruct *)(dev.base + MIDQ_OFFSET); + dev.DSPQ = (struct JobQueueStruct *)(dev.base + DSPQ_OFFSET); + + dev.SMA = (struct SMA0_CommonData *)(dev.base + SMA_STRUCT_START); + + dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + dev.CurDARQD = (LPDAQD)(dev.base + DARQ_DATA_BUFF); + + dev.sample_size = DEFSAMPLESIZE; + dev.sample_rate = DEFSAMPLERATE; + dev.channels = DEFCHANNELS; + + for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart); + writew(DAP_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + } + + for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart); + writew(DAR_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } + + dev.pwDSPQData = (WORD *)(dev.base + DSPQ_DATA_BUFF); + dev.pwMODQData = (WORD *)(dev.base + MODQ_DATA_BUFF); + dev.pwMIDQData = (WORD *)(dev.base + MIDQ_DATA_BUFF); + + writew(PCTODSP_BASED(MIDQ_DATA_BUFF), &dev.MIDQ->wStart); + writew(PCTODSP_OFFSET(MIDQ_BUFF_SIZE) - 1, &dev.MIDQ->wSize); + writew(0, &dev.MIDQ->wHead); + writew(0, &dev.MIDQ->wTail); + + writew(PCTODSP_BASED(MODQ_DATA_BUFF), &dev.MODQ->wStart); + writew(PCTODSP_OFFSET(MODQ_BUFF_SIZE) - 1, &dev.MODQ->wSize); + writew(0, &dev.MODQ->wHead); + writew(0, &dev.MODQ->wTail); + + writew(PCTODSP_BASED(DAPQ_DATA_BUFF), &dev.DAPQ->wStart); + writew(PCTODSP_OFFSET(DAPQ_BUFF_SIZE) - 1, &dev.DAPQ->wSize); + writew(0, &dev.DAPQ->wHead); + writew(0, &dev.DAPQ->wTail); + + writew(PCTODSP_BASED(DARQ_DATA_BUFF), &dev.DARQ->wStart); + writew(PCTODSP_OFFSET(DARQ_BUFF_SIZE) - 1, &dev.DARQ->wSize); + writew(0, &dev.DARQ->wHead); + writew(0, &dev.DARQ->wTail); + + writew(PCTODSP_BASED(DSPQ_DATA_BUFF), &dev.DSPQ->wStart); + writew(PCTODSP_OFFSET(DSPQ_BUFF_SIZE) - 1, &dev.DSPQ->wSize); + writew(0, &dev.DSPQ->wHead); + writew(0, &dev.DSPQ->wTail); + + writew(0, &dev.SMA->wCurrPlayBytes); + writew(0, &dev.SMA->wCurrRecordBytes); + + writew(0, &dev.SMA->wCurrPlayVolLeft); + writew(0, &dev.SMA->wCurrPlayVolRight); + + writew(0, &dev.SMA->wCurrInVolLeft); + writew(0, &dev.SMA->wCurrInVolRight); + + writew(0, &dev.SMA->wCurrMastVolLeft); + writew(0, &dev.SMA->wCurrMastVolRight); + +#ifndef MSND_CLASSIC + writel(0x00010000, &dev.SMA->dwCurrPlayPitch); + writel(0x00000001, &dev.SMA->dwCurrPlayRate); +#endif + + writew(0x0000, &dev.SMA->wCurrDSPStatusFlags); + writew(0x0000, &dev.SMA->wCurrHostStatusFlags); + + writew(0x303, &dev.SMA->wCurrInputTagBits); + writew(0, &dev.SMA->wCurrLeftPeak); + writew(0, &dev.SMA->wCurrRightPeak); + + writeb(0, &dev.SMA->bInPotPosRight); + writeb(0, &dev.SMA->bInPotPosLeft); + + writeb(0, &dev.SMA->bAuxPotPosRight); + writeb(0, &dev.SMA->bAuxPotPosLeft); + +#ifndef MSND_CLASSIC + writew(1, &dev.SMA->wCurrPlayFormat); + writew(dev.sample_size, &dev.SMA->wCurrPlaySampleSize); + writew(dev.channels, &dev.SMA->wCurrPlayChannels); + writew(dev.sample_rate, &dev.SMA->wCurrPlaySampleRate); +#endif + writew(dev.sample_rate, &dev.SMA->wCalFreqAtoD); + + return 0; +} + +__initfunc(static int calibrate_adc(WORD srate)) +{ + if (!dev.calibrate_signal) { + + printk(KERN_INFO LOGNAME ": ADC calibration to board ground "); + writew(readw(&dev.SMA->wCurrHostStatusFlags) + | 0x0001, &dev.SMA->wCurrHostStatusFlags); + } + else { + + printk(KERN_INFO LOGNAME ": ADC calibration to signal ground "); + writew(readw(&dev.SMA->wCurrHostStatusFlags) + & ~0x0001, &dev.SMA->wCurrHostStatusFlags); + } + + writew(srate, &dev.SMA->wCalFreqAtoD); + + if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { + + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + current->timeout = 0; + printk("successful\n"); + return 0; + } + + printk("failed\n"); + + return -EIO; +} + +__initfunc(static int upload_dsp_code(void)) +{ + outb(HPBLKSEL_0, dev.io + HP_BLKS); + +#ifdef HAVE_DSPCODEH + printk(KERN_INFO LOGNAME ": Using resident Turtle Beach DSP code\n"); +#else + printk(KERN_INFO LOGNAME ": Loading Turtle Beach DSP code\n"); + 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"); + return -ENODEV; + } + +#ifndef HAVE_DSPCODEH + vfree(INITCODE); + vfree(PERMCODE); +#endif + + return 0; +} + +#ifdef MSND_CLASSIC +__initfunc(static void reset_proteus(void)) +{ + outb(HPPRORESET_ON, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET); + outb(HPPRORESET_OFF, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET_DONE); +} +#endif + +__initfunc(static int initialize(void)) +{ + int err, timeout; + +#ifdef MSND_CLASSIC + outb(HPWAITSTATE_0, dev.io + HP_WAIT); + 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; + + } else + printk(KERN_INFO LOGNAME ": DSP upload successful\n"); + + timeout = 2000; + + while (readw(dev.base)) { + + mdelay(1); + if (--timeout < 0) + return -EIO; + } + + return 0; +} + +__initfunc(static int attach_multisound(void)) +{ + int err; + + printk(KERN_DEBUG LOGNAME ": Intializing DSP\n"); + + if ((err = request_irq(dev.irq, intr, SA_SHIRQ, DEVNAME, &dev)) < 0) { + + printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq); + return err; + + } + + request_region(dev.io, dev.numio, DEVNAME); + + if ((err = initialize()) < 0) { + + printk(KERN_WARNING LOGNAME ": Initialization failure\n"); + 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 ((DSPMINOR = register_sound_dsp(&dev_fileops)) < 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 DSPMINOR; + } + + if ((MIXERMINOR = register_sound_mixer(&dev_fileops)) < 0) { + + printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n"); + unregister_sound_mixer(MIXERMINOR); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return MIXERMINOR; + } + printk(KERN_INFO LOGNAME ": Using DSP minor %d, mixer minor %d\n", MIXERMINOR, DSPMINOR); + + calibrate_adc(dev.sample_rate); + set_recsrc(0); + + return 0; +} + +static void unload_multisound(void) +{ + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + unregister_sound_mixer(MIXERMINOR); + unregister_sound_dsp(DSPMINOR); + msnd_unregister(&dev); +} + +static void mod_inc_ref(void) +{ + MOD_INC_USE_COUNT; +} + +static void mod_dec_ref(void) +{ + MOD_DEC_USE_COUNT; +} + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>"); +MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver"); +MODULE_PARM (io, "i"); +MODULE_PARM (irq, "i"); +MODULE_PARM (mem, "i"); +MODULE_PARM (major, "i"); +MODULE_PARM (fifosize, "i"); +MODULE_PARM (calibrate_signal, "i"); + +static int io __initdata = -1; +static int irq __initdata = -1; +static int mem __initdata = -1; +static int fifosize __initdata = DEFFIFOSIZE; +static int calibrate_signal __initdata; + +int init_module(void) +#else /* not a module */ +#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 +static int io __initdata = CONFIG_MSNDPIN_IO; +static int irq __initdata = CONFIG_MSNDPIN_IRQ; +static int mem __initdata = CONFIG_MSNDPIN_MEM; +#endif +static int fifosize __initdata = DEFFIFOSIZE; +static int calibrate_signal __initdata; + +#ifdef MSND_CLASSIC +__initfunc(int msnd_classic_init(void)) +#else +__initfunc(int msnd_pinnacle_init(void)) +#endif /* MSND_CLASSIC */ +#endif +{ + int err; + + 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"); + } + + 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\n"); + return -EINVAL; + } + + 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; + } +#endif /* MSND_CLASSIC */ + + if (fifosize < 16) + fifosize = 16; + + if (fifosize > 768) + fifosize = 768; + +#ifdef MSND_CLASSIC + dev.type = msndClassic; +#else + dev.type = msndPinnacle; +#endif + dev.io = io; + dev.numio = DSP_NUMIO; + dev.irq = irq; + dev.base = phys_to_virt(mem); + dev.fifosize = fifosize * 1024; + dev.calibrate_signal = calibrate_signal ? 1 : 0; + dev.recsrc = 0; + dev.inc_ref = mod_inc_ref; + dev.dec_ref = mod_dec_ref; + + init_waitqueue(&dev.writeblock); + init_waitqueue(&dev.readblock); + init_waitqueue(&dev.writeflush); + msnd_fifo_init(&dev.DAPF); + msnd_fifo_init(&dev.DARF); +#ifndef LINUX20 + spin_lock_init(&dev.lock); +#endif + + printk(KERN_INFO LOGNAME ": Using %u byte digital 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; +} + +#ifdef MODULE +void cleanup_module(void) +{ + printk(KERN_INFO LOGNAME ": Unloading\n"); + + unload_multisound(); + + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + +} +#endif diff --git a/drivers/sound/msnd_pinnacle.h b/drivers/sound/msnd_pinnacle.h new file mode 100644 index 000000000..f6971c949 --- /dev/null +++ b/drivers/sound/msnd_pinnacle.h @@ -0,0 +1,244 @@ +/********************************************************************* + * + * 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. + * + * $Id: msnd_pinnacle.h,v 1.5 1998/07/18 00:12:16 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_PINNACLE_H +#define __MSND_PINNACLE_H + +#include <linux/config.h> + +#define DSP_NUMIO 0x08 + +#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 WAVEHDR_MOP 0 +#define EXTOUT_MOP 1 +#define HWINIT_MOP 0xFE +#define NO_MOP 0xFF + +#define MAX_MOP 1 + +#define EXTIN_MIP 0 +#define WAVEHDR_MIP 1 +#define HWINIT_MIP 0xFE + +#define MAX_MIP 1 + +struct SMA0_CommonData { + WORD wCurrPlayBytes; + WORD wCurrRecordBytes; + WORD wCurrPlayVolLeft; + WORD wCurrPlayVolRight; + + WORD wCurrInVolLeft; + WORD wCurrInVolRight; + WORD wCurrMHdrVolLeft; + WORD wCurrMHdrVolRight; + + DWORD dwCurrPlayPitch; + DWORD dwCurrPlayRate; + + WORD wCurrMIDIIOPatch; + + WORD wCurrPlayFormat; + WORD wCurrPlaySampleSize; + WORD wCurrPlayChannels; + WORD wCurrPlaySampleRate; + + WORD wCurrRecordFormat; + WORD wCurrRecordSampleSize; + WORD wCurrRecordChannels; + WORD wCurrRecordSampleRate; + + WORD wCurrDSPStatusFlags; + WORD wCurrHostStatusFlags; + + WORD wCurrInputTagBits; + WORD wCurrLeftPeak; + WORD wCurrRightPeak; + + BYTE bMicPotPosLeft; + BYTE bMicPotPosRight; + + BYTE bMicPotMaxLeft; + BYTE bMicPotMaxRight; + + BYTE bInPotPosLeft; + BYTE bInPotPosRight; + + BYTE bAuxPotPosLeft; + BYTE bAuxPotPosRight; + + BYTE bInPotMaxLeft; + BYTE bInPotMaxRight; + BYTE bAuxPotMaxLeft; + BYTE bAuxPotMaxRight; + BYTE bInPotMaxMethod; + BYTE bAuxPotMaxMethod; + + WORD wCurrMastVolLeft; + WORD wCurrMastVolRight; + + WORD wCalFreqAtoD; + + WORD wCurrAuxVolLeft; + WORD wCurrAuxVolRight; + + WORD wCurrPlay1VolLeft; + WORD wCurrPlay1VolRight; + WORD wCurrPlay2VolLeft; + WORD wCurrPlay2VolRight; + WORD wCurrPlay3VolLeft; + WORD wCurrPlay3VolRight; + WORD wCurrPlay4VolLeft; + WORD wCurrPlay4VolRight; + WORD wCurrPlay1PeakLeft; + WORD wCurrPlay1PeakRight; + WORD wCurrPlay2PeakLeft; + WORD wCurrPlay2PeakRight; + WORD wCurrPlay3PeakLeft; + WORD wCurrPlay3PeakRight; + WORD wCurrPlay4PeakLeft; + WORD wCurrPlay4PeakRight; + WORD wCurrPlayPeakLeft; + WORD wCurrPlayPeakRight; + + WORD wCurrDATSR; + WORD wCurrDATRXCHNL; + WORD wCurrDATTXCHNL; + WORD wCurrDATRXRate; + + DWORD dwDSPPlayCount; +} GCC_PACKED; + +#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/drivers/sound/opl3.c b/drivers/sound/opl3.c index cd768801f..bc6382f99 100644 --- a/drivers/sound/opl3.c +++ b/drivers/sound/opl3.c @@ -32,7 +32,7 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_YM3812) || defined(MODULE) +#ifdef CONFIG_YM3812 #include "opl3.h" @@ -1209,11 +1209,10 @@ void cleanup_module(void) SOUND_LOCK_END; } -#else - -#endif #endif EXPORT_SYMBOL(opl3_init); EXPORT_SYMBOL(opl3_detect); MODULE_PARM(io, "i"); + +#endif diff --git a/drivers/sound/opl3sa.c b/drivers/sound/opl3sa.c index 86fe07f10..8be3b91a9 100644 --- a/drivers/sound/opl3sa.c +++ b/drivers/sound/opl3sa.c @@ -1,5 +1,5 @@ /* - * sound/Xopl3sa.c + * sound/opl3sa.c * * Low level driver for Yamaha YMF701B aka OPL3-SA chip * @@ -19,10 +19,12 @@ */ #include <linux/config.h> +#include <linux/module.h> #undef SB_OK #include "sound_config.h" +#include "soundmodule.h" #ifdef SB_OK #include "sb.h" static int sb_initialized = 0; @@ -162,6 +164,7 @@ void attach_opl3sa_wss(struct address_info *hw_config) { int nm = num_mixers; + /* FIXME */ attach_ms_sound(hw_config); if (num_mixers > nm) /* A mixer was installed */ { @@ -293,6 +296,7 @@ MODULE_PARM(mpu_irq,"i"); struct address_info cfg; struct address_info mpu_cfg; +static int found_mpu; int init_module(void) { @@ -312,7 +316,7 @@ int init_module(void) if (probe_opl3sa_wss(&cfg) == 0) return -ENODEV; - found_mpu=probe_opl3_mpu(&mpu_cfg); + found_mpu=probe_opl3sa_mpu(&mpu_cfg); attach_opl3sa_wss(&cfg); if(found_mpu) @@ -325,7 +329,7 @@ void cleanup_module(void) { if(found_mpu) unload_opl3sa_mpu(&mpu_cfg); - unload_opl3sa(&cfg); + unload_opl3sa_wss(&cfg); SOUND_LOCK_END; } diff --git a/drivers/sound/os.h b/drivers/sound/os.h index 31fcc81f7..794036b81 100644 --- a/drivers/sound/os.h +++ b/drivers/sound/os.h @@ -57,3 +57,4 @@ extern int sound_nblocks; #undef PSEUDO_DMA_AUTOINIT #define ALLOW_BUFFER_MAPPING +extern struct file_operations oss_sound_fops; diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c index 8eb29e729..ab87cae40 100644 --- a/drivers/sound/pas2_card.c +++ b/drivers/sound/pas2_card.c @@ -9,7 +9,7 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_PAS) || defined(MODULE) +#ifdef CONFIG_PAS static unsigned char dma_bits[] = { 4, 1, 2, 3, 0, 5, 6, 7 @@ -42,6 +42,16 @@ static int joystick = 0; #else static int joystick = 1; #endif +#ifdef SYMPHONY_PAS +static int symphony = 1; +#else +static int symphony = 0; +#endif +#ifdef BROKEN_BUS_CLOCK +static int broken_bus_clock = 1; +#else +static int broken_bus_clock = 0; +#endif char pas_model = 0; @@ -89,7 +99,7 @@ static void pasintr(int irq, void *dev_id, struct pt_regs *dummy) } if (status & 0x10) { -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI pas_midi_interrupt(); #endif status &= ~0x10; @@ -199,19 +209,21 @@ static int config_pas_hw(struct address_info *hw_config) * 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. */ -#ifdef SYMPHONY_PAS - outb((0x05), 0xa8); - outb((0x60), 0xa9); -#endif -#ifdef BROKEN_BUS_CLOCK - pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388); -#else - /* - * pas_write(0x01, 0x8388); - */ - pas_write(0x01 | 0x10 | 0x20, 0x8388); -#endif + 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); @@ -219,7 +231,7 @@ static int config_pas_hw(struct address_info *hw_config) mix_write(0x80 | 5, 0x078B); mix_write(5, 0x078B); -#if !defined(DISABLE_SB_EMULATION) && (defined(CONFIG_SB) || defined(CONFIG_SB_MODULE)) +#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB) { struct address_info *sb_config; @@ -322,7 +334,7 @@ void attach_pas_card(struct address_info *hw_config) if ((pas_model = pas_read(0xFF88))) { - char temp[100]; + char temp[100]; sprintf(temp, "%s rev %d", pas_model_names[(int) pas_model], @@ -335,12 +347,12 @@ void attach_pas_card(struct address_info *hw_config) pas_pcm_init(hw_config); #endif -#if !defined(DISABLE_SB_EMULATION) && (defined(CONFIG_SB) || defined(CONFIG_SB_MODULE)) +#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB) sb_dsp_disable_midi(pas_sb_base); /* No MIDI capability */ #endif -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI pas_midi_init(); #endif pas_init_mixer(); @@ -348,14 +360,12 @@ void attach_pas_card(struct address_info *hw_config) } } -int -probe_pas(struct address_info *hw_config) +int probe_pas(struct address_info *hw_config) { return detect_pas_hw(hw_config); } -void -unload_pas(struct address_info *hw_config) +void unload_pas(struct address_info *hw_config) { sound_free_dma(hw_config->dma); free_irq(hw_config->irq, NULL); @@ -384,13 +394,15 @@ MODULE_PARM(sb_dma,"i"); MODULE_PARM(sb_dma16,"i"); MODULE_PARM(joystick,"i"); +MODULE_PARM(symphony,"i"); +MODULE_PARM(broken_bus_clock,"i"); struct address_info config; struct address_info sbhw_config; int init_module(void) { - printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n"); if (io == -1 || dma == -1 || irq == -1) { diff --git a/drivers/sound/pas2_midi.c b/drivers/sound/pas2_midi.c index 72120b83b..29203071b 100644 --- a/drivers/sound/pas2_midi.c +++ b/drivers/sound/pas2_midi.c @@ -15,7 +15,8 @@ #include "sound_config.h" -#if ( defined(MODULE) || defined(CONFIG_PAS) ) && defined(CONFIG_MIDI) +#ifdef CONFIG_PAS +#ifdef CONFIG_MIDI static int midi_busy = 0, input_opened = 0; static int my_dev; @@ -275,3 +276,4 @@ pas_midi_interrupt(void) } #endif +#endif diff --git a/drivers/sound/pas2_mixer.c b/drivers/sound/pas2_mixer.c index 7c0687b77..04a98fead 100644 --- a/drivers/sound/pas2_mixer.c +++ b/drivers/sound/pas2_mixer.c @@ -19,7 +19,7 @@ #include "sound_config.h" -#if defined(CONFIG_PAS) || defined(MODULE) +#ifdef CONFIG_PAS #ifndef DEB #define DEB(what) /* (what) */ @@ -125,11 +125,12 @@ pas_mixer_set(int whichDev, unsigned int level) left = level & 0x7f; right = (level & 0x7f00) >> 8; - if (whichDev < SOUND_MIXER_NRDEVICES) + if (whichDev < SOUND_MIXER_NRDEVICES) { if ((1 << whichDev) & rec_devices) mixer = 0x20; else mixer = 0x00; + } switch (whichDev) { diff --git a/drivers/sound/pas2_pcm.c b/drivers/sound/pas2_pcm.c index c155dc3a6..068252002 100644 --- a/drivers/sound/pas2_pcm.c +++ b/drivers/sound/pas2_pcm.c @@ -1,21 +1,25 @@ /* * 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. */ + #include <linux/config.h> #include "sound_config.h" -#if defined(MODULE) || ( defined(CONFIG_PAS) && defined(CONFIG_AUDIO) ) +#ifdef CONFIG_PAS +#ifdef CONFIG_AUDIO #ifndef DEB #define DEB(WHAT) @@ -41,11 +45,10 @@ static int pcm_busy = 0; int pas_audiodev = 0; static int open_mode = 0; -static int -pcm_set_speed(int arg) +static int pcm_set_speed(int arg) { - int foo, tmp; - unsigned long flags; + int foo, tmp; + unsigned long flags; if (arg == 0) return pcm_speed; @@ -56,30 +59,31 @@ pcm_set_speed(int arg) arg = 5000; if (pcm_channels & 2) - { - foo = (596590 + (arg / 2)) / arg; - arg = (596590 + (foo / 2)) / foo; - } else - { - foo = (1193180 + (arg / 2)) / arg; - arg = (1193180 + (foo / 2)) / foo; - } + { + foo = (596590 + (arg / 2)) / arg; + arg = (596590 + (foo / 2)) / foo; + } + else + { + foo = (1193180 + (arg / 2)) / arg; + arg = (1193180 + (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) - * + * 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; @@ -112,25 +116,23 @@ pcm_set_speed(int arg) return pcm_speed; } -static int -pcm_set_channels(int arg) +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); + { + pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A); - pcm_channels = arg; - pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ - } + pcm_channels = arg; + pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ + } return pcm_channels; } -static int -pcm_set_bits(int arg) +static int pcm_set_bits(int arg) { if (arg == 0) return pcm_bits; @@ -139,11 +141,11 @@ pcm_set_bits(int arg) return pcm_bits; if (arg != pcm_bits) - { - pas_write(pas_read(0x8389) ^ 0x04, 0x8389); + { + pas_write(pas_read(0x8389) ^ 0x04, 0x8389); - pcm_bits = arg; - } + pcm_bits = arg; + } return pcm_bits; } @@ -153,9 +155,10 @@ static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); - switch (cmd) { + switch (cmd) + { case SOUND_PCM_WRITE_RATE: - if (__get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; ret = pcm_set_speed(val); break; @@ -165,13 +168,13 @@ static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) break; case SNDCTL_DSP_STEREO: - if (__get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; ret = pcm_set_channels(val + 1) - 1; break; case SOUND_PCM_WRITE_CHANNELS: - if (__get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; ret = pcm_set_channels(val); break; @@ -181,7 +184,7 @@ static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) break; case SNDCTL_DSP_SETFMT: - if (__get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; ret = pcm_set_bits(val); break; @@ -193,19 +196,17 @@ static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) default: return -EINVAL; } - return __put_user(ret, (int *)arg); + return put_user(ret, (int *)arg); } -static void -pas_audio_reset(int dev) +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) +static int pas_audio_open(int dev, int mode) { int err; unsigned long flags; @@ -215,10 +216,10 @@ pas_audio_open(int dev, int mode) save_flags(flags); cli(); if (pcm_busy) - { - restore_flags(flags); - return -EBUSY; - } + { + restore_flags(flags); + return -EBUSY; + } pcm_busy = 1; restore_flags(flags); @@ -232,8 +233,7 @@ pas_audio_open(int dev, int mode) return 0; } -static void -pas_audio_close(int dev) +static void pas_audio_close(int dev) { unsigned long flags; @@ -250,8 +250,7 @@ pas_audio_close(int dev) restore_flags(flags); } -static void -pas_audio_output_block(int dev, unsigned long buf, int count, +static void pas_audio_output_block(int dev, unsigned long buf, int count, int intrflag) { unsigned long flags, cnt; @@ -279,15 +278,15 @@ pas_audio_output_block(int dev, unsigned long buf, int count, 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, 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); @@ -298,8 +297,7 @@ pas_audio_output_block(int dev, unsigned long buf, int count, restore_flags(flags); } -static void -pas_audio_start_input(int dev, unsigned long buf, int count, +static void pas_audio_start_input(int dev, unsigned long buf, int count, int intrflag) { unsigned long flags; @@ -325,15 +323,15 @@ pas_audio_start_input(int dev, unsigned long buf, int count, 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, 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); @@ -345,8 +343,7 @@ pas_audio_start_input(int dev, unsigned long buf, int count, } #ifndef NO_TRIGGER -static void -pas_audio_trigger(int dev, int state) +static void pas_audio_trigger(int dev, int state) { unsigned long flags; @@ -365,15 +362,13 @@ pas_audio_trigger(int dev, int state) } #endif -static int -pas_audio_prepare_for_input(int dev, int bsize, int bcount) +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) +static int pas_audio_prepare_for_output(int dev, int bsize, int bcount) { pas_audio_reset(dev); return 0; @@ -396,8 +391,7 @@ static struct audio_driver pas_audio_driver = pas_audio_trigger }; -void -pas_pcm_init(struct address_info *hw_config) +void pas_pcm_init(struct address_info *hw_config) { DEB(printk("pas2_pcm.c: long pas_pcm_init()\n")); @@ -407,55 +401,45 @@ pas_pcm_init(struct address_info *hw_config) pcm_set_speed(DSP_DEFAULT_SPEED); - if ((pas_audiodev = sound_alloc_audiodev()) != -1) - { - - 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) - { - return; - } - } else + 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) +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("PAS: Unexpected PCM interrupt\n"); - } - } + { + /* + * 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"); + } + } } #endif +#endif diff --git a/drivers/sound/pss.c b/drivers/sound/pss.c index 9b00aa43a..fd6e0b6f5 100644 --- a/drivers/sound/pss.c +++ b/drivers/sound/pss.c @@ -22,6 +22,8 @@ * 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 */ @@ -32,7 +34,8 @@ #include "sound_firmware.h" #include "soundmodule.h" -#if (defined(CONFIG_PSS) && defined(CONFIG_AUDIO))||defined(MODULE) +#ifdef CONFIG_PSS +#ifdef CONFIG_AUDIO /* * PSS registers. @@ -80,14 +83,21 @@ #include "coproc.h" -#ifdef CONFIG_PSS_HAVE_BOOT +#ifdef PSS_HAVE_LD #include "pss_boot.h" #else -static unsigned char *pss_synth = NULL; static int pss_synthLen = 0; +static unsigned char *pss_synth = +NULL; +#endif + +/* If compiled into kernel, it enable or disable pss mixer */ +#ifdef CONFIG_PSS_MIXER +static unsigned char pss_mixer = 1; +#else +static unsigned char pss_mixer = 0; #endif -unsigned char pss_mixer = 1; typedef struct pss_mixerdata { unsigned int volume_l; @@ -375,12 +385,7 @@ static void set_master_volume(pss_confdata *devc, int left, int right) static void set_synth_volume(pss_confdata *devc, int volume) { - /* Should use: - int vol = (int)(0x8000/100.0 * (float)volume); - - Fixme: integerise the above cleanly - */ - int vol = (0x8000/(100L*volume)); + int vol = ((0x8000*volume)/100L); pss_write(devc, 0x0080); pss_write(devc, vol); pss_write(devc, 0x0081); @@ -389,32 +394,21 @@ static void set_synth_volume(pss_confdata *devc, int volume) static void set_bass(pss_confdata *devc, int level) { - /* Should use - int vol = (int)((0xfd - 0xf0)/100.0 * (float)level) + 0xf0; - - Fixme: integerise cleanly - */ - int vol = (int)((0xfd - 0xf0)/100L * level) + 0xf0; + 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) { - /* Should use - int vol = (int)((0xfd - 0xf0)/100.0 * (float)level) + 0xf0; - - Fixme: integerise properly - */ - - int vol = ((0xfd - 0xf0)/100L * level) + 0xf0; + 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, 23, 23); + set_master_volume(devc, 33, 33); set_bass(devc, 50); set_treble(devc, 50); set_synth_volume(devc, 30); @@ -423,7 +417,7 @@ static void pss_mixer_reset(pss_confdata *devc) if(pss_mixer) { - devc->mixer.volume_l = devc->mixer.volume_r = 23; + devc->mixer.volume_l = devc->mixer.volume_r = 33; devc->mixer.bass = 50; devc->mixer.treble = 50; devc->mixer.synth = 30; @@ -1059,7 +1053,7 @@ MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on MODULE_PARM(mpu_irq, "i"); MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)"); MODULE_PARM(pss_mixer, "b"); -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_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).\n"); @@ -1122,3 +1116,4 @@ void cleanup_module(void) } #endif #endif +#endif diff --git a/drivers/sound/sb.h b/drivers/sound/sb.h index 4567d3f5b..8d7ffe1a7 100644 --- a/drivers/sound/sb.h +++ b/drivers/sound/sb.h @@ -1,4 +1,5 @@ #include <linux/config.h> +#include "legacy.h" #ifdef CONFIG_SBDSP #define DSP_RESET (devc->base + 0x6) @@ -112,6 +113,7 @@ typedef struct sb_devc { int input_opened; int midi_broken; void (*midi_input_intr) (int dev, unsigned char data); + void *midi_irq_cookie; /* IRQ cookie for the midi */ } sb_devc; int sb_dsp_command (sb_devc *devc, unsigned char val); @@ -131,4 +133,5 @@ int ess_write (sb_devc *devc, unsigned char reg, unsigned char data); int ess_read (sb_devc *devc, unsigned char reg); extern int acer; +extern sb_devc *last_sb; #endif diff --git a/drivers/sound/sb_audio.c b/drivers/sound/sb_audio.c index f9465cfe5..971a7414f 100644 --- a/drivers/sound/sb_audio.c +++ b/drivers/sound/sb_audio.c @@ -14,13 +14,13 @@ * Alan Cox : Formatting and clean ups * * Status - * Mostly working. mmap bug still present (swaps channels) + * Mostly working. Weird uart bug causing irq storms */ #include <linux/config.h> #include "sound_config.h" -#if defined(CONFIG_SBDSP) || defined(MODULE) +#ifdef CONFIG_SBDSP #include "sb_mixer.h" #include "sb.h" @@ -32,7 +32,7 @@ static int sb_audio_open(int dev, int mode) if (devc == NULL) { - printk(KERN_ERR "SB: Incomplete initialization\n"); + printk(KERN_ERR "Sound Blaster: incomplete initialization.\n"); return -ENXIO; } if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) @@ -146,7 +146,7 @@ static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); } else - printk(KERN_WARNING "soundblaster: Unable to start DAC\n"); + printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n"); restore_flags(flags); devc->intr_active = 1; } @@ -177,7 +177,7 @@ static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); } else - printk(KERN_ERR "soundblaster: Unable to start ADC\n"); + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); restore_flags(flags); devc->intr_active = 1; @@ -322,10 +322,10 @@ static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes, cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ if (!sb_dsp_command(devc, cmd)) - printk(KERN_ERR "soundblaster: Unable to start DAC\n"); + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); } else - printk(KERN_ERR "soundblaster: Unable to start DAC\n"); + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); restore_flags(flags); devc->intr_active = 1; } @@ -362,10 +362,10 @@ static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */ if (!sb_dsp_command(devc, cmd)) - printk(KERN_ERR "soundblaster: Unable to start ADC\n"); + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); } else - printk(KERN_ERR "soundblaster: Unable to start ADC\n"); + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); restore_flags(flags); devc->intr_active = 1; } @@ -1138,7 +1138,7 @@ void sb_audio_init(sb_devc * devc, char *name) audio_flags, format_mask, devc, devc->dma8, devc->dma8)) < 0) { - printk(KERN_ERR "sb: unable to install audio.\n"); + printk(KERN_ERR "Sound Blaster: unable to install audio.\n"); return; } audio_devs[devc->my_dev]->mixer_dev = devc->my_mixerdev; diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c index ddf863372..432501ebc 100644 --- a/drivers/sound/sb_card.c +++ b/drivers/sound/sb_card.c @@ -17,7 +17,7 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_SBDSP) || defined (MODULE) +#ifdef CONFIG_SBDSP #include "sb_mixer.h" #include "sb.h" @@ -142,7 +142,6 @@ int acer = 1; int acer = 0; #endif #endif -#endif EXPORT_SYMBOL(sb_dsp_init); EXPORT_SYMBOL(sb_dsp_detect); @@ -152,3 +151,5 @@ EXPORT_SYMBOL(attach_sb_card); EXPORT_SYMBOL(probe_sb); EXPORT_SYMBOL(unload_sb); EXPORT_SYMBOL(sb_be_quiet); + +#endif diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index d82c9379f..8fe5a44f0 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -18,7 +18,7 @@ #include "sound_config.h" #include "sound_firmware.h" -#if defined(CONFIG_SBDSP) || defined(MODULE) +#ifdef CONFIG_SBDSP #ifndef CONFIG_AUDIO #error You will need to configure the sound driver with CONFIG_AUDIO option. @@ -57,6 +57,7 @@ static int smw_ucodeLen = 0; #endif +sb_devc *last_sb = NULL; /* Last sb loaded */ int sb_dsp_command(sb_devc * devc, unsigned char val) { @@ -81,7 +82,7 @@ int sb_dsp_command(sb_devc * devc, unsigned char val) return 1; } } - printk(KERN_WARNING "soundblaster: DSP Command(%x) Timeout.\n", val); + printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val); return 0; } @@ -131,9 +132,9 @@ static void sbintr(int irq, void *dev_id, struct pt_regs *dummy) { src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */ -#if defined(CONFIG_MIDI)&& (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE)) +#if defined(CONFIG_MIDI)&& defined(CONFIG_UART401) if (src & 4) - uart401intr(devc->irq, NULL, NULL); /* MPU401 interrupt */ + uart401intr(devc->irq, devc->midi_irq_cookie, NULL); /* MPU401 interrupt */ #endif if (!(src & 3)) @@ -155,7 +156,7 @@ static void sbintr(int irq, void *dev_id, struct pt_regs *dummy) break; case IMODE_MIDI: -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI sb_midi_interrupt(devc); #endif break; @@ -698,7 +699,7 @@ void sb_dsp_init(struct address_info *hw_config) sb_devc *devc; char name[100]; extern int sb_be_quiet; - int mixer3c, mixer4c; + int mixer22, mixer30; /* * Check if we had detected a SB device earlier @@ -722,12 +723,7 @@ void sb_dsp_init(struct address_info *hw_config) /* * Now continue initialization of the device */ - devc->dev = sound_alloc_audiodev(); - if (devc->dev == -1) - { - printk(KERN_WARNING "sb: too many audio devices.\n"); - return; - } + devc->caps = hw_config->driver_use_1; if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && hw_config->irq > 0) @@ -735,7 +731,6 @@ void sb_dsp_init(struct address_info *hw_config) if (request_irq(hw_config->irq, sbintr, 0, "soundblaster", devc) < 0) { printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); - sound_unload_audiodev(devc->dev); return; } devc->irq_ok = 0; @@ -744,7 +739,6 @@ void sb_dsp_init(struct address_info *hw_config) if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ { free_irq(devc->irq, devc); - sound_unload_audiodev(devc->dev); return; } if ((devc->type == 0 || devc->type == MDL_ESS) && @@ -790,6 +784,8 @@ void sb_dsp_init(struct address_info *hw_config) } /* IRQ setup */ request_region(hw_config->io_base, 16, "soundblaster"); + last_sb = devc; + switch (devc->major) { case 1: /* SB 1.0 or 1.5 */ @@ -814,21 +810,35 @@ void sb_dsp_init(struct address_info *hw_config) case 4: devc->model = hw_config->card_subtype = MDL_SB16; - /* - * The ALS007 seems to return DSP version 4.2. In addition it has 2 - * output control registers (at 0x3c and 0x4c). Both of these should - * be !=0 after a reset which forms the basis of the ALS007 test - * since a "standard" SoundBlaster does not have a register at 0x4c. + /* + * 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. */ - mixer3c = sb_getmixer(devc,0x3c); - mixer4c = sb_getmixer(devc,0x4c); - if ((devc->minor == 2) && (mixer3c != 0) && (mixer4c != 0)) + if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) { - sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */ - sb_setmixer(devc,0x4c,0x1f); - devc->submodel = SUBMDL_ALS007; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster (ALS-007)"; + 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)) + { + 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"; @@ -851,7 +861,7 @@ void sb_dsp_init(struct address_info *hw_config) if (devc->major == 3 || devc->major == 4) sb_mixer_init(devc); -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI if (!(devc->caps & SB_NO_MIDI)) sb_dsp_midi_init(devc); #endif @@ -863,8 +873,8 @@ void sb_dsp_init(struct address_info *hw_config) conf_printf(name, hw_config); /* - * Assuming that a soundcard is Sound Blaster (compatible) is the most common - * configuration error and the mother of all problems. Usually soundcards + * 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. @@ -873,7 +883,7 @@ void sb_dsp_init(struct address_info *hw_config) { if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */ { - printk(KERN_INFO "This soundcard may not be fully Sound Blaster Pro compatible.\n"); + 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"); @@ -898,13 +908,14 @@ void sb_dsp_init(struct address_info *hw_config) if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) { if (sound_alloc_dma(devc->dma16, "SoundBlaster16")) - printk(KERN_WARNING "soundblaster: Can't allocate 16 bit DMA channel %d\n", devc->dma16); + printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16); } sb_audio_init(devc, name); + hw_config->slots[0]=devc->dev; } else { - MDB(printk("soundblaster: No audio devices found.\n")); + MDB(printk("Sound Blaster: no audio devices found.\n")); } } @@ -936,7 +947,9 @@ void sb_dsp_unload(struct address_info *hw_config) { free_irq(devc->irq, devc); sound_unload_mixerdev(devc->my_mixerdev); - sound_unload_mididev(devc->my_mididev); + /* We don't have to do this bit any more the UART401 is its own + master -- Krzysztof Halasa */ + /* sound_unload_mididev(devc->my_mididev); */ sound_unload_audiodev(devc->my_dev); } kfree(devc); @@ -982,7 +995,7 @@ unsigned int sb_getmixer(sb_devc * devc, unsigned int port) return val; } -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI /* * MPU401 MIDI initialization. @@ -1034,7 +1047,7 @@ static int smw_midi_init(sb_devc * devc, struct address_info *hw_config) outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */ outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */ - udelay(3000); /* Wait at least 1ms */ + mdelay(3); /* Wait at least 1ms */ outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */ @@ -1247,14 +1260,15 @@ static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config) void attach_sbmpu(struct address_info *hw_config) { -#if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE)) +#if defined(CONFIG_MIDI) && defined(CONFIG_UART401) attach_uart401(hw_config); + last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc; #endif } int probe_sbmpu(struct address_info *hw_config) { -#if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE)) +#if defined(CONFIG_MIDI) && defined(CONFIG_UART401) sb_devc *devc = last_devc; if (last_devc == NULL) @@ -1275,7 +1289,7 @@ int probe_sbmpu(struct address_info *hw_config) 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->irq); + printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base); return 0; } hw_config->name = "Sound Blaster 16"; @@ -1312,7 +1326,7 @@ int probe_sbmpu(struct address_info *hw_config) void unload_sbmpu(struct address_info *hw_config) { -#if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE)) +#if defined(CONFIG_MIDI) && defined(CONFIG_UART401) unload_uart401(hw_config); #endif } diff --git a/drivers/sound/sb_midi.c b/drivers/sound/sb_midi.c index f7c1c85a2..970ec4a0b 100644 --- a/drivers/sound/sb_midi.c +++ b/drivers/sound/sb_midi.c @@ -179,7 +179,7 @@ void sb_dsp_midi_init(sb_devc * devc) if (dev == -1) { - printk("Sound: Too many midi devices detected\n"); + printk(KERN_ERR "sb_midi: too many MIDI devices detected\n"); return; } std_midi_synth.midi_dev = dev; @@ -188,7 +188,7 @@ void sb_dsp_midi_init(sb_devc * devc) midi_devs[dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL); if (midi_devs[dev] == NULL) { - printk(KERN_WARNING "soundblaster: Failed to allocate MIDI memory.\n"); + printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); sound_unload_mididev(dev); return; } @@ -201,7 +201,7 @@ void sb_dsp_midi_init(sb_devc * devc) midi_devs[dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); if (midi_devs[dev]->converter == NULL) { - printk(KERN_WARNING "soundblaster: Failed to allocate MIDI memory.\n"); + printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); kfree(midi_devs[dev]); sound_unload_mididev(dev); return; diff --git a/drivers/sound/sb_mixer.c b/drivers/sound/sb_mixer.c index 5f06807ce..7c4af3d03 100644 --- a/drivers/sound/sb_mixer.c +++ b/drivers/sound/sb_mixer.c @@ -18,7 +18,7 @@ #include <linux/config.h> #include "sound_config.h" -#if defined(CONFIG_SBDSP) || defined(MODULE) +#ifdef CONFIG_SBDSP #define __SB_MIXER_C__ #include "sb.h" diff --git a/drivers/sound/sb_mixer.h b/drivers/sound/sb_mixer.h index f92f02e7d..714b8a4d8 100644 --- a/drivers/sound/sb_mixer.h +++ b/drivers/sound/sb_mixer.h @@ -18,6 +18,7 @@ * */ #include <linux/config.h> +#include "legacy.h" #ifdef CONFIG_SBDSP diff --git a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c index e65995937..e4f962035 100644 --- a/drivers/sound/sequencer.c +++ b/drivers/sound/sequencer.c @@ -24,7 +24,7 @@ #define SEQUENCER_C #include "sound_config.h" -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER #include "softoss.h" int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3) = NULL; @@ -993,7 +993,7 @@ int sequencer_open(int dev, struct file *file) if (!sequencer_ok) { -/* printk("Soundcard: Sequencer not initialized\n");*/ +/* printk("Sound card: sequencer not initialized\n");*/ return -ENXIO; } if (dev) /* Patch manager device (obsolete) */ @@ -1694,6 +1694,12 @@ unsigned long compute_finetune(unsigned long base_freq, int bend, int range, void sequencer_init(void) { + /* drag in sequencer_syms.o */ + { + extern char sequencer_syms_symbol; + sequencer_syms_symbol = 0; + } + if (sequencer_ok) return; #ifdef CONFIG_MIDI diff --git a/drivers/sound/sequencer_syms.c b/drivers/sound/sequencer_syms.c new file mode 100644 index 000000000..053cd1e01 --- /dev/null +++ b/drivers/sound/sequencer_syms.c @@ -0,0 +1,37 @@ +/* + * Exported symbols for sequencer driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include <linux/module.h> + +char sequencer_syms_symbol; + +#include "sound_config.h" + +#include "sound_calls.h" + +EXPORT_SYMBOL(note_to_freq); +EXPORT_SYMBOL(compute_finetune); +EXPORT_SYMBOL(seq_copy_to_input); +EXPORT_SYMBOL(seq_input_event); +EXPORT_SYMBOL(sequencer_init); +EXPORT_SYMBOL(sequencer_timer); + +EXPORT_SYMBOL(sound_timer_init); +EXPORT_SYMBOL(sound_timer_interrupt); +EXPORT_SYMBOL(sound_timer_syncinterval); +EXPORT_SYMBOL(reprogram_timer); + +#include "softoss.h" + +EXPORT_SYMBOL(softsynthp); + +/* Tuning */ + +#define _SEQUENCER_C_ +#include "tuning.h" + +EXPORT_SYMBOL(cent_tuning); +EXPORT_SYMBOL(semitone_tuning); diff --git a/drivers/sound/sgalaxy.c b/drivers/sound/sgalaxy.c new file mode 100644 index 000000000..4e5cb870e --- /dev/null +++ b/drivers/sound/sgalaxy.c @@ -0,0 +1,190 @@ +/* + * sound/sgalaxy.c + * + * Low level driver for Aztech Sound Galaxy cards. + * Copyright 1998 Artur Skawina + * + * Supported cards: + * Aztech Sound Galaxy Waverider Pro 32 - 3D + * Aztech Sound Galaxy Washington 16 + * + * Based on cs4232.c by Hannu Savolainen and Alan Cox. + */ +/* + * 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/config.h> +#include <linux/module.h> + +#include "sound_config.h" +#include "soundmodule.h" + +#if defined(CONFIG_SGALAXY) || defined (MODULE) + +static void sleep( unsigned howlong ) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + howlong; + schedule(); + current->timeout = 0; +} + +#define DPORT 0x80 + +/* Sound Blaster regs */ + +#define SBDSP_RESET 0x6 +#define SBDSP_READ 0xA +#define SBDSP_COMMAND 0xC +#define SBDSP_STATUS SBDSP_COMMAND +#define SBDSP_DATA_AVAIL 0xE + +static int sb_rst(int base) +{ + int i; + + outb( 1, base+SBDSP_RESET ); /* reset the DSP */ + outb( 0, base+SBDSP_RESET ); + + for ( i=0; i<500; i++ ) /* delay */ + inb(DPORT); + + for ( i=0; i<100000; i++ ) + { + if ( inb( base+SBDSP_DATA_AVAIL )&0x80 ) + break; + } + + if ( inb( base+SBDSP_READ )!=0xAA ) + return 0; + + return 1; +} + +static int sb_cmd( int base, unsigned char val ) +{ + int i; + + for ( i=100000; i; i-- ) + { + if ( (inb( base+SBDSP_STATUS )&0x80)==0 ) + { + outb( val, base+SBDSP_COMMAND ); + break; + } + } + return i; /* i>0 == success */ +} + + +#define ai_sgbase driver_use_1 + +int probe_sgalaxy( struct address_info *ai ) +{ + if ( check_region( ai->io_base, 8 ) ) + { + printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); + return 0; + } + + if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) + return 1; /* The card is already active */ + + if ( check_region( ai->ai_sgbase, 0x10 ) ) + { + printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); + return 0; + } + + /* switch to MSS/WSS mode */ + + sb_rst( ai->ai_sgbase ); + + sb_cmd( ai->ai_sgbase, 9 ); + sb_cmd( ai->ai_sgbase, 0 ); + + sleep( HZ/10 ); + + if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) + return 1; + return 0; +} + +void attach_sgalaxy( struct address_info *ai ) +{ + int n; + + request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" ); + + attach_ms_sound( ai ); + n=ai->slots[0]; + + if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) + { + AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE ); /* Line-in */ + AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ + AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ + } +} + +void unload_sgalaxy( struct address_info *ai ) +{ + unload_ms_sound( ai ); + release_region( ai->ai_sgbase, 0x10 ); +} + +#ifdef MODULE + +int io = -1; +int irq = -1; +int dma = -1; +int dma2 = -1; +int sgbase = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(sgbase,"i"); + +EXPORT_NO_SYMBOLS; + +struct address_info ai; + + +int init_module(void) +{ + if ( io==-1 || irq==-1 || dma==-1 || sgbase==-1 ) + { + printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n"); + return -EINVAL; + } + + ai.io_base = io; + ai.irq = irq; + ai.dma = dma; + ai.dma2 = dma2; + ai.ai_sgbase = sgbase; + + if ( probe_sgalaxy( &ai )==0 ) + return -ENODEV; + + attach_sgalaxy( &ai ); + + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + unload_sgalaxy( &ai ); + SOUND_LOCK_END; +} + +#endif +#endif diff --git a/drivers/sound/softoss.c b/drivers/sound/softoss.c index 630668853..2a9302bbc 100644 --- a/drivers/sound/softoss.c +++ b/drivers/sound/softoss.c @@ -31,7 +31,7 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_SOFTOSS) || defined(MODULE) +#ifdef CONFIG_SOFTOSS #include "softoss.h" #include <linux/ultrasound.h> @@ -1015,7 +1015,7 @@ static int softsyn_open(int synthdev, int mode) if (devc->bits != 16 || devc->channels != 2) { audio_release((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo); -/* printk("SoftOSS: A 16 bit stereo soundcard is required\n");*/ +/* printk("SoftOSS: A 16 bit stereo sound card is required\n");*/ return -EINVAL; } if (devc->max_playahead >= audio_devs[devc->audiodev]->dmap_out->nbufs) diff --git a/drivers/sound/softoss_rs.c b/drivers/sound/softoss_rs.c index 17ebb8b59..ac9714ff5 100644 --- a/drivers/sound/softoss_rs.c +++ b/drivers/sound/softoss_rs.c @@ -18,7 +18,7 @@ #include "sound_config.h" -#if defined(CONFIG_SOFTOSS) || defined(MODULE) +#ifdef CONFIG_SOFTOSS #include "softoss.h" void softsynth_resample_loop(short *buf, int loops) diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c new file mode 100644 index 000000000..4f81fcdf3 --- /dev/null +++ b/drivers/sound/sonicvibes.c @@ -0,0 +1,2454 @@ +/*****************************************************************************/ + +/* + * sonicvibes.c -- S3 Sonic Vibes audio driver. + * + * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * none so far + * + * + * 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 + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.98 0.1 Initial release + * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in sv_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.98 0.4 Don't allow excessive interrupt rates + * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice + * + */ + +/*****************************************************************************/ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/modversions.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/sound.h> +#include <linux/malloc.h> +#include <linux/soundcard.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <asm/spinlock.h> +#include <asm/uaccess.h> +#include <asm/hardirq.h> + +#include "dm.h" + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES) + +#define SV_EXTENT_SB 0x10 +#define SV_EXTENT_ENH 0x10 +#define SV_EXTENT_SYNTH 0x4 +#define SV_EXTENT_MIDI 0x4 +#define SV_EXTENT_GAME 0x8 +#define SV_EXTENT_DMA 0x10 + + +#define SV_MIDI_DATA 0 +#define SV_MIDI_COMMAND 1 +#define SV_MIDI_STATUS 1 + +#define SV_DMA_ADDR0 0 +#define SV_DMA_ADDR1 1 +#define SV_DMA_ADDR2 2 +#define SV_DMA_ADDR3 3 +#define SV_DMA_COUNT0 4 +#define SV_DMA_COUNT1 5 +#define SV_DMA_COUNT2 6 +#define SV_DMA_MODE 0xb +#define SV_DMA_RESET 0xd +#define SV_DMA_MASK 0xf + +/* + * DONT reset the DMA controllers unless you understand + * the reset semantics. Assuming reset semantics as in + * the 8237 does not work. + */ + +#define DMA_MODE_AUTOINIT 0x10 +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ + +#define SV_CODEC_CONTROL 0 +#define SV_CODEC_INTMASK 1 +#define SV_CODEC_STATUS 2 +#define SV_CODEC_IADDR 4 +#define SV_CODEC_IDATA 5 + +#define SV_CCTRL_RESET 0x80 +#define SV_CCTRL_INTADRIVE 0x20 +#define SV_CCTRL_WAVETABLE 0x08 +#define SV_CCTRL_REVERB 0x04 +#define SV_CCTRL_ENHANCED 0x01 + +#define SV_CINTMASK_DMAA 0x01 +#define SV_CINTMASK_DMAC 0x04 +#define SV_CINTMASK_SPECIAL 0x08 +#define SV_CINTMASK_UPDOWN 0x40 +#define SV_CINTMASK_MIDI 0x80 + +#define SV_CSTAT_DMAA 0x01 +#define SV_CSTAT_DMAC 0x04 +#define SV_CSTAT_SPECIAL 0x08 +#define SV_CSTAT_UPDOWN 0x40 +#define SV_CSTAT_MIDI 0x80 + +#define SV_CIADDR_TRD 0x80 +#define SV_CIADDR_MCE 0x40 + +/* codec indirect registers */ +#define SV_CIMIX_ADCINL 0x00 +#define SV_CIMIX_ADCINR 0x01 +#define SV_CIMIX_AUX1INL 0x02 +#define SV_CIMIX_AUX1INR 0x03 +#define SV_CIMIX_CDINL 0x04 +#define SV_CIMIX_CDINR 0x05 +#define SV_CIMIX_LINEINL 0x06 +#define SV_CIMIX_LINEINR 0x07 +#define SV_CIMIX_MICIN 0x08 +#define SV_CIMIX_SYNTHINL 0x0A +#define SV_CIMIX_SYNTHINR 0x0B +#define SV_CIMIX_AUX2INL 0x0C +#define SV_CIMIX_AUX2INR 0x0D +#define SV_CIMIX_ANALOGINL 0x0E +#define SV_CIMIX_ANALOGINR 0x0F +#define SV_CIMIX_PCMINL 0x10 +#define SV_CIMIX_PCMINR 0x11 + +#define SV_CIGAMECONTROL 0x09 +#define SV_CIDATAFMT 0x12 +#define SV_CIENABLE 0x13 +#define SV_CIUPDOWN 0x14 +#define SV_CIREVISION 0x15 +#define SV_CIADCOUTPUT 0x16 +#define SV_CIDMAABASECOUNT1 0x18 +#define SV_CIDMAABASECOUNT0 0x19 +#define SV_CIDMACBASECOUNT1 0x1c +#define SV_CIDMACBASECOUNT0 0x1d +#define SV_CIPCMSR0 0x1e +#define SV_CIPCMSR1 0x1f +#define SV_CISYNTHSR0 0x20 +#define SV_CISYNTHSR1 0x21 +#define SV_CIADCCLKSOURCE 0x22 +#define SV_CIADCALTSR 0x23 +#define SV_CIADCPLLM 0x24 +#define SV_CIADCPLLN 0x25 +#define SV_CISYNTHPLLM 0x26 +#define SV_CISYNTHPLLN 0x27 +#define SV_CIUARTCONTROL 0x2a +#define SV_CIDRIVECONTROL 0x2b +#define SV_CISRSSPACE 0x2c +#define SV_CISRSCENTER 0x2d +#define SV_CIWAVETABLESRC 0x2e +#define SV_CIANALOGPWRDOWN 0x30 +#define SV_CIDIGITALPWRDOWN 0x31 + + +#define SV_CIMIX_ADCSRC_CD 0x20 +#define SV_CIMIX_ADCSRC_DAC 0x40 +#define SV_CIMIX_ADCSRC_AUX2 0x60 +#define SV_CIMIX_ADCSRC_LINE 0x80 +#define SV_CIMIX_ADCSRC_AUX1 0xa0 +#define SV_CIMIX_ADCSRC_MIC 0xc0 +#define SV_CIMIX_ADCSRC_MIXOUT 0xe0 +#define SV_CIMIX_ADCSRC_MASK 0xe0 + +#define SV_CFMT_STEREO 0x01 +#define SV_CFMT_16BIT 0x02 +#define SV_CFMT_MASK 0x03 +#define SV_CFMT_ASHIFT 0 +#define SV_CFMT_CSHIFT 4 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define SV_CENABLE_PPE 0x4 +#define SV_CENABLE_RE 0x2 +#define SV_CENABLE_PE 0x1 + + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 2 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +#define SND_DEV_DSP16 5 + +/* --------------------------------------------------------------------- */ + +struct sv_state { + /* magic */ + unsigned int magic; + + /* we keep sv cards in a linked list */ + struct sv_state *next; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned int iosb, ioenh, iosynth, iomidi, iogame, iodmaa, iodmac, irq; + + /* mixer stuff */ + struct { + unsigned int modcnt; + } mix; + + /* wave stuff */ + unsigned int rateadc, ratedac; + unsigned char fmt, enable; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + struct wait_queue *open_wait; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + struct wait_queue *wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + struct wait_queue *iwait; + struct wait_queue *owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; +}; + +/* --------------------------------------------------------------------- */ + +static struct sv_state *devs = NULL; +static unsigned long wavetable_mem = 0; + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +extern __inline__ unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* --------------------------------------------------------------------- */ + +/* + * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. + */ + +#undef DMABYTEIO + +static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa, u; + + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count--; + outl(addr, s->iodmaa + SV_DMA_ADDR0); + outl(count, s->iodmaa + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x18, s->iodmaa + SV_DMA_MODE); +} + +static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac, u; + + count >>= 1; + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count >>= 1; + count--; + outl(addr, s->iodmac + SV_DMA_ADDR0); + outl(count, s->iodmac + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x14, s->iodmac + SV_DMA_MODE); +} + +extern __inline__ unsigned get_dmaa(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return v + 1; +#else /* DMABYTEIO */ + return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1; +#endif /* DMABYTEIO */ +} + +extern __inline__ unsigned get_dmac(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return (v + 1) << 1; +#else /* DMABYTEIO */ + return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +#endif /* DMABYTEIO */ +} + +static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +static unsigned char rdindir(struct sv_state *s, unsigned char idx) +{ + unsigned char v; + + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + v = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + return v; +} + +static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR); + if (mask) { + s->fmt = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(0, s->ioenh + SV_CODEC_IADDR); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); +} + +static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +#define REFFREQUENCY 24576000 +#define ADCMULT 512 +#define FULLRATE 48000 + +static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) +{ + unsigned long flags; + unsigned char r, m, n; + unsigned xm, xn, xr, xd, metric = ~0U; + + if (rate < 625000/ADCMULT) + rate = 625000/ADCMULT; + if (rate > 150000000/ADCMULT) + rate = 150000000/ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 35; xn++) + for (xm = 3; xm < 130; xm++) { + xr = REFFREQUENCY/ADCMULT * xm / xn; + xd = abs((signed)(xr - rate)); + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(m, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(r | n, s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7); +} + +#if 0 + +static unsigned getpll(struct sv_state *s, unsigned char reg) +{ + unsigned long flags; + unsigned char m, n; + + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + m = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + n = inb(s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7); +} + +#endif + +static void set_dac_rate(struct sv_state *s, unsigned rate) +{ + unsigned div; + unsigned long flags; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + div = (rate * 65536 + FULLRATE/2) / FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIPCMSR1, div >> 8); + wrindir(s, SV_CIPCMSR0, div); + spin_unlock_irqrestore(&s->lock, flags); + s->ratedac = (div * FULLRATE + 32768) / 65536; +} + +static void set_adc_rate(struct sv_state *s, unsigned rate) +{ + unsigned long flags; + unsigned rate1, rate2, div; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + rate1 = setpll(s, SV_CIADCPLLM, rate); + div = (48000 + rate/2) / rate; + if (div > 8) + div = 8; + rate2 = (48000 + div/2) / div; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIADCALTSR, (div-1) << 4); + if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) { + wrindir(s, SV_CIADCCLKSOURCE, 0x10); + s->rateadc = rate2; + } else { + wrindir(s, SV_CIADCCLKSOURCE, 0x00); + s->rateadc = rate1; + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE); + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) + && s->dma_adc.ready) { + s->enable |= SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER 8 +#define DMABUF_MINORDER 1 + + +static void dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + + +/* DMAA is used for playback, DMAC is used for recording */ + +static int prog_dmabuf(struct sv_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + unsigned long map, mapend; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + s->enable &= ~SV_CENABLE_RE; + fmt >>= SV_CFMT_CSHIFT; + } else { + s->enable &= ~SV_CENABLE_PE; + fmt >>= SV_CFMT_ASHIFT; + } + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); + fmt &= SV_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order); + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + } + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + set_dmac(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1); + } else { + set_dmaa(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1); + } + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + return 0; +} + +extern __inline__ void clear_advance(struct sv_state *s) +{ + unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void sv_update_ptr(struct sv_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) { + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->enable &= ~SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_dac.error++; + } else if (s->dma_dac.count <= s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count < s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* hold spinlock for the following! */ +static void sv_handle_midi(struct sv_state *s) +{ + unsigned char ch; + int wake; + + wake = 0; + while (!(inb(s->iomidi+1) & 0x80)) { + ch = inb(s->iomidi); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->iomidi); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static void sv_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sv_state *s = (struct sv_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->ioenh + SV_CODEC_STATUS); + if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI))) + return; + spin_lock(&s->lock); + sv_update_ptr(s); + sv_handle_midi(s); + spin_unlock(&s->lock); +} + +static void sv_midi_timer(unsigned long data) +{ + struct sv_state *s = (struct sv_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SV_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 + +static const struct { + unsigned left:5; + unsigned right:5; + unsigned type:3; + unsigned rec:3; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL, SV_CIMIX_ADCINR, MT_4, 0 }, + [SOUND_MIXER_LINE1] = { SV_CIMIX_AUX1INL, SV_CIMIX_AUX1INR, MT_5MUTE, 5 }, + [SOUND_MIXER_CD] = { SV_CIMIX_CDINL, SV_CIMIX_CDINR, MT_5MUTE, 1 }, + [SOUND_MIXER_LINE] = { SV_CIMIX_LINEINL, SV_CIMIX_LINEINR, MT_5MUTE, 4 }, + [SOUND_MIXER_MIC] = { SV_CIMIX_MICIN, SV_CIMIX_ADCINL, MT_4MUTEMONO, 6 }, + [SOUND_MIXER_SYNTH] = { SV_CIMIX_SYNTHINL, SV_CIMIX_SYNTHINR, MT_5MUTE, 2 }, + [SOUND_MIXER_LINE2] = { SV_CIMIX_AUX2INL, SV_CIMIX_AUX2INR, MT_5MUTE, 3 }, + [SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE, 7 }, + [SOUND_MIXER_PCM] = { SV_CIMIX_PCMINL, SV_CIMIX_PCMINR, MT_6MUTE, 0 } +}; + +static int return_mixval(struct sv_state *s, unsigned i, int *arg) +{ + unsigned long flags; + unsigned char l, r, rl, rr; + + spin_lock_irqsave(&s->lock, flags); + l = rdindir(s, mixtable[i].left); + r = rdindir(s, mixtable[i].right); + spin_unlock_irqrestore(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + r &= 0xf; + l &= 0xf; + rl = 10 + 6 * (l & 15); + rr = 10 + 6 * (r & 15); + break; + + case MT_4MUTEMONO: + rl = 55 - 3 * (l & 15); + if (r & 0x10) + rl += 45; + rr = rl; + r = l; + break; + + case MT_5MUTE: + default: + rl = 100 - 3 * (l & 31); + rr = 100 - 3 * (r & 31); + break; + + case MT_6MUTE: + rl = 100 - 3 * (l & 63) / 2; + rr = 100 - 3 * (r & 63) / 2; + break; + } + if (l & 0x80) + rl = 0; + if (r & 0x80) + rr = 0; + return put_user((rr << 8) | rl, arg); +} + +static unsigned mixer_recmask(struct sv_state *s) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&s->lock, flags); + j = rdindir(s, SV_CIMIX_ADCINL) >> 5; + spin_unlock_irqrestore(&s->lock, flags); + j &= 7; + for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++); + return 1 << i; +} + +static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", 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; + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", 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 (cmd == SOUND_MIXER_PRIVATE1) { /* SRS settings */ + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val & 1) { + if (val & 2) { + l = 4 - ((val >> 2) & 7); + if (l & ~3) + l = 4; + r = 4 - ((val >> 5) & 7); + if (r & ~3) + r = 4; + wrindir(s, SV_CISRSSPACE, l); + wrindir(s, SV_CISRSCENTER, r); + } else + wrindir(s, SV_CISRSSPACE, 0x80); + } + l = rdindir(s, SV_CISRSSPACE); + r = rdindir(s, SV_CISRSCENTER); + spin_unlock_irqrestore(&s->lock, flags); + if (l & 0x80) + return put_user(0, (int *)arg); + return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, (int *)arg); + } + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, (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 || !mixtable[i].type) + return -EINVAL; + return return_mixval(s, i, (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s);*/ + else if (i > 1) + val &= ~mixer_recmask(s); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (mixtable[i].rec) + break; + } + if (!mixtable[i].rec) + return 0; + spin_lock_irqsave(&s->lock, flags); + frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5); + frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + l = val & 0xff; + r = (val >> 8) & 0xff; + if (mixtable[i].type == MT_4MUTEMONO) + l = (r + l) / 2; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rr = 0; + if (l < 10) + rl = 0x80; + else { + if (l >= 55) { + rr = 0x10; + l -= 45; + } + rl = (55 - l) / 3; + } + wrindir(s, mixtable[i].left, rl); + frobindir(s, mixtable[i].right, ~0x10, rr); + break; + + case MT_5MUTE: + if (l < 7) + rl = 0x80; + else + rl = (100 - l) / 3; + if (r < 7) + rr = 0x80; + else + rr = (100 - r) / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x80; + else + rl = (100 - l) * 2 / 3; + if (r < 6) + rr = 0x80; + else + rr = (100 - r) * 2 / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); + return return_mixval(s, i, (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static loff_t sv_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int sv_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_release_mixdev(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + +static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations sv_mixer_fops = { + &sv_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &sv_ioctl_mixdev, + NULL, /* mmap */ + &sv_open_mixdev, + &sv_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct sv_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "sv: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + return ret; +} + +static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.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 : -EBUSY; + interruptible_sleep_on(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + return ret; +} + +static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + if (file->f_flags & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_flags & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac.dmasize > s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int sv_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + vma->vm_file = file; + file->f_count++; + return 0; +} + +static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + 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: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + 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: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + 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(s, 0))) + 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->enable & SV_CENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + 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->enable & SV_CENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + 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: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + 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; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + 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)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.total_bytes >> 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)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + 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; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int sv_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + unsigned char fmtm = ~0, fmts = 0; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_flags & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(&s->dma_dac); + } + if (file->f_flags & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations sv_audio_fops = { + &sv_llseek, + &sv_read, + &sv_write, + NULL, /* readdir */ + &sv_poll, + &sv_ioctl, + &sv_mmap, + &sv_open, + &sv_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + +static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int sv_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + //outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL); + outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */ + wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */ + outb(0xff, s->iomidi+1); /* reset command */ + outb(0x3f, s->iomidi+1); /* uart command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = sv_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_midi_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + struct wait_queue wait = { current, NULL }; + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + current->timeout = tmo ? jiffies + tmo : 0; + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "sv: midi timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations sv_midi_fops = { + &sv_llseek, + &sv_midi_read, + &sv_midi_write, + NULL, /* readdir */ + &sv_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &sv_midi_open, + &sv_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct sv_state *s = (struct sv_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->iosynth + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->iosynth); + outb((p.kbd_split & 1) << 6, s->iosynth+1); + outb(0xbd, s->iosynth); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->iosynth+2); + outb(arg, s->iosynth+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->iosynth+2); + outb(arg & 1, s->iosynth+3); + return 0; + + default: + return -EINVAL; + } +} + +static int sv_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + + while (s && s->dev_dmfm != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + /* init the stuff */ + outb(1, s->iosynth); + outb(0x20, s->iosynth+1); /* enable waveforms */ + outb(4, s->iosynth+2); + outb(0, s->iosynth+3); /* no 4op enabled */ + outb(5, s->iosynth+2); + outb(1, s->iosynth+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_dmfm_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations sv_dmfm_fops = { + &sv_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &sv_dmfm_ioctl, + NULL, /* mmap */ + &sv_dmfm_open, + &sv_dmfm_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices */ +#define NR_DEVICE 5 + +static int reverb[NR_DEVICE] = { 0, }; +static int wavetable[NR_DEVICE] = { 0, }; + +static unsigned dmaio = 0xac00; + +/* --------------------------------------------------------------------- */ + +static const struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 } +}; + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_sonicvibes(void)) +#endif +{ + struct sv_state *s; + struct pci_dev *pcidev = NULL; + mm_segment_t fs; + int i, val, index = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "sv: version v0.5 time " __TIME__ " " __DATE__ "\n"); +#if 0 + if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) + printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); +#endif + while (index < NR_DEVICE && + (pcidev = pci_find_device(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, pcidev))) { + if (pcidev->base_address[1] == 0 || + (pcidev->base_address[1] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->base_address[2] == 0 || + (pcidev->base_address[2] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->base_address[3] == 0 || + (pcidev->base_address[3] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->irq == 0) + continue; + if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { + printk(KERN_WARNING "sv: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct sv_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = SV_MAGIC; + s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->ioenh = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + s->iosynth = pcidev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK; + s->iomidi = pcidev->base_address[3] & PCI_BASE_ADDRESS_IO_MASK; + s->iogame = pcidev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK; + pci_read_config_dword(pcidev, 0x40, &s->iodmaa); + pci_read_config_dword(pcidev, 0x48, &s->iodmac); + dmaio &= ~(SV_EXTENT_DMA-1); + s->iodmaa &= ~(SV_EXTENT_DMA-1); + s->iodmac &= ~(SV_EXTENT_DMA-1); + if (!(s->iodmaa)) { + s->iodmaa = dmaio; + dmaio += SV_EXTENT_DMA; + printk(KERN_INFO "sv: BIOS did not allocate DDMA channel A io, allocated at %#x\n", + s->iodmaa); + } + if (!(s->iodmac)) { + s->iodmac = dmaio; + dmaio += SV_EXTENT_DMA; + printk(KERN_INFO "sv: BIOS did not allocate DDMA channel C io, allocated at %#x\n", + s->iodmac); + } + pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9); /* enable and use extended mode */ + pci_write_config_dword(pcidev, 0x48, s->iodmac | 9); /* enable */ + printk(KERN_DEBUG "sv: io ports: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + s->iosb, s->ioenh, s->iosynth, s->iomidi, s->iogame, s->iodmaa, s->iodmac); + if (s->ioenh == 0 || s->iodmaa == 0 || s->iodmac == 0) + continue; + s->irq = pcidev->irq; + + /* hack */ + pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ + + + if (check_region(s->ioenh, SV_EXTENT_ENH)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); + goto err_region5; + } + request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM"); + if (check_region(s->iodmaa, SV_EXTENT_DMA)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); + goto err_region4; + } + request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA"); + if (check_region(s->iodmac, SV_EXTENT_DMA)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); + goto err_region3; + } + request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC"); + if (check_region(s->iomidi, SV_EXTENT_MIDI)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); + goto err_region2; + } + request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi"); + if (check_region(s->iosynth, SV_EXTENT_SYNTH)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); + goto err_region1; + } + request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth"); + /* initialize codec registers */ + outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ + udelay(50); + outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ + udelay(50); + outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE | SV_CCTRL_REVERB*/, + s->ioenh + SV_CODEC_CONTROL); + inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ + wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ + wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ + outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); + //outb(0xff, s->iodmaa + SV_DMA_RESET); + //outb(0xff, s->iodmac + SV_DMA_RESET); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ + wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ + wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ + setpll(s, SV_CIADCPLLM, 8000); + wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ + wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); + wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); + wrindir(s, SV_CIADCOUTPUT, 0); + /* request irq */ + if (request_irq(s->irq, sv_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", s)) { + printk(KERN_ERR "sv: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "sv: found adapter at io %#06x irq %u dmaa %#06x dmac %#06x revision %u\n", + s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&sv_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_midi = register_sound_midi(&sv_midi_fops)) < 0) + goto err_dev3; + if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) + goto err_dev4; + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + index++; + continue; + + err_dev4: + unregister_sound_midi(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "sv: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->iosynth, SV_EXTENT_SYNTH); + err_region1: + release_region(s->iomidi, SV_EXTENT_MIDI); + err_region2: + release_region(s->iodmac, SV_EXTENT_DMA); + err_region3: + release_region(s->iodmaa, SV_EXTENT_DMA); + err_region4: + release_region(s->ioenh, SV_EXTENT_ENH); + err_region5: + kfree_s(s, sizeof(struct sv_state)); + } + if (!devs) { + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + return -ENODEV; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(reverb, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(wavetable, "if 1 the LINE input is converted to LINE out"); + +MODULE_PARM(dmaio, "i"); +MODULE_PARM_DESC(dmaio, "if the motherboard BIOS did not allocate DDMA io, allocate them starting at this address"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("S3 SonicVibes Driver"); + +void cleanup_module(void) +{ + struct sv_state *s; + + while ((s = devs)) { + devs = devs->next; + outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */ + synchronize_irq(); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ + //outb(0, s->iodmaa + SV_DMA_RESET); + //outb(0, s->iodmac + SV_DMA_RESET); + free_irq(s->irq, s); + release_region(s->iodmac, SV_EXTENT_DMA); + release_region(s->iodmaa, SV_EXTENT_DMA); + release_region(s->ioenh, SV_EXTENT_ENH); + release_region(s->iomidi, SV_EXTENT_MIDI); + release_region(s->iosynth, SV_EXTENT_SYNTH); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree_s(s, sizeof(struct sv_state)); + } + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + printk(KERN_INFO "sv: unloading\n"); +} + +#endif /* MODULE */ diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h index 2a95950e5..a5d2272b4 100644 --- a/drivers/sound/sound_calls.h +++ b/drivers/sound/sound_calls.h @@ -272,3 +272,39 @@ void unload_v_midi (struct address_info *hw_config); void attach_vidc(struct address_info *hw_config); int probe_vidc(struct address_info *hw_config); void unload_vidc(struct address_info *hw_config); + +/* From wavefront.c */ +void attach_wavefront (struct address_info *hw_config); +int probe_wavefront (struct address_info *hw_config); +void unload_wavefront (struct address_info *hw_config); + +/* From wf_midi.c */ +void attach_wf_mpu(struct address_info * hw_config); +int probe_wf_mpu(struct address_info *hw_config); +void unload_wf_mpu(struct address_info *hw_config); +int virtual_midi_enable (int mididev, struct address_info *); +void virtual_midi_disable (int mididev); + +/* From wavefront.c */ +void attach_wavefront (struct address_info *hw_config); +int probe_wavefront (struct address_info *hw_config); +void unload_wavefront (struct address_info *hw_config); + +/* From wf_midi.c */ +void attach_wf_mpu(struct address_info * hw_config); +int probe_wf_mpu(struct address_info *hw_config); +void unload_wf_mpu(struct address_info *hw_config); +int virtual_midi_enable (int mididev, struct address_info *); +void virtual_midi_disable (int mididev); + +/* From wavefront.c */ +void attach_wavefront (struct address_info *hw_config); +int probe_wavefront (struct address_info *hw_config); +void unload_wavefront (struct address_info *hw_config); + +/* From wf_midi.c */ +void attach_wf_mpu(struct address_info * hw_config); +int probe_wf_mpu(struct address_info *hw_config); +void unload_wf_mpu(struct address_info *hw_config); +int virtual_midi_enable (int mididev, struct address_info *); +void virtual_midi_disable (int mididev); diff --git a/drivers/sound/sound_config.h b/drivers/sound/sound_config.h index d158a48ff..805de62dc 100644 --- a/drivers/sound/sound_config.h +++ b/drivers/sound/sound_config.h @@ -1,6 +1,6 @@ /* sound_config.h * - * A driver for Soundcards, misc configuration parameters. + * A driver for sound cards, misc. configuration parameters. */ /* * Copyright (C) by Hannu Savolainen 1993-1997 @@ -10,14 +10,19 @@ * for more info. */ -#include <linux/fs.h> -#include "local.h.master" + +#ifndef _SOUND_CONFIG_H_ +#define _SOUND_CONFIG_H_ + #include <linux/config.h> +#include <linux/fs.h> +#include <linux/sound.h> + +#include "legacy.h" #include "os.h" #include "soundvers.h" - #ifndef SND_DEFAULT_ENABLE #define SND_DEFAULT_ENABLE 1 #endif @@ -181,3 +186,5 @@ extern __inline__ int translate_mode(struct file *file) #define TIMER_ARMED 121234 #define TIMER_NOT_ARMED 1 + +#endif diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c new file mode 100644 index 000000000..95ef40a0f --- /dev/null +++ b/drivers/sound/sound_core.c @@ -0,0 +1,341 @@ +/* + * Sound core handling. Breaks out sound functions to submodules + * + * Author: Alan Cox <alan.cox@linux.org> + * + * Fixes: + * + * + * 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. + * + * -------------------- + * + * Top level handler for the sound subsystem. Various devices can + * plug into this. The fact they dont all go via OSS doesn't mean + * they don't have to implement the OSS API. There is a lot of logic + * to keeping much of the OSS weight out of the code in a compatibility + * module, but its up to the driver to rember to load it... + * + * The code provides a set of functions for registration of devices + * by type. This is done rather than providing a single call so that + * we can hide any future changes in the internals (eg when we go to + * 32bit dev_t) from the modules and their interface. + * + * Secondly we need to allocate the dsp, dsp16 and audio devices as + * one. Thus we misuse the chains a bit to simplify this. + * + * Thirdly to make it more fun and for 2.3.x and above we do all + * of this using fine grained locking. + * + * FIXME: we have to resolve modules and fine grained load/unload + * locking at some point in 2.3.x. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/fs.h> +#include <linux/sound.h> +#include <linux/major.h> + + +struct sound_unit +{ + int unit_minor; + struct file_operations *unit_fops; + struct sound_unit *next; +}; + +/* + * Low level list operator. Scan the ordered list, find a hole and + * join into it. Called with the lock asserted + */ + +static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int low, int top) +{ + int n=low; + + while(n<top) + { + /* Found a hole ? */ + if(*list==NULL || (*list)->unit_minor>n) + break; + list=&((*list)->next); + n+=16; + } + + if(n==top) + { + return -1; + } + + + /* + * Fill it in + */ + + s->unit_minor=n; + s->unit_fops=fops; + + /* + * Link it + */ + + s->next=*list; + *list=s; + + + MOD_INC_USE_COUNT; + return n; +} + +/* + * Remove a node from the chain. Called with the lock asserted + */ + +static void __sound_remove_unit(struct sound_unit **list, int unit) +{ + while(*list) + { + struct sound_unit *p=*list; + if(p->unit_minor==unit) + { + *list=p->next; + kfree(p); + MOD_DEC_USE_COUNT; + return; + } + list=&(p->next); + } + printk(KERN_ERR "Sound device %d went missing!\n", unit); +} + +/* + * This lock guards the sound loader list. + */ + +static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED; + +/* + * Allocate the controlling structure and add it to the sound driver + * list. Acquires locks as needed + */ + +static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int low, int top) +{ + int r; + struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL); + if(s==NULL) + return -1; + + spin_lock(&sound_loader_lock); + r=__sound_insert_unit(s,list,fops,low,top); + spin_unlock(&sound_loader_lock); + + if(r==-1) + kfree(s); + return r; +} + +/* + * Remove a unit. Acquires locks as needed. The drivers MUST have + * completed the removal before their file operations become + * invalid. + */ + +static void sound_remove_unit(struct sound_unit **list, int unit) +{ + spin_lock(&sound_loader_lock); + __sound_remove_unit(list, unit); + spin_unlock(&sound_loader_lock); +} + +/* + * Allocations + * + * 0 *16 Mixers + * 1 *8 Sequencers + * 2 *16 Midi + * 3 *16 DSP + * 4 *16 SunDSP + * 5 *16 DSP16 + * 6 -- sndstat (obsolete) + * 7 *16 unused + * 8 -- alternate sequencer (see above) + * 9 *16 unused + * 10 *16 unused + * 11 *16 unused + * 12 *16 unused + * 13 *16 unused + * 14 *16 unused + * 15 *16 unused + */ + +static struct sound_unit *chains[16]; + +int register_sound_special(struct file_operations *fops, int unit) +{ + return sound_insert_unit(&chains[unit&15], fops, unit, unit+1); +} + +EXPORT_SYMBOL(register_sound_special); + +int register_sound_mixer(struct file_operations *fops) +{ + return sound_insert_unit(&chains[0], fops, 0, 128); +} + +EXPORT_SYMBOL(register_sound_mixer); + +int register_sound_midi(struct file_operations *fops) +{ + return sound_insert_unit(&chains[2], fops, 2, 130); +} + +EXPORT_SYMBOL(register_sound_midi); + +/* + * DSP's are registered as a triple. Register only one and cheat + * in open - see below. + */ + +int register_sound_dsp(struct file_operations *fops) +{ + return sound_insert_unit(&chains[3], fops, 3, 131); +} + +EXPORT_SYMBOL(register_sound_dsp); + +void unregister_sound_special(int unit) +{ + sound_remove_unit(&chains[unit&15], unit); +} + +EXPORT_SYMBOL(unregister_sound_special); + +void unregister_sound_mixer(int unit) +{ + sound_remove_unit(&chains[0], unit); +} + +EXPORT_SYMBOL(unregister_sound_mixer); + +void unregister_sound_midi(int unit) +{ + return sound_remove_unit(&chains[2], unit); +} + +EXPORT_SYMBOL(unregister_sound_midi); + +void unregister_sound_dsp(int unit) +{ + return sound_remove_unit(&chains[3], unit); +} + +EXPORT_SYMBOL(unregister_sound_dsp); + + +/* + * Now our file operations + */ + +static int soundcore_open(struct inode *, struct file *); + +static struct file_operations soundcore_fops= +{ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + soundcore_open, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +int soundcore_open(struct inode *inode, struct file *file) +{ + int chain; + int unit=MINOR(inode->i_rdev); + struct sound_unit *s; + + chain=unit&0x0F; + if(chain==4 || chain==5) /* dsp/audio/dsp16 */ + { + unit&=0xF0; + unit|=3; + chain=3; + } + + spin_lock(&sound_loader_lock); + + s=chains[chain]; + + while(s && s->unit_minor <= unit) + { + if(s->unit_minor==unit) + { + file->f_op=s->unit_fops; + spin_unlock(&sound_loader_lock); + if(file->f_op->open) + return file->f_op->open(inode,file); + else + return 0; + break; + } + s=s->next; + } + spin_unlock(&sound_loader_lock); + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + /* We have nothing to really do here - we know the lists must be + empty */ + unregister_chrdev(SOUND_MAJOR, "sound"); +} + +int init_module(void) +#else +int soundcore_init(void) +#endif +{ + if(register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) + { + printk(KERN_ERR "soundcore: sound device already in use.\n"); + return -EBUSY; + } + /* + * Now init non OSS drivers + */ +#ifdef CONFIG_SOUND_SONICVIBES + init_sonicvibes(); +#endif +#ifdef CONFIG_SOUND_ES1370 + init_es1370(); +#endif +#ifdef CONFIG_SOUND_ES1371 + init_es1371(); +#endif +#ifdef CONFIG_SOUND_MSNDCLAS + msnd_classic_init(); +#endif +#ifdef CONFIG_SOUND_MSNDPIN + msnd_pinnacle_init(); +#endif + return 0; +} diff --git a/drivers/sound/sound_firmware.c b/drivers/sound/sound_firmware.c index 97ce9d657..f4777addf 100644 --- a/drivers/sound/sound_firmware.c +++ b/drivers/sound/sound_firmware.c @@ -1,5 +1,6 @@ #include "os.h" #define __KERNEL_SYSCALLS__ +#include <linux/module.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/malloc.h> @@ -57,3 +58,4 @@ int mod_firmware_load(const char *fn, char **fp) return r; } +EXPORT_SYMBOL(mod_firmware_load); diff --git a/drivers/sound/sound_syms.c b/drivers/sound/sound_syms.c index dd06d250a..08f092e90 100644 --- a/drivers/sound/sound_syms.c +++ b/drivers/sound/sound_syms.c @@ -7,28 +7,22 @@ #include <linux/module.h> #include "sound_config.h" -#define _MIDI_SYNTH_C_ -#include "midi_synth.h" -#define _SEQUENCER_C_ -#include "tuning.h" -#include <linux/notifier.h> -#include "sound_firmware.h" - -extern struct notifier_block *sound_locker; -extern void sound_notifier_chain_register(struct notifier_block *); +#include "sound_calls.h" +char sound_syms_symbol; EXPORT_SYMBOL(mixer_devs); EXPORT_SYMBOL(audio_devs); EXPORT_SYMBOL(num_mixers); EXPORT_SYMBOL(num_audiodevs); -EXPORT_SYMBOL(note_to_freq); -EXPORT_SYMBOL(compute_finetune); -EXPORT_SYMBOL(seq_copy_to_input); -EXPORT_SYMBOL(seq_input_event); -EXPORT_SYMBOL(sequencer_init); -EXPORT_SYMBOL(sequencer_timer); +EXPORT_SYMBOL(midi_devs); +EXPORT_SYMBOL(num_midis); +EXPORT_SYMBOL(synth_devs); +EXPORT_SYMBOL(num_synths); + +EXPORT_SYMBOL(sound_timer_devs); +EXPORT_SYMBOL(num_sound_timers); EXPORT_SYMBOL(sound_install_audiodrv); EXPORT_SYMBOL(sound_install_mixer); @@ -49,56 +43,16 @@ EXPORT_SYMBOL(sound_unload_synthdev); EXPORT_SYMBOL(load_mixer_volumes); -EXPORT_SYMBOL(DMAbuf_start_dma); -EXPORT_SYMBOL(DMAbuf_open_dma); -EXPORT_SYMBOL(DMAbuf_close_dma); -EXPORT_SYMBOL(DMAbuf_inputintr); -EXPORT_SYMBOL(DMAbuf_outputintr); -EXPORT_SYMBOL(dma_ioctl); - EXPORT_SYMBOL(conf_printf); EXPORT_SYMBOL(conf_printf2); -EXPORT_SYMBOL(sound_timer_init); -EXPORT_SYMBOL(sound_timer_interrupt); -EXPORT_SYMBOL(sound_timer_syncinterval); -EXPORT_SYMBOL(sound_timer_devs); +extern int softoss_dev; +EXPORT_SYMBOL(softoss_dev); /* Locking */ +#include "soundmodule.h" EXPORT_SYMBOL(sound_locker); EXPORT_SYMBOL(sound_notifier_chain_register); -/* MIDI symbols */ -EXPORT_SYMBOL(midi_devs); -EXPORT_SYMBOL(num_midis); -EXPORT_SYMBOL(synth_devs); -EXPORT_SYMBOL(num_synths); - -EXPORT_SYMBOL(do_midi_msg); -EXPORT_SYMBOL(midi_synth_open); -EXPORT_SYMBOL(midi_synth_close); -EXPORT_SYMBOL(midi_synth_ioctl); -EXPORT_SYMBOL(midi_synth_kill_note); -EXPORT_SYMBOL(midi_synth_start_note); -EXPORT_SYMBOL(midi_synth_set_instr); -EXPORT_SYMBOL(midi_synth_reset); -EXPORT_SYMBOL(midi_synth_hw_control); -EXPORT_SYMBOL(midi_synth_aftertouch); -EXPORT_SYMBOL(midi_synth_controller); -EXPORT_SYMBOL(midi_synth_panning); -EXPORT_SYMBOL(midi_synth_setup_voice); -EXPORT_SYMBOL(midi_synth_send_sysex); -EXPORT_SYMBOL(midi_synth_bender); -EXPORT_SYMBOL(midi_synth_load_patch); - -/* Firmware */ - -EXPORT_SYMBOL(mod_firmware_load); - -/* Tuning */ - -EXPORT_SYMBOL(cent_tuning); -EXPORT_SYMBOL(semitone_tuning); - MODULE_DESCRIPTION("Sound subsystem"); MODULE_AUTHOR("Hannu Savolainen, et al."); diff --git a/drivers/sound/sound_timer.c b/drivers/sound/sound_timer.c index 986668316..8033437f1 100644 --- a/drivers/sound/sound_timer.c +++ b/drivers/sound/sound_timer.c @@ -16,7 +16,7 @@ #include "sound_config.h" -#if defined(CONFIG_SEQUENCER) || defined(CONFIG_SEQUENCER_MODULE) +#if defined(CONFIG_SEQUENCER) static volatile int initialized = 0, opened = 0, tmr_running = 0; static volatile time_t tmr_offs, tmr_ctr; diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index 42ea158cc..2abd7fa22 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -1,7 +1,7 @@ /* * linux/drivers/sound/soundcard.c * - * Soundcard driver for Linux + * Sound card driver for Linux */ /* * Copyright (C) by Hannu Savolainen 1993-1997 @@ -38,11 +38,10 @@ #include <linux/delay.h> #include <linux/proc_fs.h> -#define SOUND_CORE - #include "soundmodule.h" +struct notifier_block *sound_locker=(struct notifier_block *)0; +static int lock_depth = 0; -#include <linux/major.h> #ifdef MODULE #define modular 1 #else @@ -57,8 +56,6 @@ #endif static int chrdev_registered = 0; -static int sound_major = SOUND_MAJOR; - static int is_unloading = 0; /* @@ -92,17 +89,19 @@ int *load_mixer_volumes(char *name, int *levels, int present) int i, n; for (i = 0; i < num_mixer_volumes; i++) + { if (strcmp(name, mixer_vols[i].name) == 0) - { - if (present) - mixer_vols[i].num = i; - return mixer_vols[i].levels; - } + { + if (present) + mixer_vols[i].num = i; + return mixer_vols[i].levels; + } + } if (num_mixer_volumes >= MAX_MIXER_DEV) - { - printk("Sound: Too many mixers (%s)\n", name); - return levels; - } + { + printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); + return levels; + } n = num_mixer_volumes++; strcpy(mixer_vols[n].name, name); @@ -154,13 +153,16 @@ static int sound_proc_get_info(char *buffer, char **start, off_t offset, int len #else #define MODULEPROCSTRING "Driver compiled into kernel" #endif - + + down(&uts_sem); + len = sprintf(buffer, "OSS/Free:" SOUND_VERSION_STRING "\n" "Load type: " MODULEPROCSTRING "\n" "Kernel: %s %s %s %s %s\n" "Config options: %x\n\nInstalled drivers: \n", system_utsname.sysname, system_utsname.nodename, system_utsname.release, system_utsname.version, system_utsname.machine, SELECTED_SOUND_OPTIONS); + up(&uts_sem); for (i = 0; (i < num_sound_drivers) && (pos <= offset + length); i++) { if (!sound_drivers[i].card_type) @@ -175,7 +177,8 @@ static int sound_proc_get_info(char *buffer, char **start, off_t offset, int len } len += sprintf(buffer + len, "\nCard config: \n"); - for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) { + for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) + { if (!snd_installed_cards[i].card_type) continue; if (!snd_installed_cards[i].enabled) @@ -432,12 +435,12 @@ static int sound_open(struct inode *inode, struct file *file) } dev = MINOR(inode->i_rdev); if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) { - /* printk("SoundCard Error: The soundcard system has not been configured\n");*/ + /* printk("SoundCard Error: The sound system has not been configured\n");*/ return -ENXIO; } DEB(printk("sound_open(dev=%d)\n", dev)); if ((dev >= SND_NDEVS) || (dev < 0)) { - printk(KERN_ERR "Invalid minor device %d\n", dev); + /* printk(KERN_ERR "Invalid minor device %d\n", dev);*/ return -ENXIO; } switch (dev & 0x0f) { @@ -447,11 +450,11 @@ static int sound_open(struct inode *inode, struct file *file) case SND_DEV_CTL: dev >>= 4; #ifdef CONFIG_KMOD - if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { - char modname[20]; - sprintf(modname, "mixer%d", dev); - request_module(modname); - } + if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { + char modname[20]; + sprintf(modname, "mixer%d", dev); + request_module(modname); + } #endif if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) return -ENXIO; @@ -486,9 +489,12 @@ static int sound_open(struct inode *inode, struct file *file) return -ENXIO; } in_use++; -#ifdef MODULE - SOUND_INC_USE_COUNT; + +#ifdef CONFIG_MODULES + notifier_call_chain(&sound_locker, 1, 0); + lock_depth++; #endif + return 0; } @@ -527,9 +533,12 @@ static int sound_release(struct inode *inode, struct file *file) printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); } in_use--; -#ifdef MODULE - SOUND_DEC_USE_COUNT; + +#ifdef CONFIG_MODULES + notifier_call_chain(&sound_locker, 0, 0); + lock_depth--; #endif + return 0; } @@ -662,18 +671,18 @@ static unsigned int sound_poll(struct file *file, poll_table * wait) DEB(printk("sound_poll(dev=%d)\n", dev)); switch (dev & 0x0f) { -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_poll(dev, file, wait); #endif -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI case SND_DEV_MIDIN: return MIDIbuf_poll(dev, file, wait); #endif -#if defined(CONFIG_AUDIO) || defined(MODULE) +#ifdef CONFIG_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: @@ -750,7 +759,7 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma) return 0; } -static struct file_operations sound_fops = +struct file_operations oss_sound_fops = { sound_lseek, sound_read, @@ -763,6 +772,36 @@ static struct file_operations sound_fops = sound_release }; +/* + * Create the required special subdevices + */ + +static int create_special_devices(void) +{ + int seq1,seq2; + int sndstat=register_sound_special(&oss_sound_fops, 6); + if(sndstat==-1) + goto bad1; + seq1=register_sound_special(&oss_sound_fops, 1); + if(seq1==-1) + goto bad2; + seq2=register_sound_special(&oss_sound_fops, 8); + if(seq2!=-1) + return 0; + unregister_sound_special(1); +bad2: + unregister_sound_special(6); +bad1: + return -1; +} + +static void destroy_special_devices(void) +{ + unregister_sound_special(6); + unregister_sound_special(1); + unregister_sound_special(8); +} + #ifdef MODULE static void #else @@ -770,8 +809,14 @@ void #endif soundcard_init(void) { + /* drag in sound_syms.o */ + { + extern char sound_syms_symbol; + sound_syms_symbol = 0; + } + #ifndef MODULE - register_chrdev(sound_major, "sound", &sound_fops); + create_special_devices(); chrdev_registered = 1; #endif @@ -785,20 +830,22 @@ soundcard_init(void) return; /* No cards detected */ #endif -#if defined(CONFIG_AUDIO) +#ifdef CONFIG_AUDIO if (num_audiodevs || modular) /* Audio devices present */ { audio_init_devices(); } #endif - - + if (proc_register(&proc_root, &proc_root_sound)) + printk(KERN_ERR "sound: registering /proc/sound failed\n"); } static int sound[20] = { 0 }; +#ifdef MODULE + int init_module(void) { int err; @@ -816,7 +863,7 @@ int init_module(void) if (i) sound_setup("sound=", ints); - err = register_chrdev(sound_major, "sound", &sound_fops); + err = create_special_devices(); if (err) { printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); @@ -828,14 +875,9 @@ int init_module(void) if (sound_nblocks >= 1024) printk(KERN_ERR "Sound warning: Deallocation table was too small.\n"); - if (proc_register(&proc_root, &proc_root_sound)) - printk(KERN_ERR "sound: registering /proc/sound failed\n"); return 0; } -#ifdef MODULE - - void cleanup_module(void) { int i; @@ -847,9 +889,9 @@ void cleanup_module(void) if (proc_unregister(&proc_root, PROC_SOUND)) printk(KERN_ERR "sound: unregistering /proc/sound failed\n"); if (chrdev_registered) - unregister_chrdev(sound_major, "sound"); + destroy_special_devices(); -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER sound_stop_timer(); #endif @@ -942,7 +984,7 @@ void sound_close_dma(int chn) restore_flags(flags); } -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER static void do_sequencer_timer(unsigned long dummy) { @@ -1022,12 +1064,6 @@ void conf_printf2(char *name, int base, int irq, int dma, int dma2) * Module and lock management */ -struct notifier_block *sound_locker=(struct notifier_block *)0; -static int lock_depth = 0; - -#define SOUND_INC_USE_COUNT do { notifier_call_chain(&sound_locker, 1, 0); lock_depth++; } while(0); -#define SOUND_DEC_USE_COUNT do { notifier_call_chain(&sound_locker, 0, 0); lock_depth--; } while(0); - /* * When a sound module is registered we need to bring it to the current * lock level... diff --git a/drivers/sound/soundmodule.h b/drivers/sound/soundmodule.h index dac6316bb..b89e74e51 100644 --- a/drivers/sound/soundmodule.h +++ b/drivers/sound/soundmodule.h @@ -5,23 +5,12 @@ extern struct notifier_block *sound_locker; extern void sound_notifier_chain_register(struct notifier_block *); -extern int lock_depth; #ifdef MODULE -#ifdef SOUND_CORE - -#define SOUND_INC_USE_COUNT do { notifier_call_chain(&sound_locker, 1, 0); lock_depth++; } while(0); -#define SOUND_DEC_USE_COUNT do { notifier_call_chain(&sound_locker, 0, 0); lock_depth--; } while(0); - -#else - - #define SOUND_LOCK sound_notifier_chain_register(&sound_notifier); #define SOUND_LOCK_END notifier_chain_unregister(&sound_locker, &sound_notifier) - - static int my_notifier_call(struct notifier_block *b, unsigned long foo, void *bar) { if(foo) @@ -40,4 +29,3 @@ static struct notifier_block sound_notifier= #endif #endif -#endif diff --git a/drivers/sound/sscape.c b/drivers/sound/sscape.c index 2da737c5c..1b823692c 100644 --- a/drivers/sound/sscape.c +++ b/drivers/sound/sscape.c @@ -22,7 +22,7 @@ #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_SSCAPE) || defined(MODULE) +#ifdef CONFIG_SSCAPE #include "coproc.h" diff --git a/drivers/sound/sys_timer.c b/drivers/sound/sys_timer.c index 4d0456dd1..2e74b6055 100644 --- a/drivers/sound/sys_timer.c +++ b/drivers/sound/sys_timer.c @@ -19,7 +19,7 @@ #include "sound_config.h" -#if defined(CONFIG_SEQUENCER) || defined(MODULE) +#ifdef CONFIG_SEQUENCER static volatile int opened = 0, tmr_running = 0; static volatile time_t tmr_offs, tmr_ctr; diff --git a/drivers/sound/trix.c b/drivers/sound/trix.c index 1c5820fcd..1ed1b8d78 100644 --- a/drivers/sound/trix.c +++ b/drivers/sound/trix.c @@ -23,15 +23,9 @@ #include "sb.h" #include "sound_firmware.h" -#if defined(CONFIG_TRIX) || defined (MODULE) +#ifdef CONFIG_TRIX -#if defined(CONFIG_UART401) || defined(CONFIG_UART401_MODULE) -#if defined(CONFIG_MIDI) -#define DO_MIDI -#endif -#endif - -#ifdef CONFIG_TRIX_HAVE_BOOT +#ifdef INCLUDE_TRIX_BOOT #include "trix_boot.h" #else static unsigned char *trix_boot = NULL; @@ -360,7 +354,7 @@ void attach_trix_mpu(struct address_info *hw_config) int probe_trix_mpu(struct address_info *hw_config) { -#ifdef DO_MIDI +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) unsigned char conf; static char irq_bits[] = { -1, -1, -1, 1, 2, 3, -1, 4, -1, 5 @@ -443,7 +437,7 @@ void unload_trix_wss(struct address_info *hw_config) void unload_trix_mpu(struct address_info *hw_config) { -#ifdef DO_MIDI +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) unload_uart401(hw_config); #endif } diff --git a/drivers/sound/uart401.c b/drivers/sound/uart401.c index 4d8857e56..7ccc65a4f 100644 --- a/drivers/sound/uart401.c +++ b/drivers/sound/uart401.c @@ -25,7 +25,8 @@ #include "sound_config.h" #include "soundmodule.h" -#if (defined(CONFIG_UART401)||defined(CONFIG_MIDI)) || defined(MODULE) +#ifdef CONFIG_UART401 +#ifdef CONFIG_MIDI typedef struct uart401_devc { @@ -80,7 +81,9 @@ static void enter_uart_mode(uart401_devc * devc); static void uart401_input_loop(uart401_devc * devc) { - while (input_avail(devc)) + int work_limit=30000; + + while (input_avail(devc) && --work_limit) { unsigned char c = uart401_read(devc); @@ -89,17 +92,19 @@ static void uart401_input_loop(uart401_devc * devc) 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); } void uart401intr(int irq, void *dev_id, struct pt_regs *dummy) { uart401_devc *devc = dev_id; - if (irq < 1 || irq > 15) - return; - if (devc == NULL) + { + printk(KERN_ERR "uart401: bad devc\n"); return; + } if (input_avail(devc)) uart401_input_loop(devc); @@ -114,9 +119,10 @@ uart401_open(int dev, int mode, uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; if (devc->opened) - { return -EBUSY; - } + + /* Flush the UART */ + while (input_avail(devc)) uart401_read(devc); @@ -310,9 +316,9 @@ void attach_uart401(struct address_info *hw_config) if (midi_devs[devc->my_dev]->converter == NULL) { printk(KERN_WARNING "uart401: Failed to allocate memory\n"); - sound_unload_mididev(devc->my_dev); kfree(midi_devs[devc->my_dev]); kfree(devc); + sound_unload_mididev(devc->my_dev); devc=NULL; return; } @@ -431,7 +437,6 @@ void unload_uart401(struct address_info *hw_config) if (!devc->share_irq) free_irq(devc->irq, devc); - sound_unload_mididev(hw_config->slots[4]); if (devc) { kfree(midi_devs[devc->my_dev]->converter); @@ -439,6 +444,8 @@ void unload_uart401(struct address_info *hw_config) kfree(devc); devc = NULL; } + /* This kills midi_devs[x] */ + sound_unload_mididev(hw_config->slots[4]); } #ifdef MODULE @@ -475,13 +482,12 @@ void cleanup_module(void) SOUND_LOCK_END; } -#else - -#endif - #endif EXPORT_SYMBOL(attach_uart401); EXPORT_SYMBOL(probe_uart401); EXPORT_SYMBOL(unload_uart401); EXPORT_SYMBOL(uart401intr); + +#endif +#endif diff --git a/drivers/sound/uart6850.c b/drivers/sound/uart6850.c index 75b02ff94..13dcb5fc2 100644 --- a/drivers/sound/uart6850.c +++ b/drivers/sound/uart6850.c @@ -24,8 +24,10 @@ */ #include "sound_config.h" + +#ifdef CONFIG_SOUND_UART6850 +#ifdef CONFIG_MIDI #include "soundmodule.h" -#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) || defined(MODULE) static int uart6850_base = 0x330; @@ -353,3 +355,4 @@ void cleanup_module(void) } #endif #endif +#endif diff --git a/drivers/sound/v_midi.c b/drivers/sound/v_midi.c index 4820d5f36..c1d695c99 100644 --- a/drivers/sound/v_midi.c +++ b/drivers/sound/v_midi.c @@ -21,12 +21,10 @@ #include <linux/config.h> #include <linux/module.h> - - #include "sound_config.h" #include "soundmodule.h" -#if defined(CONFIG_VMIDI) || defined(MODULE) +#ifdef CONFIG_VMIDI #include "v_midi.h" diff --git a/drivers/sound/wavfront.c b/drivers/sound/wavfront.c new file mode 100644 index 000000000..735dcd93f --- /dev/null +++ b/drivers/sound/wavfront.c @@ -0,0 +1,3412 @@ +/* + * sound/wavefront.c + * + * A low level driver for Turtle Beach WaveFront Series + * (Maui, Tropez, Tropez Plus, and perhaps the Monterey & Rio) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + * It also provides support for the ICS emulation of an MPU-401. Full + * support for the ICS emulation's "virtual MIDI mode" is provided in + * wf_midi.c. + * + * Support is also provided for the Tropez Plus' onboard FX processor, + * a Yamaha YSS225. Currently, code exists to configure the YSS225, + * and there is an interface allowing tweaking of any of its memory + * addresses. However, I have been unable to decipher the logical + * positioning of the configuration info for various effects, so for + * now, you just get the YSS225 in the same state as Turtle Beach's + * "SETUPSND.EXE" utility leaves it. + * + * The boards' CODEC (a Crystal CS4232) is supported by cs4232.[co], + * This chip also controls the configuration of the card: the wavefront + * synth is logical unit 4. + * + * NOTE: this driver has been written to support multiple WaveFront + * cards, but without using PnP to configure the CS4232, all of them + * would end up with the same configuration. Further, the current + * module loading interface doesn't permit this, since it only allows + * once instance of a module (which happens to be equivalent to a + * single hardware configuration) to be installed at one time.In + * addition, there is a hard limit on the available DMA channels that + * also makes installing more than 1 card limited to purely MIDI/synth + * activities on the second card. Still, the coding style gets rid of + * virtually all globals, which I believe is a better way to code + * device drivers (or anything else, for that matter). + * + ********************************************************************** + * + * Copyright (C) by Paul Barton-Davis 1998 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * Although the relevant code here is all new, the handling of + * sample/alias/multi- samples is entirely based on a driver by Matt + * Martin and Rutger Nijlunsing which demonstrated how to get things + * to most aspects of this to work correctly. The GUS patch loading + * code has been almost unaltered by me, except to fit formatting and + * function names in the rest of the file. Many thanks to them. + * + * Appreciation and thanks to Hannu Savolainen for his early work on the Maui + * driver, and answering a few questions while this one was developed. + * + * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their + * complete lack of help in developing this driver, and in particular + * for their utter silence in response to questions about undocumented + * aspects of configuring a WaveFront soundcard, particularly the + * effects processor. + * + * This program 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/config.h> +#include <linux/module.h> +#include <asm/init.h> + +#include "sound_config.h" +#include "soundmodule.h" + +#include <linux/wavefront.h> + +#define MIDI_SYNTH_NAME "WaveFront MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +#define COPY_FROM_USER(a,b,c) copy_from_user ((a),(b),(c)) +#define COPY_TO_USER(a,b,c) copy_to_user ((a),(b),(c)) + +#if defined(CONFIG_SOUND_WAVEFRONT) || defined(CONFIG_SOUND_WAVEFRONT_MODULE) + +/* This thing is meant to work as a module */ + +#ifdef MODULE + +/* bitmasks for WaveFront status port value */ + +#define STAT_INTR_WRITE 0x40 +#define STAT_CAN_WRITE 0x20 +#define STAT_RINTR_ENABLED 0x10 +#define STAT_INTR_READ 0x04 +#define STAT_CAN_READ 0x02 +#define STAT_WINTR_ENABLED 0x01 + +/*** Module-accessible parameters ***************************************/ + +int wf_raw = 0; /* we normally check for "raw state" to firmware + loading. if set, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +int wf_debug_default = 0; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +char *wf_ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +/* These three don't need to be messed with unless you're trying to + tweak the driver for optimal I/O performance. Read wavefront_wait() + and wavefront_sleep() to see what they do. You may need or want to + tweak them for CPU's different than the 486/66Mhz that I run on. +*/ + +int wf_short_wait_count = 5000; /* loops, CPU dependent */ +int wf_sleep_interval = 50; /* HZ/wf_sleep_interval seconds per sleep */ +int wf_sleep_tries = 100; /*2sec*/ /* number of times we'll try to sleep */ + +MODULE_PARM(wf_raw,"i"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM(wf_debug_default,"i"); +MODULE_PARM(wf_ospath,"s"); +MODULE_PARM(wf_short_wait_count,"i"); +MODULE_PARM(wf_sleep_interval,"i"); +MODULE_PARM(wf_sleep_tries,"i"); + +/***************************************************************************/ + +static struct synth_info wavefront_info = +{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, + 0, 32, 0, 0, SYNTH_CAP_INPUT}; + +static int (*midi_load_patch) (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) = NULL; + + +typedef struct wf_config { + int installed; /* well, is it ? note: doesn't mean "working" */ + int devno; /* device number from kernel */ + int irq; /* "you were one, one of the few ..." */ + int base; /* low i/o port address */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + int opened; /* flag, holds open(1) mode */ + char debug; /* debugging flags */ + unsigned int freemem; /* installed RAM, in bytes */ + int synthdev; /* OSS minor devnum for synth */ + int mididev; /* OSS minor devno for internal MIDI */ + int ext_mididev; /* OSS minor devno for external MIDI */ + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_on; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ +} wf_config; + +static wf_config wfs[WAVEFRONT_MAX_DEVICES]; + +#define wavefront_status(hw) (inb (hw->status_port)) + +/* forward references */ + +static int wffx_ioctl (struct wf_config *, wavefront_fx_info *); +static int wffx_init (struct wf_config *hw); +static int wavefront_delete_sample (struct wf_config *hw, int sampnum); + +static volatile int irq2hw[17] = +{-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +static volatile int dev2hw[17] = +{-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0x0, 0x0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { 0x00 } +}; + +wf_config * +hw_from_dev (int dev) + +{ + int i; + + if ((i = dev2hw[dev]) == -1) { + printk (KERN_ERR + "WaveFront: no hardware associated with device %d.\n", + dev); + return 0; + } + + return &wfs[i]; +} + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static int +wavefront_sleep (wf_config *hw, int limit) + +{ + current->timeout = jiffies + limit; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; + + return signal_pending(current); +} + +static int +wavefront_wait (wf_config *hw, int mask) + +{ + int i; + + for (i = 0; i < wf_short_wait_count; i++) { + if (wavefront_status(hw) & mask) { + return 1; + } + } + + for (i = 0; i < wf_sleep_tries; i++) { + + if (wavefront_status(hw) & mask) { + return 1; + } + + if (wavefront_sleep (hw, HZ/wf_sleep_interval)) { + return 0; + } + } + + return 0; +} + +static int +wavefront_read (wf_config *hw) +{ + if (wavefront_wait (hw, STAT_CAN_READ)) + return inb (hw->data_port); + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: read timeout.\n"); + } + return -1; +} + +static int +wavefront_write (wf_config *hw, unsigned char data) +{ + if (wavefront_wait (hw, STAT_CAN_WRITE)) { + outb (data, hw->data_port); + return 1; + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: write timeout.\n"); + } + return 0; +} + +static int +wavefront_cmd (wf_config *hw, int cmd, + unsigned char *rbuf, + unsigned char *wbuf) + +{ + int ack; + int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + printk (KERN_WARNING "WaveFront: command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned int) rbuf; + rbuf = 0; + } + + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "Wavefront: 0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, wfcmd->write_cnt, + wfcmd->need_ack); + } + + if (!wavefront_write (hw, cmd)) { + if (hw->debug & (WF_DEBUG_IO|WF_DEBUG_CMD)) { + printk (KERN_DEBUG "WaveFront: cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + } + return 1; + } + + if (wfcmd->write_cnt > 0) { + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + } + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (!wavefront_write (hw, wbuf[i])) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad write for byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } + return 1; + } + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + } + + if (wfcmd->read_cnt > 0) { + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + } + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read(hw)) == -1) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad read for byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read (hw)) == -1) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad read for error byte at " + "read byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return 0; + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: error %d (%s) during " + "read for byte " + "%d of 0x%x [%s].\n", + c, + wavefront_errorstr (c), + i, cmd, wfcmd->action); + } + return 1; + + } + } else { + rbuf[i] = c; + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: read[%d] = 0x%x\n", + i, rbuf[i]); + } + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "WaveFront: reading ACK for 0x%x\n", + cmd); + } + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read(hw)) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: cannot read ack for 0x%x [%s].\n", + cmd, wfcmd->action); + } + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read (hw)) == -1) { + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: cannot read err for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + } + + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: 0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + } + return -err; + } + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } + } else { + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG + "Wavefront: 0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + } + } + + return 0; + +} + +/*********************************************************************** +WaveFront: data munging + +Things here are weird. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8- to 32-bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +weird casting and worrying about bit-level offsets. + +**********************************************************************/ + +static +unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + int i; + + for (i = 0;i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (wf_config *hw, int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = wavefront_cmd (hw, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + hw->sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (struct wf_config *hw, int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (wavefront_cmd (hw, WFC_GET_NSAMPLES, rbuf, wbuf)) { + printk ("WaveFront: cannot request sample count.\n"); + } + + sc_real = sc_alias = sc_multi = hw->samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (wavefront_cmd (hw, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot identify sample " + "type of slot %d\n", i); + hw->sample_status[i] = WF_ST_EMPTY; + continue; + } + + hw->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + hw->sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + printk (KERN_WARNING + "WaveFront: unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + hw->samples_used++; + } + } + + printk (KERN_INFO + "WaveFront: %d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", hw->samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - hw->samples_used); + + + return 0; + +} + +static int +wavefront_get_patch_status (struct wf_config *hw) +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = wavefront_cmd (hw, WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + hw->patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + hw->sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + hw->patch_status[i] = 0; + } else { + printk (KERN_ERR "WaveFront: upload patch " + "error 0x%x\n", x); + hw->patch_status[i] = 0; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (hw->patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (hw->patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + printk (KERN_INFO + "WaveFront: %d patch slots filled, %d in use\n", cnt, cnt2); + + return 0; +} + +static int +wavefront_get_program_status (struct wf_config *hw) +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = wavefront_cmd (hw, WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + hw->prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + hw->patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + hw->prog_status[i] = 0; + } else { + printk (KERN_ERR "WaveFront: upload program " + "error 0x%x\n", x); + hw->prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (hw->prog_status[i]) { + cnt++; + } + } + + printk (KERN_INFO "WaveFront: %d programs slots in use\n", cnt); + + return 0; +} + +static int +wavefront_send_patch (wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: downloading patch %d\n", + header->number); + } + + hw->patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_PATCH, 0, buf)) { + printk (KERN_ERR "WaveFront: download patch failed\n"); + return -EIO; + } + + return 0; +} + +static int +wavefront_send_program (wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG + "WaveFront: downloading program %d\n", header->number); + } + + hw->prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + hw->patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_PROGRAM, 0, buf)) { + printk (KERN_WARNING "WaveFront: download patch failed\n"); + return -EIO; + } + + return 0; +} + +static int +wavefront_freemem (wf_config *hw) + +{ + char rbuf[8]; + + if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + printk (KERN_WARNING "WaveFront: can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (wf_config *hw, + wavefront_patch_info *header, + UINT16 *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + UINT16 sample_short; + UINT32 length; + UINT16 *data_end = 0; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%x\n", + header->size ? "" : "header ", + header->number, header->subkey, header->size, + (int) header->dataptr); + } + + if (header->size) { + + /* XXX its a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little crazy: + we'd need 24 * 512 bytes (12K) of kernel space just to + hold copies of the original sample headers; the whole + patch/program/sample header data is about 158K!!! + */ + + if (hw->rom_samples_rdonly) { + if (hw->sample_status[header->number] & WF_SLOT_ROM) { + printk (KERN_ERR "WaveFront: sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (hw, header->number); + } + + if (header->size) { + hw->freemem = wavefront_freemem (hw); + + if (hw->freemem < header->size) { + printk (KERN_ERR + "WaveFront: insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0) { + switch (header->hdr.s.SampleResolution) { + case LINEAR_16BIT: + break; + default: + printk (KERN_ERR + "WaveFront: channel selection only possible " + "on 16-bit samples"); + return -EINVAL; + } + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), initial_skip, skip); + } + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly weird. What kind of weirdo decided that in + a system dominated by 16- and 32-bit integers, they would use + a 12-bit transfer size ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (wavefront_cmd (hw, header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + printk (KERN_WARNING "WaveFront: sample %sdownload refused.\n", + header->size ? "" : "header "); + return -EIO; + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (wavefront_cmd (hw, WFC_DOWNLOAD_BLOCK, 0, 0)) { + printk (KERN_WARNING "WaveFront: download block " + "request refused.\n"); + return -EIO; + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, hw->block_port); + } else { + outw (sample_short, hw->last_block_port); + } + } + + /* Get "DMA page acknowledge" */ + + if ((dma_ack = wavefront_read (hw)) != WF_DMA_ACK) { + if (dma_ack == -1) { + printk (KERN_ERR "WaveFront: upload sample " + "DMA ack timeout\n"); + return -EIO; + } else { + printk (KERN_ERR "WaveFront: upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -EIO; + } + } + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return 0; +} + +static int +wavefront_send_alias (struct wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + } + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + printk (KERN_ERR "WaveFront: download alias failed.\n"); + return -EIO; + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return 0; +} + +static int +wavefront_send_multisample (struct wf_config *hw, + wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: multi %d with %d=%d samples\n", + header->number, header->hdr.ms.NumberOfSamples, num_samples); + } + + for (i = 0; i < num_samples; i++) { + if ((hw->debug & (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) == + (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) { + printk (KERN_DEBUG "WaveFront: sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (wavefront_cmd (hw, WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) ((num_samples*2)+3), + msample_hdr)) { + printk (KERN_ERR "WaveFront: download of multisample failed.\n"); + return -EIO; + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return 0; +} + +static int +wavefront_fetch_multisample (struct wf_config *hw, + wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (wavefront_cmd (hw, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + printk (KERN_ERR "WaveFront: upload multisample failed.\n"); + return -EIO; + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: msample %d has %d samples\n", + header->number, log_ns[0]); + } + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + char d[2]; + + if ((d[0] = wavefront_read (hw)) == -1) { + printk (KERN_ERR "WaveFront: upload multisample failed " + "during sample loop.\n"); + return -EIO; + } + + if ((d[1] = wavefront_read (hw)) == -1) { + printk (KERN_ERR "WaveFront: upload multisample failed " + "during sample loop.\n"); + return -EIO; + } + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: msample " + "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + } + + return 0; +} + + +static int +wavefront_send_drum (struct wf_config *hw, wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG + "WaveFront: downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + } + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (wavefront_cmd (hw, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + printk (KERN_ERR "WaveFront: download drum failed.\n"); + return -EIO; + } + + return 0; +} + +static int +wavefront_find_free_sample (struct wf_config *hw) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(hw->sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING "WaveFront: no free sample slots!\n"); + return -1; +} + +static int +wavefront_find_free_patch (struct wf_config *hw) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(hw->patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING "WaveFront: no free patch slots!\n"); + return -1; +} + +static int +log2_2048(int n) + +{ + int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, + 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, + 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, + 9510, 9626, 9738, 9845, 9949, 10049, 10146}; + int i; + + /* Returns 2048*log2(n) */ + + /* FIXME: this is like doing integer math + on quantum particles (RuN) */ + + i=0; + while(n>=32*256) { + n>>=8; + i+=2048*8; + } + while(n>=32) { + n>>=1; + i+=2048; + } + i+=tbl[n]; + return(i); +} + +static int +wavefront_load_gus_patch (struct wf_config *hw, + int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info guspatch; + wavefront_patch_info samp, pat, prog; + wavefront_patch *patp; + wavefront_sample *sampp; + wavefront_program *progp; + + int i,base_note; + long sizeof_patch; + + /* Copy in the header of the GUS patch */ + + sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; + COPY_FROM_USER (&((char *) &guspatch)[offs], + &(addr)[offs], sizeof_patch - offs); + + if ((i = wavefront_find_free_patch (hw)) == -1) { + return -EBUSY; + } + pat.number = i; + pat.subkey = WF_ST_PATCH; + patp = &pat.hdr.p; + + if ((i = wavefront_find_free_sample (hw)) == -1) { + return -EBUSY; + } + samp.number = i; + samp.subkey = WF_ST_SAMPLE; + samp.size = guspatch.len; + sampp = &samp.hdr.s; + + prog.number = guspatch.instr_no; + progp = &prog.hdr.pr; + + /* Setup the patch structure */ + + patp->amplitude_bias=guspatch.volume; + patp->portamento=0; + patp->sample_number= samp.number & 0xff; + patp->sample_msb= samp.number>>8; + patp->pitch_bend= /*12*/ 0; + patp->mono=1; + patp->retrigger=1; + patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; + patp->frequency_bias=0; + patp->restart=0; + patp->reuse=0; + patp->reset_lfo=1; + patp->fm_src2=0; + patp->fm_src1=WF_MOD_MOD_WHEEL; + patp->am_src=WF_MOD_PRESSURE; + patp->am_amount=127; + patp->fc1_mod_amount=0; + patp->fc2_mod_amount=0; + patp->fm_amount1=0; + patp->fm_amount2=0; + patp->envelope1.attack_level=127; + patp->envelope1.decay1_level=127; + patp->envelope1.decay2_level=127; + patp->envelope1.sustain_level=127; + patp->envelope1.release_level=0; + patp->envelope2.attack_velocity=127; + patp->envelope2.attack_level=127; + patp->envelope2.decay1_level=127; + patp->envelope2.decay2_level=127; + patp->envelope2.sustain_level=127; + patp->envelope2.release_level=0; + patp->envelope2.attack_velocity=127; + patp->randomizer=0; + + /* Program for this patch */ + + progp->layer[0].patch_number= pat.number; /* XXX is this right ? */ + progp->layer[0].mute=1; + progp->layer[0].pan_or_mod=1; + progp->layer[0].pan=7; + progp->layer[0].mix_level=127 /* guspatch.volume */; + progp->layer[0].split_type=0; + progp->layer[0].split_point=0; + progp->layer[0].updown=0; + + for (i = 1; i < 4; i++) { + progp->layer[i].mute=0; + } + + /* Sample data */ + + sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); + + for (base_note=0; + note_to_freq (base_note) < guspatch.base_note; + base_note++); + + if ((guspatch.base_note-note_to_freq(base_note)) + >(note_to_freq(base_note)-guspatch.base_note)) + base_note++; + + printk(KERN_DEBUG "ref freq=%d,base note=%d\n", + guspatch.base_freq, + base_note); + + sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) + + base_note*171); + printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); + sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; + sampp->sampleStartOffset.Fraction=0; + sampp->sampleStartOffset.Integer=0; + sampp->loopStartOffset.Fraction=0; + sampp->loopStartOffset.Integer=guspatch.loop_start + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->loopEndOffset.Fraction=0; + sampp->loopEndOffset.Integer=guspatch.loop_end + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->sampleEndOffset.Fraction=0; + sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); + sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; + sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; + + /* Now ship it down */ + + wavefront_send_sample (hw, &samp, + (unsigned short *) &(addr)[sizeof_patch], + (guspatch.mode & WAVE_UNSIGNED) ? 1:0); + wavefront_send_patch (hw, &pat); + wavefront_send_program (hw, &prog); + + /* Now pan as best we can ... use the slave/internal MIDI device + number if it exists (since it talks to the WaveFront), or the + master otherwise. + */ + +#ifdef CONFIG_MIDI + if (hw->mididev > 0) { + midi_synth_controller (hw->mididev, guspatch.instr_no, 10, + ((guspatch.panning << 4) > 127) ? + 127 : (guspatch.panning << 4)); + } +#endif CONFIG_MIDI + + return(0); +} + +int +wavefront_load_patch (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + + struct wf_config *hw; + wavefront_patch_info header; + + if ((hw = hw_from_dev (dev)) == 0) { + return -EINVAL; + } + + if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ + if (midi_load_patch == NULL) { + printk (KERN_ERR + "WaveFront: SYSEX not loadable: " + "no midi patch loader!\n"); + return -EINVAL; + } + return midi_load_patch (dev, format, addr, + offs, count, pmgr_flag); + + } else if (format == GUS_PATCH) { + return wavefront_load_gus_patch (hw, dev, format, + addr, offs, count, pmgr_flag); + + } else if (format != WAVEFRONT_PATCH) { + printk (KERN_ERR "WaveFront: unknown patch format %d\n", format); + return -EINVAL; + } + + if (count < sizeof (wavefront_patch_info)) { + printk (KERN_ERR "WaveFront: sample header too short\n"); + return -EINVAL; + } + + /* copied in so far: `offs' bytes from `addr'. We shouldn't copy + them in again, and they correspond to header->key and header->devno. + So now, copy the rest of the wavefront_patch_info struct, except + for the 'hdr' field, since this is handled via indirection + through the 'hdrptr' field. + */ + + COPY_FROM_USER (&((char *) &header)[offs], &(addr)[offs], + sizeof(wavefront_patch_info) - + sizeof(wavefront_any) - offs); + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + } + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + COPY_FROM_USER ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample)); + + return wavefront_send_sample (hw, &header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + COPY_FROM_USER ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample)); + + return wavefront_send_multisample (hw, &header); + + + case WF_ST_ALIAS: + + COPY_FROM_USER ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias)); + + return wavefront_send_alias (hw, &header); + + case WF_ST_DRUM: + COPY_FROM_USER ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum)); + + return wavefront_send_drum (hw, &header); + + case WF_ST_PATCH: + COPY_FROM_USER ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch)); + + return wavefront_send_patch (hw, &header); + + case WF_ST_PROGRAM: + COPY_FROM_USER ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program)); + + return wavefront_send_program (hw, &header); + + default: + printk (KERN_ERR "WaveFront: unknown patch type %d.\n", + header.subkey); + return -EINVAL; + } + + return 0; +} + +/*********************************************************************** +WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces +***********************************************************************/ + +static void +process_sample_hdr (UCHAR8 *buf) + +{ + wavefront_sample s; + UCHAR8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (int dev, int cmd, caddr_t arg) + +{ + struct wf_config *hw; + wavefront_control wc; + unsigned char patchnumbuf[2]; + int i; + + if ((hw = hw_from_dev (dev)) == 0) { + printk (KERN_ERR + "WaveFront: synth_control with unknown " + "device number %d\n", dev); + return -EINVAL; + } + + COPY_FROM_USER (&wc, arg, sizeof (wc)); + + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "WaveFront: synth control with " + "cmd 0x%x\n", wc.cmd); + } + + /* special case handling of or for various commands */ + + switch (wc.cmd) { + case WFC_DISABLE_INTERRUPTS: + printk (KERN_INFO "WaveFront: interrupts disabled.\n"); + outb (0x80|0x20, hw->control_port); + hw->interrupts_on = 0; + return 0; + + case WFC_ENABLE_INTERRUPTS: + printk (KERN_INFO "WaveFront: interrupts enabled.\n"); + outb (0x80|0x20|0x40, hw->control_port); + hw->interrupts_on = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc.rbuf[0] = hw->interrupts_on; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + hw->rom_samples_rdonly = wc.wbuf[0]; + wc.status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc.wbuf[0] | (wc.wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + printk (KERN_WARNING "WaveFront: invalid slot ID %d\n", + i); + wc.status = EINVAL; + return 0; + } + wc.rbuf[0] = hw->sample_status[i]; + wc.status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + hw->debug = wc.wbuf[0]; + printk (KERN_INFO "WaveFront: debug = 0x%x\n", hw->debug); + return 0; + + case WFC_FX_IOCTL: + wffx_ioctl (hw, (wavefront_fx_info *) &wc.wbuf[0]); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((UINT32 *) wc.wbuf), patchnumbuf, 2); + memcpy (wc.wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO "WaveFront: support for various uploads " + "being considered.\n"); + wc.status = EINVAL; + return -EINVAL; + } + + wc.status = wavefront_cmd (hw, wc.cmd, wc.rbuf, wc.wbuf); + + /* Special case handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc.status == 0) { + switch (wc.cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + hw->freemem = demunge_int32 (wc.rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc.rbuf, wc.rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc.rbuf, wc.rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc.rbuf, wc.rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc.rbuf); + break; + + case WFC_UPLOAD_MULTISAMPLE: + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO "WaveFront: support for " + "various uploads " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + virtual_midi_disable (hw->mididev); + break; + + case WFC_VMIDI_ON: + virtual_midi_enable (hw->mididev, 0); + break; + + break; + } + } + + /* XXX It would be nice to avoid a complete copy of the whole + struct sometimes. But I think its fast enough that this + is a low priority fix. + */ + + COPY_TO_USER (arg, &wc, sizeof (wc)); + return 0; +} + + +/*********************************************************************** +WaveFront: MIDI synth interface +***********************************************************************/ + + +static int +wavefront_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + wf_config *hw; + unsigned char rbuf[4]; + + if ((hw = hw_from_dev (dev)) == 0) { + return -EINVAL; + } + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + memcpy (&((char *) arg)[0], &wavefront_info, + sizeof (wavefront_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + printk (KERN_WARNING + "WaveFront: cannot reset sample status in kernel.\n"); + return 0; /* don't force an error */ + break; + + case SNDCTL_SEQ_PERCMODE: + /* XXX does this correspond to anything obvious ?*/ + return 0; /* don't force an error */ + break; + + case SNDCTL_SYNTH_MEMAVL: + if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0) != 0) { + printk (KERN_ERR + "WaveFront: cannot get free memory size\n"); + return 0; + } else { + hw->freemem = demunge_int32 (rbuf, 4); + return hw->freemem; + } + + case SNDCTL_SYNTH_CONTROL: + return wavefront_synth_control (dev, cmd, arg); + + default: + return -EINVAL; + } +} + +static int +wavefront_open (int dev, int mode) + +{ + struct wf_config *hw; + + if ((hw = hw_from_dev (dev)) == 0) { + return -EINVAL; + } + + if (hw->opened) { + printk (KERN_ERR "WaveFront: warning: device in use\n"); + } + + hw->opened = mode; + return 0; +} + +static void +wavefront_close (int dev) + +{ + struct wf_config *hw; + + if ((hw = hw_from_dev (dev)) == 0) { + printk (KERN_ERR + "WaveFront: close() called on non-existent dev %d", dev); + return; + } + + hw->opened = 0; + hw->debug = 0; + + return; +} + +static void +wavefront_aftertouch (int dev, int channel, int pressure) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + midi_synth_aftertouch (hw->mididev,channel,pressure); +}; + +static void +wavefront_bender (int dev, int chn, int value) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + midi_synth_bender (hw->mididev, chn, value); +}; + +static void +wavefront_controller (int dev, int channel, int ctrl_num, int value) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + if(ctrl_num==CTRL_PITCH_BENDER) wavefront_bender(0,channel,value); + midi_synth_controller (hw->mididev, channel,ctrl_num,value); +}; + +static void +wavefront_panning(int dev, int channel, int pressure) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + midi_synth_controller(hw->mididev,channel,CTL_PAN,pressure); +}; + +static int +wavefront_set_instr (int dev, int channel, int instr_no) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return 1; + return(midi_synth_set_instr(hw->mididev,channel,instr_no)); +}; + +static int +wavefront_kill_note (int dev, int channel, int note, int volume) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return 1; + if (note==255) + return(midi_synth_start_note(hw->mididev, channel, 0, 0)); + return(midi_synth_kill_note(hw->mididev, channel, note, volume)); +}; + +static int +wavefront_start_note (int dev, int channel, int note, int volume) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return 1; + + if (note==255) { + /*midi_synth_controller(hw->mididev,channel,7,volume);*/ + midi_synth_aftertouch(hw->mididev,channel,volume); + return(0); + }; + if (volume==0) { + volume=127; + midi_synth_aftertouch(hw->mididev,channel,0); + }; + midi_synth_start_note (hw->mididev, channel, note, volume); + return(0); +}; + +static void +wavefront_setup_voice (int dev, int voice, int chn) +{ +}; + +static void wavefront_reset (int dev) + +{ + int i; + for(i=0;i<16;i++) { + midi_synth_kill_note (dev,i,0,0); + }; +}; + +static struct synth_operations wavefront_operations = +{ + "WaveFront", + &wavefront_info, + 0, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_WAVEFRONT, + wavefront_open, + wavefront_close, + wavefront_ioctl, + wavefront_kill_note, + wavefront_start_note, + wavefront_set_instr, + wavefront_reset, + NULL, + wavefront_load_patch, + wavefront_aftertouch, + wavefront_controller, + wavefront_panning, + NULL, + wavefront_bender, + NULL, + wavefront_setup_voice +}; + + +/*********************************************************************** +WaveFront: OSS/Free and/or Linux kernel installation interface +***********************************************************************/ + +void +wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy) +{ + int i; + + if (irq < 0 || irq > 16) { + printk (KERN_WARNING "WaveFront: bogus interrupt %d recv'd\n", + irq); + } else if ((i = irq2hw[irq]) == -1) { + printk (KERN_ALERT + "WaveFront: interrupt from unknown hw (irq=%d).\n", irq); + } else { + wfs[i].irq_ok = 1; + } +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused + +*/ + +/* CONTROL REGISTER +0 Host Rx Interrupt Enable (1=Enabled) 0x1 +1 Unused 0x2 +2 Unused 0x4 +3 Unused 0x8 +4 Host Tx Interrupt Enable 0x10 +5 Mute (0=Mute; 1=Play) 0x20 +6 Master Interrupt Enable (1=Enabled) 0x40 +7 Master Reset (0=Reset; 1=Run) 0x80 +*/ + +int +probe_wavefront (struct address_info *hw_config) + +{ + int i; + int tmp1, tmp2; + unsigned char bits; + unsigned char rbuf[32], wbuf[32]; + wf_config *hw; + + if (hw_config->irq < 0 || hw_config->irq > 16) { + printk (KERN_WARNING "WaveFront: impossible IRQ suggested(%d)\n", + hw_config->irq); + return 0; + } + + /* Yeah yeah, TB docs say 8, but the FX device on the Tropez Plus + takes up another 8 ... + */ + + if (check_region (hw_config->io_base, 16)) { + printk (KERN_ERR "WaveFront: IO address range 0x%x - 0x%x " + "already in use - ignored\n", hw_config->io_base, + hw_config->io_base+15); + return 0; + } + + for (i = 0; i < WAVEFRONT_MAX_DEVICES; i++) { + if (!wfs[i].installed) { + wfs[i].installed = 1; + break; + } + } + + if (i == WAVEFRONT_MAX_DEVICES) { + printk (KERN_WARNING "WaveFront: no device slots available (max = %d).\n", + WAVEFRONT_MAX_DEVICES); + return 0; + } + + hw = &wfs[i]; + + hw->irq = hw_config->irq; + hw->base = hw_config->io_base; + + hw->israw = 0; + hw->debug = wf_debug_default; + hw->interrupts_on = 0; + hw->rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ + + hw_config->slots[WF_SYNTH_SLOT] = hw->synthdev = -1; + hw_config->slots[WF_INTERNAL_MIDI_SLOT] = hw->mididev = -1; + hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = hw->ext_mididev = -1; + + irq2hw[hw_config->irq] = i; + + if (wavefront_cmd (hw, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + hw->fw_version[0] = rbuf[0]; + hw->fw_version[1] = rbuf[1]; + printk (KERN_INFO "WaveFront: firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + if (wavefront_cmd (hw, WFC_HARDWARE_VERSION, rbuf, wbuf) == 0) { + hw->hw_version[0] = rbuf[0]; + hw->hw_version[1] = rbuf[1]; + } else { + printk (KERN_INFO "WaveFront: not raw, but no hardware version!\n"); + return 0; + } + if (!wf_raw) { + return 1; + } + } else { + hw->israw = 1; + printk (KERN_INFO + "WaveFront: no response to firmware probe, " + "assume raw.\n"); + } + + if (request_irq (hw_config->irq, wavefrontintr, + 0, "WaveFront", NULL) < 0) { + printk (KERN_WARNING "WaveFront: IRQ %d not available!\n", + hw_config->irq); + return 0; + } + + switch (hw_config->irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk (KERN_WARNING "WaveFront: invalid IRQ %d\n", + hw_config->irq); + return 0; + } + + /* try reset of port */ + + outb (0x0, hw->control_port); + + /* At this point, the board is in reset, and the H/W initialization + register is accessed at the same address as the data port. + + Bit 7 - Enable IRQ Driver + 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs + 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. + + Bit 6 - MIDI Interface Select + + XXX PBD: I think this documentation is backwards. I leave bit + 6 unset, and get MIDI data from the 9 pin D connector. + + 0 - Use the MIDI Input from the 26-pin WaveBlaster + compatible header as the serial MIDI source + 1 - Use the MIDI Input from the 9-pin D connector as the serial MIDI + source. + + Bits 5:3 - IRQ Selection + 0 0 0 - IRQ 2/9 + 0 0 1 - IRQ 5 + 0 1 0 - IRQ 12 + 0 1 1 - IRQ 15 + 1 0 0 - Reserved + 1 0 1 - Reserved + 1 1 0 - Reserved + 1 1 1 - Reserved + + Bits 2:1 - Reserved + Bit 0 - Disable Boot ROM + 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM + 1 - memory accesses to 03FC30-03FFFFH are directed to external + storage. + + */ + + /* configure hardware: IRQ, plus external MIDI interface selected */ + + outb (bits | 0x80, hw->data_port); + + /* take us out of reset, unmute, master + TX + RX interrupts on */ + + outb (0x80|0x20|0x40|0x10|0x1, hw->control_port); + + for (i = 0; i < 1000000 && !hw->irq_ok; i++); + + /* Data port is now the data port, not the h/w initialization port + + The boot ROM will check the OSRAM, and will then + wait for the either the "download OS" or + "report h/w version" commands. + + Any other command will supposedly be ignored. + */ + + if (!hw->irq_ok) { + printk (KERN_WARNING + "WaveFront: intr not received after h/w un-reset.\n"); + free_irq (hw_config->irq, NULL); + return 0; + } else { + hw->irq_ok = 0; + } + + hw->interrupts_on = 1; + + /* WaveFront SDK says: + + "When the Master Reset is set to zero (0), the audio board is held + in reset, which is the power-up condition. Setting Master Reset to one + (1) allows the on-board processor to run. It takes approximately two + to four seconds, depending on the memory configuration, for the + on-board processor to complete it's initialization routine before it + will respond to commands after a reset." + + Actually, it seems that most of the time, even with 8MB of RAM, + its actually ready immediately. + */ + + if (!wavefront_wait (hw, STAT_CAN_WRITE)) { + if (!wavefront_wait (hw, STAT_CAN_WRITE)) { + if (!wavefront_wait (hw, STAT_CAN_WRITE)) { + printk (KERN_WARNING + "WaveFront: OS not ready after " + "memory check.\n"); + free_irq (hw_config->irq, NULL); + return 0; + } + } + } + + /* get H/W version, and check we get an interrupt */ + + outb (WFC_HARDWARE_VERSION, hw->data_port); + + for (i = 0; i < 1000000 && !hw->irq_ok; i++); + + /* We don't need the IRQ anymore for the WaveFront code, + and to allow an MPU-401 driver to attach to it later, lets + give it back .... + + DO NOT alter irq2hw[], since we'll still use this to lookup + the config struct from an address_info struct. + */ + + free_irq (hw_config->irq, NULL); + + if (!hw->irq_ok) { + printk (KERN_WARNING + "WaveFront: interrupt not received after " + "h/w version cmd.\n"); + return 0; + } else { + hw->irq_ok = 0; + } + + if ((tmp1 = wavefront_read(hw)) == -1) { + printk (KERN_WARNING + "WaveFront @ 0x%x not ready, ignoring\n", hw->base); + return 0; + } + + if ((tmp2 = wavefront_read(hw)) == -1) { + printk (KERN_WARNING + "WaveFront @ 0x%x not responding correctly, ignoring\n", + hw->base); + return 0; + } + + printk (KERN_INFO "WaveFront: hardware version %d.%d\n", tmp1, tmp2); + + return 1; +} + +#include "os.h" +#define __KERNEL_SYSCALLS__ +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/unistd.h> +#include <asm/uaccess.h> + +static int errno; + +static int +wavefront_download_firmware (wf_config *hw, char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ + int section_cnt_downloaded = 0; + int fd; + int c; + int i; + mm_segment_t fs; + + /* This tries to be a bit cleverer than the stuff Alan Cox did for + the generic sound firmware, in that it actually knows + something about the structure of the Motorola firmware. In + particular, it uses a version that has been stripped of the + 20K of useless header information, and had section lengths + added, making it possible to load the entire OS without any + [kv]malloc() activity, since the longest entity we ever read is + 42 bytes (well, WF_SECTION_MAX) long. + */ + + fs = get_fs(); + set_fs (get_ds()); + + if ((fd = open (path, 0, 0)) < 0) { + printk (KERN_WARNING "WaveFront: Unable to load \"%s\".\n", path); + return 1; + } + + while (1) { + int x; + + if ((x = read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + printk (KERN_ERR "WaveFront: firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (read (fd, section, section_length) != section_length) { + printk (KERN_ERR "WaveFront: firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (!wavefront_write (hw, WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (!wavefront_write (hw, section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (hw, STAT_CAN_READ)) { + + if ((c = inb (hw->data_port)) != WF_ACK) { + + printk (KERN_ERR "WaveFront: download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } else if ((hw->debug & WF_DEBUG_IO) && + !(++section_cnt_downloaded % 10)) { + printk (KERN_DEBUG "."); + } + + } else { + printk (KERN_ERR "WaveFront: timed out " + "for download ACK.\n"); + } + + } + + close (fd); + set_fs (fs); + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG "\n"); + } + return 0; + + failure: + close (fd); + set_fs (fs); + printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); + return 1; +} + +static int +wavefront_config_midi (wf_config *hw, struct address_info *hw_config) + +{ + unsigned char rbuf[4], wbuf[4]; + + if (!probe_wf_mpu (hw_config)) { + printk (KERN_WARNING "WaveFront: could not install " + "MPU-401 device.\n"); + return 1; + } + + /* Attach an modified MPU-401 driver to the master MIDI interface */ + + hw_config->name = "WaveFront Internal MIDI"; + attach_wf_mpu (hw_config); + + if (hw_config->slots[WF_INTERNAL_MIDI_SLOT] == -1) { + printk (KERN_WARNING "WaveFront: MPU-401 not configured.\n"); + return 1; + } + + hw->mididev = hw_config->slots[WF_INTERNAL_MIDI_SLOT]; + + /* Route external MIDI to WaveFront synth (by default) */ + + if (wavefront_cmd (hw, WFC_MISYNTH_ON, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot enable MIDI-IN to synth routing.\n"); + /* XXX error ? */ + } + + /* Get the regular MIDI patch loading function, so we can + use it if we ever get handed a SYSEX patch. This is + unlikely, because its so damn slow, but we may as well + leave this functionality from maui.c behind, since it + could be useful for sequencer applications that can + only use MIDI to do patch loading. + */ + + if (midi_devs[hw->mididev]->converter != NULL) { + midi_load_patch = midi_devs[hw->mididev]->converter->load_patch; + midi_devs[hw->mididev]->converter->load_patch = + &wavefront_load_patch; + } + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (wavefront_cmd (hw, WFC_VMIDI_OFF, rbuf, wbuf)) { + printk (KERN_WARNING "WaveFront: cannot disable " + "virtual MIDI mode\n"); + /* XXX go ahead and try anyway ? */ + } + + hw_config->name = "WaveFront External MIDI"; + + if (virtual_midi_enable (hw->mididev, hw_config)) { + printk (KERN_WARNING "WaveFront: no virtual MIDI access.\n"); + } else { + hw->ext_mididev = hw_config->slots[WF_EXTERNAL_MIDI_SLOT]; + if (wavefront_cmd (hw, WFC_VMIDI_ON, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot enable virtual MIDI mode.\n"); + virtual_midi_disable (hw->mididev); + } + } + + return 0; +} + +static int +wavefront_init (wf_config *hw, struct address_info *hw_config) + +{ + int samples_are_from_rom = 0; + + /* XXX what state does the board need to be in before + I can download the firmware ? I think any state + after it acknowledges a hardware version command + post-booting. + */ + + if (hw->israw || wf_raw) { + samples_are_from_rom = 1; + + if (wavefront_download_firmware (hw, wf_ospath)) { + return 1; + } + + /* enter normal operation: + bit 7: (on) reset 0x80 + bit 6: interrupts enabled 0x40 + bit 5: (on) mute (i.e. in play mode) 0x20 + bits 4-0: zero + + note: tx and rx interrupts turned off + */ + + outb (0x80|0x40|0x20, hw->control_port); + + /* Set up MPU-401 emulation mode. For some reason, this always + fails the first time, and generates no ACK, so we'll + treat it as a special case by sleeping for a while, and then + trying again. + */ + + wavefront_write (hw, 0xf0); + wavefront_write (hw, 1); + wavefront_sleep (hw, (HZ/wf_sleep_interval) * wf_sleep_tries); + wavefront_write (hw, 0xf0); + wavefront_write (hw, 1); + if (wavefront_read (hw) != 0x80) { + printk (KERN_ERR + "WaveFront: set MPU emulation mode " + "command failed.\n"); + return (1); + } + } + + hw->freemem = wavefront_freemem (hw); + printk (KERN_INFO "WaveFront: available DRAM %dk\n", hw->freemem / 1024); + + wavefront_get_sample_status (hw, samples_are_from_rom); + wavefront_get_program_status (hw); + wavefront_get_patch_status (hw); + + if (fx_raw) { + wffx_init (hw); + } + + return 0; +} + +void +attach_wavefront (struct address_info *hw_config) +{ + int i; + struct wf_config *hw; + + if ((i = irq2hw[hw_config->irq]) == -1) { + printk (KERN_ERR "WaveFront: cannot attach unknown irq 0x%x.\n", + hw_config->irq); + return; + } + + hw = &wfs[i]; + + if ((i = sound_alloc_synthdev()) == -1) { + printk (KERN_ERR "WaveFront: Too many synthesizers\n"); + return; + } else { + hw_config->slots[WF_SYNTH_SLOT] = i; + hw->synthdev = i; + dev2hw[hw->synthdev] = i; + synth_devs[hw->synthdev] = &wavefront_operations; + } + + if (wavefront_init (hw, hw_config)) { + printk (KERN_WARNING "WaveFront: board could not " + "be initialized.\n"); + sound_unload_synthdev (i); + hw->installed = 0; + return; + } + + request_region (hw_config->io_base+2, 6, "WaveFront synth"); + request_region (hw_config->io_base+8, 8, "WaveFront FX"); + + conf_printf2 ("WaveFront Synth", hw_config->io_base, 0, -1, -1); + +#if defined(CONFIG_MIDI) + if (wavefront_config_midi (hw, hw_config)) { + printk (KERN_WARNING "WaveFront: could not initialize MIDI.\n"); + } +#else + printk (KERN_WARNING + "WaveFront: MIDI not configured at kernel-config time.\n"); +#endif CONFIG_MIDI + + return; +} +void +unload_wavefront (struct address_info *hw_config) +{ + struct wf_config *hw; + int i; + + if ((i = irq2hw[hw_config->irq]) == -1) { + printk (KERN_ERR "WaveFront: unloading unrecognized device!\n"); + return; + } + hw = &wfs[i]; + + /* the first two are freed by the wf_mpu code */ + release_region (hw_config->io_base+2, 6); + release_region (hw_config->io_base+8, 8); + sound_unload_synthdev (hw->synthdev); +#if defined(CONFIG_MIDI) + unload_wf_mpu (hw_config); +#endif +} + +/***********************************************************************/ +/* WaveFront FX control */ +/***********************************************************************/ + +#include "yss225.h" + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static int +wffx_idle (struct wf_config *hw) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (hw->fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + printk (KERN_ERR "WaveFront: FX device never idle.\n"); + return 0; + } + + return (1); +} + +static void +wffx_mute (struct wf_config *hw, int onoff) + +{ + if (!wffx_idle(hw)) { + return; + } + + outb (onoff ? 0x02 : 0x00, hw->fx_op); +} + +static int +wffx_memset (struct wf_config *hw, int page, + int addr, int cnt, unsigned short *data) +{ + if (page < 0 || page > 7) { + printk (KERN_ERR "WaveFront: FX memset: " + "page must be >= 0 and <= 7\n"); + return -EINVAL; + } + + if (addr < 0 || addr > 0x7f) { + printk (KERN_ERR "WaveFront: FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -EINVAL; + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (page, hw->fx_dsp_page); + outb (addr, hw->fx_dsp_addr); + outb ((data[0] >> 8), hw->fx_dsp_msb); + outb ((data[0] & 0xff), hw->fx_dsp_lsb); + + printk (KERN_INFO "WaveFront: FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (page, hw->fx_dsp_page); + outb (addr, hw->fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), hw->fx_dsp_msb); + outb ((data[i] & 0xff), hw->fx_dsp_lsb); + if (!wffx_idle (hw)) { + break; + } + } + + if (i != cnt) { + printk (KERN_WARNING + "WaveFront: FX memset " + "(0x%x, 0x%x, 0x%x, %d) incomplete\n", + page, addr, (int) data, cnt); + return -EIO; + } + } + + return 0; +} + +static int +wffx_ioctl (struct wf_config *hw, wavefront_fx_info *r) + +{ + unsigned short page_data[256]; + unsigned short *pd; + + switch (r->request) { + case WFFX_MUTE: + wffx_mute (hw, r->data[0]); + return 0; + + case WFFX_MEMSET: + + if (r->data[2] <= 0) { + printk (KERN_ERR "WaveFront: cannot write " + "<= 0 bytes to FX\n"); + return -EINVAL; + } else if (r->data[2] == 1) { + pd = (unsigned short *) &r->data[3]; + } else { + if (r->data[2] > sizeof (page_data)) { + printk (KERN_ERR + "WaveFront: cannot write " + "> 255 bytes to FX\n"); + return -EINVAL; + } + COPY_FROM_USER(page_data, (unsigned char *) r->data[3], + r->data[2]); + pd = page_data; + } + + return wffx_memset (hw, + r->data[0], /* page */ + r->data[1], /* addr */ + r->data[2], /* cnt */ + pd); + + default: + printk (KERN_WARNING + "WaveFront: FX: ioctl %d not yet supported\n", + r->request); + return -EINVAL; + } +} + +/* YSS225 initialization. + + This code was developed using DOSEmu. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEmu enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. It's really pretty weird. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + +static int +wffx_init (struct wf_config *hw) + +{ + int i; + int j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle (hw)) { + return (-1); + } + + outb (i, hw->fx_mod_addr); + outb (0x0, hw->fx_mod_data); + } + } + + if (!wffx_idle(hw)) return (-1); + outb (0x02, hw->fx_op); /* mute on */ + + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x44, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x42, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x43, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x7c, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x7e, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x46, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x49, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x47, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x4a, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle (hw)) { + return (-1); + } + + outb (i, hw->fx_mod_addr); + outb (0x0, hw->fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x00, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], hw->fx_dsp_msb); + outb (page_zero[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x01, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], hw->fx_dsp_msb); + outb (page_one[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x02, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x03, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x04, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x06, hw->fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], hw->fx_dsp_addr); + outb (page_six[i+1], hw->fx_dsp_msb); + outb (page_six[i+2], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x07, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], hw->fx_dsp_msb); + outb (page_seven[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, hw->fx_mod_addr); + outb (i, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0x02, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, hw->fx_mod_addr); + outb (0xff, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x1e, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, hw->fx_mod_addr); + outb (0xff, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x2e, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x3f, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x4e, hw->fx_mod_addr); + outb (0x0e, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0x4f, hw->fx_mod_addr); + outb (0x0e, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x6c, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6d, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6e, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6f, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, hw->fx_mod_addr); + outb (0xc0, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0xde, hw->fx_mod_addr); + outb (0x10, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xdf, hw->fx_mod_addr); + outb (0x10, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, hw->fx_mod_addr); + outb (i, hw->fx_mod_data); + outb (0x02, hw->fx_mod_addr); + outb (0x01, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x02, hw->fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], hw->fx_dsp_page); + outb (coefficients[i+1], hw->fx_dsp_addr); + outb (coefficients[i+2], hw->fx_dsp_msb); + outb (coefficients[i+3], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wffx_idle(hw)) return (-1); + outb (0x1e, hw->fx_mod_addr); + outb (0x14, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xde, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xdf, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + + /* some more coefficients */ + + if (!wffx_idle(hw)) return (-1); + outb (0x06, hw->fx_dsp_page); + outb (0x78, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x40, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x03, hw->fx_dsp_addr); + outb (0x0f, hw->fx_dsp_msb); + outb (0xff, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x0b, hw->fx_dsp_addr); + outb (0x0f, hw->fx_dsp_msb); + outb (0xff, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x02, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x0a, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x46, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x49, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x00, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], hw->fx_dsp_msb); + outb (page_zero_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x01, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], hw->fx_dsp_msb); + outb (page_one_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + if (!wffx_idle(hw)) return (-1); + if (!wffx_idle(hw)) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x02, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x03, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x04, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x06, hw->fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x07, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], hw->fx_dsp_msb); + outb (page_seven_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], hw->fx_mod_addr); + outb (mod_v2[i+1], hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], hw->fx_dsp_page); + outb (coefficients2[i+1], hw->fx_dsp_addr); + outb (coefficients2[i+2], hw->fx_dsp_msb); + outb (coefficients2[i+3], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x00, hw->fx_op); + if (!wffx_idle(hw)) return (-1); + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, hw->fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, hw->fx_dsp_addr); + outb (coefficients3[i], hw->fx_dsp_msb); + outb (coefficients3[i+1], hw->fx_dsp_lsb); + } + + outb (0x00, hw->fx_op); /* mute off */ + + return 0; +} + +EXPORT_NO_SYMBOLS; +struct address_info cfg; + +int io = -1; +int irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); + +int init_module (void) + +{ + printk ("Turtle Beach WaveFront Driver\n" + "Copyright (C) by Hannu Savolainen, " + "Paul Barton-Davis 1993-1998.\n"); + + if (io == -1 || irq == -1) { + printk (KERN_INFO "WaveFront: irq and io " + "options must be set.\n"); + return -EINVAL; + } + cfg.io_base = io; + cfg.irq = irq; + + if (probe_wavefront (&cfg) == 0) { + return -ENODEV; + } + attach_wavefront (&cfg); + SOUND_LOCK; + return 0; +} + +void cleanup_module (void) + +{ + unload_wavefront (&cfg); + SOUND_LOCK_END; +} + +#endif MODULE +#endif CONFIG_SOUND_WAVEFRONT + + diff --git a/drivers/sound/wf_midi.c b/drivers/sound/wf_midi.c new file mode 100644 index 000000000..c525abc97 --- /dev/null +++ b/drivers/sound/wf_midi.c @@ -0,0 +1,984 @@ +/* + * sound/wf_midi.c + * + * The low level driver for the WaveFront ICS2115 MIDI interface(s) + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez Plus. This code has nothing + * to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct + * midi devices (/dev/midiNN and /dev/midiNN+1) to be used + * completely independently, giving 32 channels of MIDI routing, + * 16 to the WaveFront synth and 16 to the external MIDI bus. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +/* + * Copyright (C) by Paul Barton-Davis 1998 + * Substantial portions of this file are derived from work that is: + * + * 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. + */ + +#include <linux/config.h> +#include "sound_config.h" +#include "soundmodule.h" + +#include <linux/wavefront.h> + +#if (defined(CONFIG_WAVEFRONT) && defined(CONFIG_MIDI)) || defined(MODULE) + +struct wf_mpu_config { + int base; /* I/O base */ + int irq; + int opened; /* Open mode */ + int devno; + int synthno; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + + void (*inputintr) (int dev, unsigned char data); + + /* Virtual MIDI support */ + + char configured_for_virtual; /* setup for virtual completed */ + char isvirtual; /* do virtual I/O stuff */ + char isexternal; /* i am an external interface */ + int internal; /* external interface midi_devno */ + int external; /* external interface midi_devno */ +}; + +#define DATAPORT(base) (base) +#define COMDPORT(base) (base+1) +#define STATPORT(base) (base+1) + +static void start_uart_mode (struct wf_mpu_config *devc); + +static int +wf_mpu_status (struct wf_mpu_config *devc) +{ + return inb (STATPORT (devc->base)); +} + +static void +wf_mpu_cmd (struct wf_mpu_config *devc, unsigned char cmd) +{ + outb ((cmd), COMDPORT(devc->base)); +} + +#define input_avail(devc) (!(wf_mpu_status(devc)&INPUT_AVAIL)) +#define output_ready(devc) (!(wf_mpu_status(devc)&OUTPUT_READY)) + +static int +read_data (struct wf_mpu_config *devc) +{ + return inb (DATAPORT (devc->base)); +} + +static void +write_data (struct wf_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 wf_mpu_config dev_conf[MAX_MIDI_DEV] = +{ + {0} +}; + +static volatile int irq2dev[17] = +{-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +static struct synth_info wf_mpu_synth_info_proto = +{"WaveFront MPU-401 interface", 0, + SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; + +static struct synth_info wf_mpu_synth_info[MAX_MIDI_DEV]; + +/* + * States for the input scanner (should be in dev_table.h) + */ + +#define MST_SYSMSG 100 /* System message (sysx etc). */ +#define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define MST_SONGSEL 103 /* Song select */ +#define MST_SONGPOS 104 /* Song position pointer */ +#define MST_TIMED 105 /* Leading timing byte rcvd */ + +/* buffer space check for input scanner */ + +#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ +{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ + mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} + +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 */ +}; + +static int +wf_mpu_input_scanner (struct wf_mpu_config *devc, unsigned char midic) +{ + struct midi_input_info *mi; + + mi = &midi_devs[devc->devno]->in_info; + + switch (mi->m_state) { + case MST_INIT: + switch (midic) { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + break; + + case 0xfd: + /* XXX do something useful with this. If there is + an external MIDI timer (e.g. a hardware sequencer, + a useful timer can be derived ... + + For now, no timer support. + */ + break; + + case 0xfe: + return MPU_ACK; + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + break; + + case 0xf9: + break; + + case 0xff: + mi->m_state = MST_SYSMSG; + break; + + default: + if (midic <= 0xef) { + mi->m_state = MST_TIMED; + } + else + printk (KERN_ERR "<MPU: Unknown event %02x> ", + midic); + } + break; + + case MST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + mi->m_state = MST_DATA; + + if (msg < 8) { /* Data byte */ + + msg = ((int) (mi->m_prev_status & 0xf0) >> 4); + msg -= 8; + mi->m_left = len_tab[msg] - 1; + + mi->m_ptr = 2; + mi->m_buf[0] = mi->m_prev_status; + mi->m_buf[1] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (devc->synthno, mi->m_buf, + mi->m_ptr); + mi->m_ptr = 0; + } + } else if (msg == 0xf) { /* MPU MARK */ + + mi->m_state = MST_INIT; + + switch (midic) { + case 0xf8: + break; + + case 0xf9: + break; + + case 0xfc: + break; + + default: + } + } else { + mi->m_prev_status = midic; + msg -= 8; + mi->m_left = len_tab[msg]; + + mi->m_ptr = 1; + mi->m_buf[0] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (devc->synthno, mi->m_buf, + mi->m_ptr); + mi->m_ptr = 0; + } + } + } + break; + + case MST_SYSMSG: + switch (midic) { + case 0xf0: + mi->m_state = MST_SYSEX; + break; + + case 0xf1: + mi->m_state = MST_MTC; + break; + + case 0xf2: + mi->m_state = MST_SONGPOS; + mi->m_ptr = 0; + break; + + case 0xf3: + mi->m_state = MST_SONGSEL; + break; + + case 0xf6: + mi->m_state = MST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xfA: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFB: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFC: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFE: + /* active sensing */ + mi->m_state = MST_INIT; + break; + + case 0xff: + mi->m_state = MST_INIT; + break; + + default: + printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); + mi->m_state = MST_INIT; + } + break; + + case MST_MTC: + mi->m_state = MST_INIT; + break; + + case MST_SYSEX: + if (midic == 0xf7) { + mi->m_state = MST_INIT; + } else { + /* XXX fix me */ + } + break; + + case MST_SONGPOS: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if (mi->m_ptr == 2) { + mi->m_state = MST_INIT; + mi->m_ptr = 0; + /* XXX need ext MIDI timer support */ + } + break; + + case MST_DATA: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if ((--mi->m_left) <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (devc->synthno, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + break; + + default: + printk (KERN_ERR "Bad state %d ", mi->m_state); + mi->m_state = MST_INIT; + } + + return 1; +} + +void +wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct wf_mpu_config *devc; + int dev; + static struct wf_mpu_config *isrc = 0; + int n; + struct midi_input_info *mi; + static int cnt = 0; + + if (irq < 0 || irq > 15) { + printk (KERN_ERR "WF-MPU: bogus interrupt #%d", irq); + return; + } + dev = irq2dev[irq]; + mi = &midi_devs[dev]->in_info; + if (mi->m_busy) return; + mi->m_busy = 1; + sti (); + + n = 50; + + /* guarantee that we're working with the "real" (internal) + interface before doing anything physical. + */ + + devc = &dev_conf[dev]; + devc = &dev_conf[devc->internal]; + + if (isrc == 0) { + + /* This is just an initial setting. If Virtual MIDI mode is + enabled on the ICS2115, we'll get a switch char before + anything else, and if it isn't, then the guess will be + correct for our purposes. + */ + + isrc = &dev_conf[devc->internal]; + } + + while (input_avail (devc) && n-- > 0) { + unsigned char c = read_data (devc); + + if (devc->isvirtual) { + if (c == WF_EXTERNAL_SWITCH) { + isrc = &dev_conf[devc->external]; + continue; + } else if (c == WF_INTERNAL_SWITCH) { + isrc = &dev_conf[devc->internal]; + continue; + } /* else just leave it as it is */ + } else { + isrc = &dev_conf[devc->internal]; + } + + if (isrc->mode == MODE_SYNTH) { + + wf_mpu_input_scanner (isrc, c); + + } else if (isrc->opened & OPEN_READ) { + + if (isrc->inputintr) { + isrc->inputintr (isrc->devno, c); + } + } + } + + mi->m_busy = 0; +} + +static int +wf_mpu_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) + ) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return -ENXIO; + + devc = &dev_conf[dev]; + + if (devc->opened) { + return -EBUSY; + } + + devc->mode = MODE_MIDI; + devc->opened = mode; + devc->synthno = 0; + + devc->inputintr = input; + return 0; +} + +static void +wf_mpu_close (int dev) +{ + struct wf_mpu_config *devc; + + devc = &dev_conf[dev]; + devc->mode = 0; + devc->inputintr = NULL; + devc->opened = 0; +} + +static int +wf_mpu_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + static int lastoutdev = -1; + + struct wf_mpu_config *devc; + unsigned char switchch; + + /* The actual output has to occur using the "internal" config info + */ + + devc = &dev_conf[dev_conf[dev].internal]; + + if (devc->isvirtual && lastoutdev != dev) { + + if (dev == devc->internal) { + switchch = WF_INTERNAL_SWITCH; + } else if (dev == devc->external) { + switchch = WF_EXTERNAL_SWITCH; + } else { + printk (KERN_ERR "WF-MPU: bad device number %d", dev); + return (0); + } + + for (timeout = 30000; timeout > 0 && !output_ready (devc); + timeout--); + + save_flags (flags); + cli (); + + if (!output_ready (devc)) { + printk (KERN_WARNING "WF-MPU: Send switch " + "byte timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (devc, switchch); + restore_flags (flags); + } + + lastoutdev = 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--); + + save_flags (flags); + cli (); + if (!output_ready (devc)) { + printk (KERN_WARNING "WF-MPU: Send data timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (devc, midi_byte); + restore_flags (flags); + + return 1; +} + +static int +wf_mpu_start_read (int dev) +{ + return 0; +} + +static int +wf_mpu_end_read (int dev) +{ + return 0; +} + +static int +wf_mpu_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + printk (KERN_WARNING + "WF-MPU: Intelligent mode not supported by hardware.\n"); + return -(EINVAL); +} + +static void +wf_mpu_kick (int dev) +{ +} + +static int +wf_mpu_buffer_status (int dev) +{ + return 0; /* + * No data in buffers + */ +} + +static int +wf_mpu_synth_ioctl (int dev, + unsigned int cmd, caddr_t arg) +{ + int midi_dev; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) + return -(ENXIO); + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + copy_to_user (&((char *) arg)[0], + &wf_mpu_synth_info[midi_dev], + sizeof (struct synth_info)); + + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + + default: + return -(EINVAL); + } +} + +static int +wf_mpu_synth_open (int dev, int mode) +{ + int midi_dev; + struct wf_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]; + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_SYNTH; + devc->synthno = dev; + devc->opened = mode; + devc->inputintr = NULL; + return 0; +} + +static void +wf_mpu_synth_close (int dev) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + devc = &dev_conf[midi_dev]; + devc->inputintr = NULL; + devc->opened = 0; + devc->mode = 0; +} + +#define MIDI_SYNTH_NAME "WaveFront (MIDI)" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations wf_mpu_synth_proto = +{ + "WaveFront (ICS2115)", + NULL, /* info field, filled in during configuration */ + 0, /* MIDI dev XXX should this be -1 ? */ + SYNTH_TYPE_MIDI, + SAMPLE_TYPE_WAVEFRONT, + wf_mpu_synth_open, + wf_mpu_synth_close, + wf_mpu_synth_ioctl, + midi_synth_kill_note, + midi_synth_start_note, + midi_synth_set_instr, + midi_synth_reset, + midi_synth_hw_control, + midi_synth_load_patch, + midi_synth_aftertouch, + midi_synth_controller, + midi_synth_panning, + NULL, + midi_synth_bender, + NULL, /* alloc */ + midi_synth_setup_voice, + midi_synth_send_sysex +}; + +static struct synth_operations wf_mpu_synth_operations[WAVEFRONT_MAX_DEVICES*2]; +static struct midi_operations wf_mpu_midi_operations[WAVEFRONT_MAX_DEVICES*2]; +static int wfmpu_cnt = 0; + +static struct midi_operations wf_mpu_midi_proto = +{ + {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + NULL, /*converter*/ + {0}, /* in_info */ + wf_mpu_open, + wf_mpu_close, + wf_mpu_ioctl, + wf_mpu_out, + wf_mpu_start_read, + wf_mpu_end_read, + wf_mpu_kick, + NULL, + wf_mpu_buffer_status, + NULL +}; + + +static int +config_wf_mpu (int dev, struct address_info *hw_config) + +{ + struct wf_mpu_config *devc; + int internal; + + if (wfmpu_cnt >= WAVEFRONT_MAX_DEVICES * 2) { + printk (KERN_ERR "WF-MPU: eh ? more MPU devices " + "than cards ?!!\n"); + return (-1); + } + + /* There is no synth available on the external interface, + so do the synth stuff to the internal interface only. + */ + + internal = dev_conf[dev].internal; + devc = &dev_conf[internal]; + + if (!dev_conf[dev].isexternal) { + memcpy ((char *) &wf_mpu_synth_operations[wfmpu_cnt], + (char *) &wf_mpu_synth_proto, + sizeof (struct synth_operations)); + } + + memcpy ((char *) &wf_mpu_midi_operations[wfmpu_cnt], + (char *) &wf_mpu_midi_proto, + sizeof (struct midi_operations)); + + if (dev_conf[dev].isexternal) { + wf_mpu_midi_operations[wfmpu_cnt].converter = NULL; + } else { + wf_mpu_midi_operations[wfmpu_cnt].converter = + &wf_mpu_synth_operations[wfmpu_cnt]; + } + + memcpy ((char *) &wf_mpu_synth_info[dev], + (char *) &wf_mpu_synth_info_proto, + sizeof (struct synth_info)); + + strcpy (wf_mpu_synth_info[dev].name, hw_config->name); + strcpy (wf_mpu_midi_operations[wfmpu_cnt].info.name, hw_config->name); + + conf_printf (hw_config->name, hw_config); + + if (!dev_conf[dev].isexternal) { + wf_mpu_synth_operations[wfmpu_cnt].midi_dev = dev; + } + wf_mpu_synth_operations[wfmpu_cnt].info = &wf_mpu_synth_info[dev]; + + midi_devs[dev] = &wf_mpu_midi_operations[wfmpu_cnt]; + + dev_conf[dev].opened = 0; + dev_conf[dev].mode = 0; + dev_conf[dev].configured_for_virtual = 0; + dev_conf[dev].devno = dev; + + midi_devs[dev]->in_info.m_busy = 0; + midi_devs[dev]->in_info.m_state = MST_INIT; + midi_devs[dev]->in_info.m_ptr = 0; + midi_devs[dev]->in_info.m_left = 0; + midi_devs[dev]->in_info.m_prev_status = 0; + + wfmpu_cnt++; + + return (0); +} + +int +virtual_midi_enable (int dev, struct address_info *hw_config) + +{ + int idev; + int edev; + struct wf_mpu_config *devc; + + devc = &dev_conf[dev]; + + if (devc->configured_for_virtual) { + + idev = devc->internal; + edev = devc->external; + + } else { + + if (hw_config == NULL) { + printk (KERN_ERR + "WF-MPU: virtual midi first " + "enabled without hw_config!\n"); + return -EINVAL; + } + + idev = devc->internal; + + if ((edev = sound_alloc_mididev()) == -1) { + printk (KERN_ERR + "WF-MPU: too many midi devices detected\n"); + return -1; + } + + hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = edev; + } + + dev_conf[edev].isvirtual = 1; + dev_conf[idev].isvirtual = 1; + + if (dev_conf[idev].configured_for_virtual) { + return 0; + } + + /* Configure external interface struct */ + + devc = &dev_conf[edev]; + devc->internal = idev; + devc->external = edev; + devc->isexternal = 1; + + /* Configure external interface struct + (devc->isexternal and devc->internal set in attach_wf_mpu()) + */ + + devc = &dev_conf[idev]; + devc->external = edev; + + /* Configure the tables for the external */ + + if (config_wf_mpu (edev, hw_config)) { + printk (KERN_WARNING "WF-MPU: configuration for MIDI " + "device %d failed\n", edev); + return (-1); + } + + /* Don't bother to do this again if we are toggled back and + forth between virtual MIDI mode and "normal" operation. + */ + + dev_conf[edev].configured_for_virtual = 1; + dev_conf[idev].configured_for_virtual = 1; + + return 0; +} + +void +virtual_midi_disable (int dev) + +{ + struct wf_mpu_config *devc; + unsigned long flags; + + save_flags (flags); + cli(); + + /* Assumes for logical purposes that the caller has taken + care of fiddling with WaveFront hardware commands to + turn off Virtual MIDI mode. + */ + + devc = &dev_conf[dev]; + + devc = &dev_conf[devc->internal]; + devc->isvirtual = 0; + + devc = &dev_conf[devc->external]; + devc->isvirtual = 0; + + restore_flags (flags); +} + +void +attach_wf_mpu (struct address_info *hw_config) +{ + int m; + struct wf_mpu_config *devc; + + if (request_irq (hw_config->irq, wf_mpuintr, + 0, "WaveFront MIDI", NULL) < 0) { + printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", + hw_config->irq); + return; + } + + if ((m = sound_alloc_mididev()) == -1){ + printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); + free_irq (hw_config->irq, NULL); + release_region (hw_config->io_base, 2); + return; + } + + request_region (hw_config->io_base, 2, "WaveFront MPU"); + + hw_config->slots[WF_INTERNAL_MIDI_SLOT] = m; + devc = &dev_conf[m]; + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->isexternal = 0; + devc->internal = m; + devc->external = -1; + devc->isvirtual = 0; + + irq2dev[devc->irq] = m; + + if (config_wf_mpu (m, hw_config)) { + printk (KERN_WARNING + "WF-MPU: configuration for MIDI device %d failed\n", m); + sound_unload_mididev (m); + } + + /* This being a WaveFront (ICS-2115) emulated MPU-401, we have + to switch it into UART (dumb) mode, because otherwise, it + won't do anything at all. + */ + + start_uart_mode (devc); + +} + +int +probe_wf_mpu (struct address_info *hw_config) + +{ + if (hw_config->irq < 0 || hw_config->irq > 16) { + printk (KERN_WARNING "WF-MPU: bogus IRQ value requested (%d)\n", + hw_config->irq); + return 0; + } + + if (check_region (hw_config->io_base, 2)) { + printk (KERN_WARNING "WF-MPU: I/O port %x already in use\n\n", + hw_config->io_base); + return 0; + } + + if (inb (hw_config->io_base + 1) == 0xff) { /* Just bus float? */ + printk ("WF-MPU: Port %x looks dead.\n", hw_config->io_base); + return 0; + } + + return 1; +} + +void +unload_wf_mpu (struct address_info *hw_config) +{ + + release_region (hw_config->io_base, 2); + sound_unload_mididev (hw_config->slots[WF_INTERNAL_MIDI_SLOT]); + if (hw_config->irq > 0) { + free_irq (hw_config->irq, NULL); + } + if (hw_config->slots[WF_EXTERNAL_MIDI_SLOT] > 0) { + sound_unload_mididev (hw_config->slots[WF_EXTERNAL_MIDI_SLOT]); + } +} + +static void +start_uart_mode (struct wf_mpu_config *devc) +{ + int ok, timeout; + unsigned long flags; + + save_flags (flags); + cli (); + + for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); + + wf_mpu_cmd (devc, UART_MODE_ON); + + for (ok = 0, timeout = 50000; timeout > 0 && !ok; timeout--) { + if (input_avail (devc)) { + if (read_data (devc) == MPU_ACK) { + ok = 1; + } + } + } + + restore_flags (flags); +} + +#endif + + diff --git a/drivers/sound/yss225.c b/drivers/sound/yss225.c new file mode 100644 index 000000000..e5d7ec098 --- /dev/null +++ b/drivers/sound/yss225.c @@ -0,0 +1,317 @@ +unsigned char page_zero[] = { +0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, +0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, +0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, +0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, +0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, +0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, +0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, +0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, +0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, +0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, +0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, +0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, +0x1d, 0x02, 0xdf +}; + +unsigned char page_one[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, +0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, +0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, +0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, +0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, +0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, +0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, +0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, +0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, +0x60, 0x00, 0x1b +}; + +unsigned char page_two[] = { +0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, +0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, +0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, +0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, +0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, +0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, +0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, +0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 +}; + +unsigned char page_three[] = { +0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, +0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, +0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, +0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, +0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 +}; + +unsigned char page_four[] = { +0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, +0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, +0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, +0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, +0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 +}; + +unsigned char page_six[] = { +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, +0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, +0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, +0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, +0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, +0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, +0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, +0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, +0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, +0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, +0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, +0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, +0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, +0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, +0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, +0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, +0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, +0x80, 0x00, 0x7e, 0x80, 0x80 +}; + +unsigned char page_seven[] = { +0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, +0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, +0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, +0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, +0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, +0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, +0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, +0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, +0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, +0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, +0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, +0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x00 +}; + +unsigned char page_zero_v2[] = { +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_one_v2[] = { +0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_two_v2[] = { +0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_three_v2[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_four_v2[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_seven_v2[] = { +0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char mod_v2[] = { +0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, +0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, +0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, +0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, +0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, +0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, +0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, +0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, +0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, +0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, +0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, +0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, +0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, +0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, +0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, +0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, +0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, +0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, +0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, +0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, +0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, +0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, +0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, +0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 +}; +unsigned char coefficients[] = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, +0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, +0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, +0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, +0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, +0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, +0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, +0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, +0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, +0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, +0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, +0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, +0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, +0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, +0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, +0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, +0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, +0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, +0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, +0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, +0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, +0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, +0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, +0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, +0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, +0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, +0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, +0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, +0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, +0xba +}; +unsigned char coefficients2[] = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, +0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, +0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, +0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, +0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 +}; +unsigned char coefficients3[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, +0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, +0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, +0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, +0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, +0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, +0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, +0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, +0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, +0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, +0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, +0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, +0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, +0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, +0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, +0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, +0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, +0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, +0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, +0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, +0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, +0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, +0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, +0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, +0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, +0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, +0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, +0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, +0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, +0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, +0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, +0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, +0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, +0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, +0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, +0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, +0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff +}; + diff --git a/drivers/sound/yss225.h b/drivers/sound/yss225.h new file mode 100644 index 000000000..7e4cd6571 --- /dev/null +++ b/drivers/sound/yss225.h @@ -0,0 +1,24 @@ +#ifndef __yss255_h__ +#define __yss255_h__ + +extern unsigned char page_zero[256]; +extern unsigned char page_one[256]; +extern unsigned char page_two[128]; +extern unsigned char page_three[128]; +extern unsigned char page_four[128]; +extern unsigned char page_six[192]; +extern unsigned char page_seven[256]; +extern unsigned char page_zero_v2[96]; +extern unsigned char page_one_v2[96]; +extern unsigned char page_two_v2[48]; +extern unsigned char page_three_v2[48]; +extern unsigned char page_four_v2[48]; +extern unsigned char page_seven_v2[96]; +extern unsigned char mod_v2[304]; +extern unsigned char coefficients[364]; +extern unsigned char coefficients2[56]; +extern unsigned char coefficients3[404]; + + +#endif __ys225_h__ + |